about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-10-11 06:26:03 +0000
committerbors <bors@rust-lang.org>2018-10-11 06:26:03 +0000
commitcb6eeddd4dcefa4b71bb4b6bb087d05ad8e82145 (patch)
tree4c9b6b6174dcbb0390563ca52365f1c3220c7af4 /src
parent9746a2d40d69b145f8247e4a5fb2978e5f4d5c97 (diff)
parentbe64bf3b9dea4b5f30b7044fcb2c1b86f3e7960f (diff)
downloadrust-cb6eeddd4dcefa4b71bb4b6bb087d05ad8e82145.tar.gz
rust-cb6eeddd4dcefa4b71bb4b6bb087d05ad8e82145.zip
Auto merge of #54969 - Manishearth:rollup, r=Manishearth
Rollup of 9 pull requests

Successful merges:

 - #54747 (codegen_llvm: verify that inline assembly operands are scalars)
 - #54848 (Better Diagnostic for Trait Object Capture)
 - #54850 (Fix #54707 - parse_trait_item_ now handles interpolated blocks as function body decls)
 - #54858 (second round of refactorings for universes)
 - #54862 (Implement RFC 2539: cfg_attr with multiple attributes)
 - #54869 (Fix mobile docs)
 - #54870 (Stabilize tool lints)
 - #54893 (Fix internal compiler error on malformed match arm pattern.)
 - #54904 (Stabilize the `Option::replace` method)

Failed merges:

 - #54909 ( Add chalk rules related to associated type defs)

r? @ghost
Diffstat (limited to 'src')
-rw-r--r--src/Cargo.lock8
-rw-r--r--src/doc/unstable-book/src/language-features/cfg-attr-multi.md20
-rw-r--r--src/doc/unstable-book/src/language-features/tool-lints.md35
-rw-r--r--src/libcore/option.rs4
-rw-r--r--src/libcore/tests/lib.rs1
-rw-r--r--src/librustc/Cargo.toml2
-rw-r--r--src/librustc/ich/impls_ty.rs2
-rw-r--r--src/librustc/lint/levels.rs27
-rw-r--r--src/librustc/traits/mod.rs22
-rw-r--r--src/librustc/traits/structural_impls.rs34
-rw-r--r--src/librustc/ty/context.rs29
-rw-r--r--src/librustc/ty/flags.rs5
-rw-r--r--src/librustc_driver/lib.rs1
-rw-r--r--src/librustc_traits/Cargo.toml2
-rw-r--r--src/librustc_traits/chalk_context.rs41
-rw-r--r--src/librustc_traits/lowering.rs114
-rw-r--r--src/libsyntax/config.rs95
-rw-r--r--src/libsyntax/feature_gate.rs8
-rw-r--r--src/libsyntax/parse/parser.rs5
-rw-r--r--src/test/run-pass/tool_lints.rs2
-rw-r--r--src/test/run-pass/tool_lints_2018_preview.rs2
-rw-r--r--src/test/ui-fulldeps/lint_tool_test.rs2
-rw-r--r--src/test/ui/cfg-attr-trailing-comma.rs13
-rw-r--r--src/test/ui/cfg-attr-trailing-comma.stderr14
-rw-r--r--src/test/ui/chalkify/lower_trait.rs9
-rw-r--r--src/test/ui/chalkify/lower_trait.stderr23
-rw-r--r--src/test/ui/conditional-compilation/auxiliary/namespaced_enums.rs (renamed from src/test/ui/auxiliary/namespaced_enums.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-arg-invalid-1.rs (renamed from src/test/ui/cfg-arg-invalid-1.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-arg-invalid-2.rs (renamed from src/test/ui/cfg-arg-invalid-2.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-arg-invalid-3.rs (renamed from src/test/ui/cfg-arg-invalid-3.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-arg-invalid-4.rs (renamed from src/test/ui/cfg-arg-invalid-4.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-arg-invalid-5.rs (renamed from src/test/ui/cfg-arg-invalid-5.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-cfg-2.rs (renamed from src/test/ui/cfg-attr-cfg-2.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-cfg-2.stderr (renamed from src/test/ui/cfg-attr-cfg-2.stderr)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-crate-2.rs (renamed from src/test/ui/cfg-attr-crate-2.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-crate-2.stderr (renamed from src/test/ui/cfg-attr-crate-2.stderr)2
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-invalid-predicate.rs (renamed from src/test/ui/cfg-attr-invalid-predicate.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-invalid-predicate.stderr (renamed from src/test/ui/cfg-attr-invalid-predicate.stderr)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-multi-false.rs20
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.rs (renamed from src/test/ui/feature-gates/feature-gate-tool_lints-fail.rs)10
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.stderr11
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.rs16
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.stderr11
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-multi-true.rs22
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr38
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-parse.rs45
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-parse.stderr32
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs (renamed from src/test/ui/cfg-attr-syntax-validation.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr (renamed from src/test/ui/cfg-attr-syntax-validation.stderr)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.rs (renamed from src/test/ui/cfg-attr-unknown-attribute-macro-expansion.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.stderr (renamed from src/test/ui/cfg-attr-unknown-attribute-macro-expansion.stderr)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-empty-codemap.rs (renamed from src/test/ui/cfg-empty-codemap.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-in-crate-1.rs (renamed from src/test/ui/cfg-in-crate-1.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-in-crate-1.stderr (renamed from src/test/ui/cfg-in-crate-1.stderr)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-non-opt-expr.rs (renamed from src/test/ui/cfg-non-opt-expr.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg-non-opt-expr.stderr (renamed from src/test/ui/cfg-non-opt-expr.stderr)0
-rw-r--r--src/test/ui/conditional-compilation/cfg_attr_path.rs (renamed from src/test/ui/cfg_attr_path.rs)0
-rw-r--r--src/test/ui/conditional-compilation/cfg_attr_path.stderr (renamed from src/test/ui/cfg_attr_path.stderr)0
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.rs5
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.stderr11
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.rs3
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.stderr11
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-1.rs7
-rw-r--r--src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-2.rs9
-rw-r--r--src/test/ui/feature-gates/feature-gate-tool_lints-fail.stderr11
-rw-r--r--src/test/ui/feature-gates/feature-gate-tool_lints.stderr11
-rw-r--r--src/test/ui/resolve/issue-54379.rs (renamed from src/test/ui/feature-gates/feature-gate-tool_lints.rs)12
-rw-r--r--src/test/ui/resolve/issue-54379.stderr24
-rw-r--r--src/test/ui/tool_lints-fail.rs2
-rw-r--r--src/test/ui/tool_lints.rs2
-rw-r--r--src/test/ui/unknown-lint-tool-name.rs2
71 files changed, 597 insertions, 240 deletions
diff --git a/src/Cargo.lock b/src/Cargo.lock
index 6a9488226b1..2719587f20e 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -270,7 +270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "chalk-engine"
-version = "0.7.0"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "chalk-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1898,7 +1898,7 @@ dependencies = [
  "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "chalk-engine 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "flate2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fmt_macros 0.0.0",
  "graphviz 0.0.0",
@@ -2434,7 +2434,7 @@ name = "rustc_traits"
 version = "0.0.0"
 dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "chalk-engine 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "graphviz 0.0.0",
  "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc 0.0.0",
@@ -3195,7 +3195,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum cargo_metadata 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6809b327f87369e6f3651efd2c5a96c49847a3ed2559477ecba79014751ee1"
 "checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
 "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
-"checksum chalk-engine 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25ce2f28f55ed544a2a3756b7acf41dd7d6f27acffb2086439950925506af7d0"
+"checksum chalk-engine 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6749eb72e7d4355d944a99f15fbaea701b978c18c5e184a025fcde942b0c9779"
 "checksum chalk-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "295635afd6853aa9f20baeb7f0204862440c0fe994c5a253d5f479dac41d047e"
 "checksum chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6962c635d530328acc53ac6a955e83093fedc91c5809dfac1fa60fa470830a37"
 "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
diff --git a/src/doc/unstable-book/src/language-features/cfg-attr-multi.md b/src/doc/unstable-book/src/language-features/cfg-attr-multi.md
new file mode 100644
index 00000000000..6365d3e71c6
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/cfg-attr-multi.md
@@ -0,0 +1,20 @@
+# `cfg_attr_multi`
+
+The tracking issue for this feature is: [#54881]
+The RFC for this feature is: [#2539]
+
+[#54881]: https://github.com/rust-lang/rust/issues/54881
+[#2539]: https://github.com/rust-lang/rfcs/pull/2539
+
+------------------------
+
+This feature flag lets you put multiple attributes into a `cfg_attr` attribute.
+
+Example:
+
+```rust,ignore
+#[cfg_attr(all(), must_use, optimize)]
+```
+
+Because `cfg_attr` resolves before procedural macros, this does not affect
+macro resolution at all.
\ No newline at end of file
diff --git a/src/doc/unstable-book/src/language-features/tool-lints.md b/src/doc/unstable-book/src/language-features/tool-lints.md
deleted file mode 100644
index 5c0d33b5ab0..00000000000
--- a/src/doc/unstable-book/src/language-features/tool-lints.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# `tool_lints`
-
-The tracking issue for this feature is: [#44690]
-
-[#44690]: https://github.com/rust-lang/rust/issues/44690
-
-------------------------
-
-Tool lints let you use scoped lints, to `allow`, `warn`, `deny` or `forbid` lints of
-certain tools.
-
-Currently `clippy` is the only available lint tool.
-
-It is recommended for lint tools to implement the scoped lints like this:
-
-- `#[_(TOOL_NAME::lintname)]`: for lint names
-- `#[_(TOOL_NAME::lintgroup)]`: for groups of lints
-- `#[_(TOOL_NAME::all)]`: for (almost[^1]) all lints
-
-## An example
-
-```rust
-#![feature(tool_lints)]
-
-#![warn(clippy::pedantic)]
-
-#[allow(clippy::filter_map)]
-fn main() {
-    let v = vec![0; 10];
-    let _ = v.into_iter().filter(|&x| x < 1).map(|x| x + 1).collect::<Vec<_>>();
-    println!("No filter_map()!");
-}
-```
-
-[^1]: Some defined lint groups can be excluded here.
diff --git a/src/libcore/option.rs b/src/libcore/option.rs
index 0255f7a0885..cf1c77041b9 100644
--- a/src/libcore/option.rs
+++ b/src/libcore/option.rs
@@ -867,8 +867,6 @@ impl<T> Option<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(option_replace)]
-    ///
     /// let mut x = Some(2);
     /// let old = x.replace(5);
     /// assert_eq!(x, Some(5));
@@ -880,7 +878,7 @@ impl<T> Option<T> {
     /// assert_eq!(old, None);
     /// ```
     #[inline]
-    #[unstable(feature = "option_replace", issue = "51998")]
+    #[stable(feature = "option_replace", since = "1.31.0")]
     pub fn replace(&mut self, value: T) -> Option<T> {
         mem::replace(self, Some(value))
     }
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index ada61d8dfd8..0beb60a1270 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -39,7 +39,6 @@
 #![feature(reverse_bits)]
 #![feature(inner_deref)]
 #![feature(slice_internals)]
-#![feature(option_replace)]
 #![feature(slice_partition_dedup)]
 #![feature(copy_within)]
 
diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml
index 6c3b52196a3..d0ec8640ce9 100644
--- a/src/librustc/Cargo.toml
+++ b/src/librustc/Cargo.toml
@@ -31,7 +31,7 @@ syntax_pos = { path = "../libsyntax_pos" }
 backtrace = "0.3.3"
 parking_lot = "0.6"
 byteorder = { version = "1.1", features = ["i128"]}
-chalk-engine = { version = "0.7.0", default-features=false }
+chalk-engine = { version = "0.8.0", default-features=false }
 rustc_fs_util = { path = "../librustc_fs_util" }
 smallvec = { version = "0.6.5", features = ["union"] }
 
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index e8c89cb3e0e..f51a3e71d07 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -1370,7 +1370,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for traits::Goal<'tcx> {
     fn hash_stable<W: StableHasherResult>(&self,
                                           hcx: &mut StableHashingContext<'a>,
                                           hasher: &mut StableHasher<W>) {
-        use traits::Goal::*;
+        use traits::GoalKind::*;
 
         mem::discriminant(self).hash_stable(hcx, hasher);
         match self {
diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs
index 87d33e473e7..950754a07ab 100644
--- a/src/librustc/lint/levels.rs
+++ b/src/librustc/lint/levels.rs
@@ -18,11 +18,10 @@ use lint::context::CheckLintNameResult;
 use lint::{self, Lint, LintId, Level, LintSource};
 use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey,
                                            StableHasher, StableHasherResult};
-use session::{config::nightly_options, Session};
+use session::Session;
 use syntax::ast;
 use syntax::attr;
 use syntax::source_map::MultiSpan;
-use syntax::feature_gate;
 use syntax::symbol::Symbol;
 use util::nodemap::FxHashMap;
 
@@ -228,18 +227,7 @@ impl<'a> LintLevelsBuilder<'a> {
                     }
                 };
                 let tool_name = if let Some(lint_tool) = word.is_scoped() {
-                    let gate_feature = !self.sess.features_untracked().tool_lints;
-                    let known_tool = attr::is_known_lint_tool(lint_tool);
-                    if gate_feature {
-                        feature_gate::emit_feature_err(
-                            &sess.parse_sess,
-                            "tool_lints",
-                            word.span,
-                            feature_gate::GateIssue::Language,
-                            &format!("scoped lint `{}` is experimental", word.ident),
-                        );
-                    }
-                    if !known_tool {
+                    if !attr::is_known_lint_tool(lint_tool) {
                         span_err!(
                             sess,
                             lint_tool.span,
@@ -247,9 +235,6 @@ impl<'a> LintLevelsBuilder<'a> {
                             "an unknown tool name found in scoped lint: `{}`",
                             word.ident
                         );
-                    }
-
-                    if gate_feature || !known_tool {
                         continue;
                     }
 
@@ -299,13 +284,7 @@ impl<'a> LintLevelsBuilder<'a> {
                                     "change it to",
                                     new_lint_name.to_string(),
                                     Applicability::MachineApplicable,
-                                );
-
-                                if nightly_options::is_nightly_build() {
-                                    err.emit();
-                                } else {
-                                    err.cancel();
-                                }
+                                ).emit();
 
                                 let src = LintSource::Node(Symbol::intern(&new_lint_name), li.span);
                                 for id in ids {
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 286e35c5d4e..6e4abee32c0 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -318,31 +318,33 @@ pub enum QuantifierKind {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum Goal<'tcx> {
-    Implies(Clauses<'tcx>, &'tcx Goal<'tcx>),
-    And(&'tcx Goal<'tcx>, &'tcx Goal<'tcx>),
-    Not(&'tcx Goal<'tcx>),
+pub enum GoalKind<'tcx> {
+    Implies(Clauses<'tcx>, Goal<'tcx>),
+    And(Goal<'tcx>, Goal<'tcx>),
+    Not(Goal<'tcx>),
     DomainGoal(DomainGoal<'tcx>),
-    Quantified(QuantifierKind, ty::Binder<&'tcx Goal<'tcx>>),
+    Quantified(QuantifierKind, ty::Binder<Goal<'tcx>>),
     CannotProve,
 }
 
+pub type Goal<'tcx> = &'tcx GoalKind<'tcx>;
+
 pub type Goals<'tcx> = &'tcx List<Goal<'tcx>>;
 
 impl<'tcx> DomainGoal<'tcx> {
-    pub fn into_goal(self) -> Goal<'tcx> {
-        Goal::DomainGoal(self)
+    pub fn into_goal(self) -> GoalKind<'tcx> {
+        GoalKind::DomainGoal(self)
     }
 }
 
-impl<'tcx> Goal<'tcx> {
+impl<'tcx> GoalKind<'tcx> {
     pub fn from_poly_domain_goal<'a>(
         domain_goal: PolyDomainGoal<'tcx>,
         tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    ) -> Goal<'tcx> {
+    ) -> GoalKind<'tcx> {
         match domain_goal.no_late_bound_regions() {
             Some(p) => p.into_goal(),
-            None => Goal::Quantified(
+            None => GoalKind::Quantified(
                 QuantifierKind::Universal,
                 domain_goal.map_bound(|p| tcx.mk_goal(p.into_goal()))
             ),
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 22e79fc2638..1524f89af29 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -469,7 +469,7 @@ impl fmt::Display for traits::QuantifierKind {
 
 impl<'tcx> fmt::Display for traits::Goal<'tcx> {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        use traits::Goal::*;
+        use traits::GoalKind::*;
 
         match self {
             Implies(hypotheses, goal) => {
@@ -598,25 +598,25 @@ CloneTypeFoldableAndLiftImpls! {
 }
 
 EnumTypeFoldableImpl! {
-    impl<'tcx> TypeFoldable<'tcx> for traits::Goal<'tcx> {
-        (traits::Goal::Implies)(hypotheses, goal),
-        (traits::Goal::And)(goal1, goal2),
-        (traits::Goal::Not)(goal),
-        (traits::Goal::DomainGoal)(domain_goal),
-        (traits::Goal::Quantified)(qkind, goal),
-        (traits::Goal::CannotProve),
+    impl<'tcx> TypeFoldable<'tcx> for traits::GoalKind<'tcx> {
+        (traits::GoalKind::Implies)(hypotheses, goal),
+        (traits::GoalKind::And)(goal1, goal2),
+        (traits::GoalKind::Not)(goal),
+        (traits::GoalKind::DomainGoal)(domain_goal),
+        (traits::GoalKind::Quantified)(qkind, goal),
+        (traits::GoalKind::CannotProve),
     }
 }
 
 EnumLiftImpl! {
-    impl<'a, 'tcx> Lift<'tcx> for traits::Goal<'a> {
-        type Lifted = traits::Goal<'tcx>;
-        (traits::Goal::Implies)(hypotheses, goal),
-        (traits::Goal::And)(goal1, goal2),
-        (traits::Goal::Not)(goal),
-        (traits::Goal::DomainGoal)(domain_goal),
-        (traits::Goal::Quantified)(kind, goal),
-        (traits::Goal::CannotProve),
+    impl<'a, 'tcx> Lift<'tcx> for traits::GoalKind<'a> {
+        type Lifted = traits::GoalKind<'tcx>;
+        (traits::GoalKind::Implies)(hypotheses, goal),
+        (traits::GoalKind::And)(goal1, goal2),
+        (traits::GoalKind::Not)(goal),
+        (traits::GoalKind::DomainGoal)(domain_goal),
+        (traits::GoalKind::Quantified)(kind, goal),
+        (traits::GoalKind::CannotProve),
     }
 }
 
@@ -633,7 +633,7 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<traits::Goal<'tcx>> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for &'tcx traits::Goal<'tcx> {
+impl<'tcx> TypeFoldable<'tcx> for traits::Goal<'tcx> {
     fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
         let v = (**self).fold_with(folder);
         folder.tcx().mk_goal(v)
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 46ba5f5ef36..3d4ae572d0b 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -36,7 +36,7 @@ use mir::interpret::Allocation;
 use ty::subst::{CanonicalSubsts, Kind, Substs, Subst};
 use ty::ReprOptions;
 use traits;
-use traits::{Clause, Clauses, Goal, Goals};
+use traits::{Clause, Clauses, GoalKind, Goal, Goals};
 use ty::{self, Ty, TypeAndMut};
 use ty::{TyS, TyKind, List};
 use ty::{AdtKind, AdtDef, ClosureSubsts, GeneratorSubsts, Region, Const};
@@ -143,7 +143,8 @@ pub struct CtxtInterners<'tcx> {
     predicates: InternedSet<'tcx, List<Predicate<'tcx>>>,
     const_: InternedSet<'tcx, Const<'tcx>>,
     clauses: InternedSet<'tcx, List<Clause<'tcx>>>,
-    goals: InternedSet<'tcx, List<Goal<'tcx>>>,
+    goal: InternedSet<'tcx, GoalKind<'tcx>>,
+    goal_list: InternedSet<'tcx, List<Goal<'tcx>>>,
 }
 
 impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
@@ -159,7 +160,8 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
             predicates: Default::default(),
             const_: Default::default(),
             clauses: Default::default(),
-            goals: Default::default(),
+            goal: Default::default(),
+            goal_list: Default::default(),
         }
     }
 
@@ -1731,9 +1733,9 @@ impl<'a, 'tcx> Lift<'tcx> for Region<'a> {
     }
 }
 
-impl<'a, 'tcx> Lift<'tcx> for &'a Goal<'a> {
-    type Lifted = &'tcx Goal<'tcx>;
-    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<&'tcx Goal<'tcx>> {
+impl<'a, 'tcx> Lift<'tcx> for Goal<'a> {
+    type Lifted = Goal<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Goal<'tcx>> {
         if tcx.interners.arena.in_arena(*self as *const _) {
             return Some(unsafe { mem::transmute(*self) });
         }
@@ -2304,6 +2306,12 @@ impl<'tcx> Borrow<RegionKind> for Interned<'tcx, RegionKind> {
     }
 }
 
+impl<'tcx: 'lcx, 'lcx> Borrow<GoalKind<'lcx>> for Interned<'tcx, GoalKind<'tcx>> {
+    fn borrow<'a>(&'a self) -> &'a GoalKind<'lcx> {
+        &self.0
+    }
+}
+
 impl<'tcx: 'lcx, 'lcx> Borrow<[ExistentialPredicate<'lcx>]>
     for Interned<'tcx, List<ExistentialPredicate<'tcx>>> {
     fn borrow<'a>(&'a self) -> &'a [ExistentialPredicate<'lcx>] {
@@ -2419,7 +2427,8 @@ pub fn keep_local<'tcx, T: ty::TypeFoldable<'tcx>>(x: &T) -> bool {
 
 direct_interners!('tcx,
     region: mk_region(|r: &RegionKind| r.keep_in_local_tcx()) -> RegionKind,
-    const_: mk_const(|c: &Const<'_>| keep_local(&c.ty) || keep_local(&c.val)) -> Const<'tcx>
+    const_: mk_const(|c: &Const<'_>| keep_local(&c.ty) || keep_local(&c.val)) -> Const<'tcx>,
+    goal: mk_goal(|c: &GoalKind<'_>| keep_local(c)) -> GoalKind<'tcx>
 );
 
 macro_rules! slice_interners {
@@ -2438,7 +2447,7 @@ slice_interners!(
     type_list: _intern_type_list(Ty),
     substs: _intern_substs(Kind),
     clauses: _intern_clauses(Clause),
-    goals: _intern_goals(Goal)
+    goal_list: _intern_goals(Goal)
 );
 
 // This isn't a perfect fit: CanonicalVarInfo slices are always
@@ -2818,10 +2827,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         iter.intern_with(|xs| self.intern_goals(xs))
     }
 
-    pub fn mk_goal(self, goal: Goal<'tcx>) -> &'tcx Goal<'_> {
-        &self.intern_goals(&[goal])[0]
-    }
-
     pub fn lint_hir<S: Into<MultiSpan>>(self,
                                         lint: &'static Lint,
                                         hir_id: HirId,
diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs
index c3d41873009..10a90dfc8a8 100644
--- a/src/librustc/ty/flags.rs
+++ b/src/librustc/ty/flags.rs
@@ -148,7 +148,10 @@ impl FlagComputation {
                 self.add_projection_ty(data);
             }
 
-            &ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
+            &ty::UnnormalizedProjection(ref data) => {
+                self.add_flags(TypeFlags::HAS_PROJECTION);
+                self.add_projection_ty(data);
+            },
 
             &ty::Opaque(_, substs) => {
                 self.add_flags(TypeFlags::HAS_PROJECTION);
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 4405c0aef90..0514bd20c98 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -21,7 +21,6 @@
 #![feature(box_syntax)]
 #![cfg_attr(unix, feature(libc))]
 #![feature(nll)]
-#![feature(option_replace)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(slice_sort_by_cached_key)]
diff --git a/src/librustc_traits/Cargo.toml b/src/librustc_traits/Cargo.toml
index cd21ee601a7..16f0f11757a 100644
--- a/src/librustc_traits/Cargo.toml
+++ b/src/librustc_traits/Cargo.toml
@@ -16,5 +16,5 @@ rustc = { path = "../librustc" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 syntax = { path = "../libsyntax" }
 syntax_pos = { path = "../libsyntax_pos" }
-chalk-engine = { version = "0.7.0", default-features=false }
+chalk-engine = { version = "0.8.0", default-features=false }
 smallvec = { version = "0.6.5", features = ["union"] }
diff --git a/src/librustc_traits/chalk_context.rs b/src/librustc_traits/chalk_context.rs
index 4c28df97bdf..dea3aa4372a 100644
--- a/src/librustc_traits/chalk_context.rs
+++ b/src/librustc_traits/chalk_context.rs
@@ -19,6 +19,7 @@ use rustc::traits::{
     ExClauseFold,
     ExClauseLift,
     Goal,
+    GoalKind,
     ProgramClause,
     QuantifierKind
 };
@@ -92,7 +93,7 @@ impl context::Context for ChalkArenas<'tcx> {
 
     type DomainGoal = DomainGoal<'tcx>;
 
-    type BindersGoal = ty::Binder<&'tcx Goal<'tcx>>;
+    type BindersGoal = ty::Binder<Goal<'tcx>>;
 
     type Parameter = Kind<'tcx>;
 
@@ -102,14 +103,6 @@ impl context::Context for ChalkArenas<'tcx> {
 
     type UnificationResult = InferOk<'tcx, ()>;
 
-    fn into_goal(domain_goal: DomainGoal<'tcx>) -> Goal<'tcx> {
-        Goal::DomainGoal(domain_goal)
-    }
-
-    fn cannot_prove() -> Goal<'tcx> {
-        Goal::CannotProve
-    }
-
     fn goal_in_environment(
         env: &ty::ParamEnv<'tcx>,
         goal: Goal<'tcx>,
@@ -251,15 +244,23 @@ impl context::ContextOps<ChalkArenas<'gcx>> for ChalkContext<'cx, 'gcx> {
 impl context::InferenceTable<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
     for ChalkInferenceContext<'cx, 'gcx, 'tcx>
 {
+    fn into_goal(&self, domain_goal: DomainGoal<'tcx>) -> Goal<'tcx> {
+        self.infcx.tcx.mk_goal(GoalKind::DomainGoal(domain_goal))
+    }
+
+    fn cannot_prove(&self) -> Goal<'tcx> {
+        self.infcx.tcx.mk_goal(GoalKind::CannotProve)
+    }
+
     fn into_hh_goal(&mut self, goal: Goal<'tcx>) -> ChalkHhGoal<'tcx> {
-        match goal {
-            Goal::Implies(..) => panic!("FIXME rust-lang-nursery/chalk#94"),
-            Goal::And(left, right) => HhGoal::And(*left, *right),
-            Goal::Not(subgoal) => HhGoal::Not(*subgoal),
-            Goal::DomainGoal(d) => HhGoal::DomainGoal(d),
-            Goal::Quantified(QuantifierKind::Universal, binder) => HhGoal::ForAll(binder),
-            Goal::Quantified(QuantifierKind::Existential, binder) => HhGoal::Exists(binder),
-            Goal::CannotProve => HhGoal::CannotProve,
+        match *goal {
+            GoalKind::Implies(..) => panic!("FIXME rust-lang-nursery/chalk#94"),
+            GoalKind::And(left, right) => HhGoal::And(left, right),
+            GoalKind::Not(subgoal) => HhGoal::Not(subgoal),
+            GoalKind::DomainGoal(d) => HhGoal::DomainGoal(d),
+            GoalKind::Quantified(QuantifierKind::Universal, binder) => HhGoal::ForAll(binder),
+            GoalKind::Quantified(QuantifierKind::Existential, binder) => HhGoal::Exists(binder),
+            GoalKind::CannotProve => HhGoal::CannotProve,
         }
     }
 
@@ -363,21 +364,21 @@ impl context::UnificationOps<ChalkArenas<'gcx>, ChalkArenas<'tcx>>
 
     fn instantiate_binders_universally(
         &mut self,
-        _arg: &ty::Binder<&'tcx Goal<'tcx>>,
+        _arg: &ty::Binder<Goal<'tcx>>,
     ) -> Goal<'tcx> {
         panic!("FIXME -- universal instantiation needs sgrif's branch")
     }
 
     fn instantiate_binders_existentially(
         &mut self,
-        arg: &ty::Binder<&'tcx Goal<'tcx>>,
+        arg: &ty::Binder<Goal<'tcx>>,
     ) -> Goal<'tcx> {
         let (value, _map) = self.infcx.replace_late_bound_regions_with_fresh_var(
             DUMMY_SP,
             LateBoundRegionConversionTime::HigherRankedType,
             arg,
         );
-        *value
+        value
     }
 
     fn debug_ex_clause(&mut self, value: &'v ChalkExClause<'tcx>) -> Box<dyn Debug + 'v> {
diff --git a/src/librustc_traits/lowering.rs b/src/librustc_traits/lowering.rs
index ad724babe49..181106d3f84 100644
--- a/src/librustc_traits/lowering.rs
+++ b/src/librustc_traits/lowering.rs
@@ -13,7 +13,14 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc::hir::map::definitions::DefPathData;
 use rustc::hir::{self, ImplPolarity};
 use rustc::traits::{
-    Clause, Clauses, DomainGoal, FromEnv, Goal, PolyDomainGoal, ProgramClause, WellFormed,
+    Clause,
+    Clauses,
+    DomainGoal,
+    FromEnv,
+    GoalKind,
+    PolyDomainGoal,
+    ProgramClause,
+    WellFormed,
     WhereClause,
 };
 use rustc::ty::query::Providers;
@@ -249,7 +256,7 @@ fn program_clauses_for_trait<'a, 'tcx>(
     let impl_trait: DomainGoal = trait_pred.lower();
 
     // `FromEnv(Self: Trait<P1..Pn>)`
-    let from_env_goal = impl_trait.into_from_env_goal().into_goal();
+    let from_env_goal = tcx.mk_goal(impl_trait.into_from_env_goal().into_goal());
     let hypotheses = tcx.intern_goals(&[from_env_goal]);
 
     // `Implemented(Self: Trait<P1..Pn>) :- FromEnv(Self: Trait<P1..Pn>)`
@@ -308,7 +315,7 @@ fn program_clauses_for_trait<'a, 'tcx>(
     let wf_clause = ProgramClause {
         goal: DomainGoal::WellFormed(WellFormed::Trait(trait_pred)),
         hypotheses: tcx.mk_goals(
-            wf_conditions.map(|wc| Goal::from_poly_domain_goal(wc, tcx)),
+            wf_conditions.map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
     };
     let wf_clause = iter::once(Clause::ForAll(ty::Binder::dummy(wf_clause)));
@@ -352,10 +359,10 @@ fn program_clauses_for_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId
         hypotheses: tcx.mk_goals(
             where_clauses
                 .into_iter()
-                .map(|wc| Goal::from_poly_domain_goal(wc, tcx)),
+                .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
     };
-    tcx.intern_clauses(&[Clause::ForAll(ty::Binder::dummy(clause))])
+    tcx.mk_clauses(iter::once(Clause::ForAll(ty::Binder::dummy(clause))))
 }
 
 pub fn program_clauses_for_type_def<'a, 'tcx>(
@@ -388,7 +395,7 @@ pub fn program_clauses_for_type_def<'a, 'tcx>(
             where_clauses
                 .iter()
                 .cloned()
-                .map(|wc| Goal::from_poly_domain_goal(wc, tcx)),
+                .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
     };
 
@@ -404,7 +411,7 @@ pub fn program_clauses_for_type_def<'a, 'tcx>(
     // ```
 
     // `FromEnv(Ty<...>)`
-    let from_env_goal = DomainGoal::FromEnv(FromEnv::Ty(ty)).into_goal();
+    let from_env_goal = tcx.mk_goal(DomainGoal::FromEnv(FromEnv::Ty(ty)).into_goal());
     let hypotheses = tcx.intern_goals(&[from_env_goal]);
 
     // For each where clause `WC`:
@@ -423,10 +430,86 @@ pub fn program_clauses_for_type_def<'a, 'tcx>(
 }
 
 pub fn program_clauses_for_associated_type_def<'a, 'tcx>(
-    _tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    _item_id: DefId,
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    item_id: DefId,
 ) -> Clauses<'tcx> {
-    unimplemented!()
+    // Rule ProjectionEq-Skolemize
+    //
+    // ```
+    // trait Trait<P1..Pn> {
+    //     type AssocType<Pn+1..Pm>;
+    // }
+    // ```
+    //
+    // `ProjectionEq` can succeed by skolemizing, see "associated type"
+    // chapter for more:
+    // ```
+    // forall<Self, P1..Pn, Pn+1..Pm> {
+    //     ProjectionEq(
+    //         <Self as Trait<P1..Pn>>::AssocType<Pn+1..Pm> =
+    //         (Trait::AssocType)<Self, P1..Pn, Pn+1..Pm>
+    //     )
+    // }
+    // ```
+
+    let item = tcx.associated_item(item_id);
+    debug_assert_eq!(item.kind, ty::AssociatedKind::Type);
+    let trait_id = match item.container {
+        ty::AssociatedItemContainer::TraitContainer(trait_id) => trait_id,
+        _ => bug!("not an trait container"),
+    };
+    let trait_ref = ty::TraitRef::identity(tcx, trait_id);
+
+    let projection_ty = ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, item.ident);
+    let placeholder_ty = tcx.mk_ty(ty::UnnormalizedProjection(projection_ty));
+    let projection_eq = WhereClause::ProjectionEq(ty::ProjectionPredicate {
+        projection_ty,
+        ty: placeholder_ty,
+    });
+
+    let projection_eq_clause = ProgramClause {
+        goal: DomainGoal::Holds(projection_eq),
+        hypotheses: &ty::List::empty(),
+    };
+
+    // Rule WellFormed-AssocTy
+    // ```
+    // forall<Self, P1..Pn, Pn+1..Pm> {
+    //     WellFormed((Trait::AssocType)<Self, P1..Pn, Pn+1..Pm>)
+    //         :- Implemented(Self: Trait<P1..Pn>)
+    // }
+    // ```
+
+    let trait_predicate = ty::TraitPredicate { trait_ref };
+    let hypothesis = tcx.mk_goal(
+        DomainGoal::Holds(WhereClause::Implemented(trait_predicate)).into_goal()
+    );
+    let wf_clause = ProgramClause {
+        goal: DomainGoal::WellFormed(WellFormed::Ty(placeholder_ty)),
+        hypotheses: tcx.mk_goals(iter::once(hypothesis)),
+    };
+
+    // Rule Implied-Trait-From-AssocTy
+    // ```
+    // forall<Self, P1..Pn, Pn+1..Pm> {
+    //     FromEnv(Self: Trait<P1..Pn>)
+    //         :- FromEnv((Trait::AssocType)<Self, P1..Pn, Pn+1..Pm>)
+    // }
+    // ```
+
+    let hypothesis = tcx.mk_goal(
+        DomainGoal::FromEnv(FromEnv::Ty(placeholder_ty)).into_goal()
+    );
+    let from_env_clause = ProgramClause {
+        goal: DomainGoal::FromEnv(FromEnv::Trait(trait_predicate)),
+        hypotheses: tcx.mk_goals(iter::once(hypothesis)),
+    };
+
+    let clauses = iter::once(projection_eq_clause)
+        .chain(iter::once(wf_clause))
+        .chain(iter::once(from_env_clause));
+    let clauses = clauses.map(|clause| Clause::ForAll(ty::Binder::dummy(clause)));
+    tcx.mk_clauses(clauses)
 }
 
 pub fn program_clauses_for_associated_type_value<'a, 'tcx>(
@@ -435,10 +518,11 @@ pub fn program_clauses_for_associated_type_value<'a, 'tcx>(
 ) -> Clauses<'tcx> {
     // Rule Normalize-From-Impl (see rustc guide)
     //
-    // ```impl<P0..Pn> Trait<A1..An> for A0
-    // {
+    // ```
+    // impl<P0..Pn> Trait<A1..An> for A0 {
     //     type AssocType<Pn+1..Pm> = T;
-    // }```
+    // }
+    // ```
     //
     // FIXME: For the moment, we don't account for where clauses written on the associated
     // ty definition (i.e. in the trait def, as in `type AssocType<T> where T: Sized`).
@@ -482,10 +566,10 @@ pub fn program_clauses_for_associated_type_value<'a, 'tcx>(
         hypotheses: tcx.mk_goals(
             hypotheses
                 .into_iter()
-                .map(|wc| Goal::from_poly_domain_goal(wc, tcx)),
+                .map(|wc| tcx.mk_goal(GoalKind::from_poly_domain_goal(wc, tcx))),
         ),
     };
-    tcx.intern_clauses(&[Clause::ForAll(ty::Binder::dummy(clause))])
+    tcx.mk_clauses(iter::once(Clause::ForAll(ty::Binder::dummy(clause))))
 }
 
 pub fn dump_program_clauses<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index a9ce2365577..e611eb86dc1 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -9,7 +9,14 @@
 // except according to those terms.
 
 use attr::HasAttrs;
-use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue};
+use feature_gate::{
+    feature_err,
+    EXPLAIN_STMT_ATTR_SYNTAX,
+    Features,
+    get_features,
+    GateIssue,
+    emit_feature_err,
+};
 use {fold, attr};
 use ast;
 use source_map::Spanned;
@@ -73,49 +80,103 @@ impl<'a> StripUnconfigured<'a> {
         if self.in_cfg(node.attrs()) { Some(node) } else { None }
     }
 
+    /// Parse and expand all `cfg_attr` attributes into a list of attributes
+    /// that are within each `cfg_attr` that has a true configuration predicate.
+    ///
+    /// Gives compiler warnigns if any `cfg_attr` does not contain any
+    /// attributes and is in the original source code. Gives compiler errors if
+    /// the syntax of any `cfg_attr` is incorrect.
     pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
         node.map_attrs(|attrs| {
-            attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect()
+            attrs.into_iter().flat_map(|attr| self.process_cfg_attr(attr)).collect()
         })
     }
 
-    fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
+    /// Parse and expand a single `cfg_attr` attribute into a list of attributes
+    /// when the configuration predicate is true, or otherwise expand into an
+    /// empty list of attributes.
+    ///
+    /// Gives a compiler warning when the `cfg_attr` contains no attribtes and
+    /// is in the original source file. Gives a compiler error if the syntax of
+    /// the attribute is incorrect
+    fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
         if !attr.check_name("cfg_attr") {
-            return Some(attr);
+            return vec![attr];
         }
 
-        let (cfg, path, tokens, span) = match attr.parse(self.sess, |parser| {
+        let gate_cfg_attr_multi = if let Some(ref features) = self.features {
+            !features.cfg_attr_multi
+        } else {
+            false
+        };
+        let cfg_attr_span = attr.span;
+
+        let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
             parser.expect(&token::OpenDelim(token::Paren))?;
-            let cfg = parser.parse_meta_item()?;
+
+            let cfg_predicate = parser.parse_meta_item()?;
             parser.expect(&token::Comma)?;
-            let lo = parser.span.lo();
-            let (path, tokens) = parser.parse_meta_item_unrestricted()?;
-            parser.eat(&token::Comma); // Optional trailing comma
+
+            // Presumably, the majority of the time there will only be one attr.
+            let mut expanded_attrs = Vec::with_capacity(1);
+
+            while !parser.check(&token::CloseDelim(token::Paren)) {
+                let lo = parser.span.lo();
+                let (path, tokens) = parser.parse_meta_item_unrestricted()?;
+                expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo)));
+                parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
+            }
+
             parser.expect(&token::CloseDelim(token::Paren))?;
-            Ok((cfg, path, tokens, parser.prev_span.with_lo(lo)))
+            Ok((cfg_predicate, expanded_attrs))
         }) {
             Ok(result) => result,
             Err(mut e) => {
                 e.emit();
-                return None;
+                return Vec::new();
             }
         };
 
-        if attr::cfg_matches(&cfg, self.sess, self.features) {
-            self.process_cfg_attr(ast::Attribute {
+        // Check feature gate and lint on zero attributes in source. Even if the feature is gated,
+        // we still compute as if it wasn't, since the emitted error will stop compilation futher
+        // along the compilation.
+        match (expanded_attrs.len(), gate_cfg_attr_multi) {
+            (0, false) => {
+                // FIXME: Emit unused attribute lint here.
+            },
+            (1, _) => {},
+            (_, true) => {
+                emit_feature_err(
+                    self.sess,
+                    "cfg_attr_multi",
+                    cfg_attr_span,
+                    GateIssue::Language,
+                    "cfg_attr with zero or more than one attributes is experimental",
+                );
+            },
+            (_, false) => {}
+        }
+
+        if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
+            // We call `process_cfg_attr` recursively in case there's a
+            // `cfg_attr` inside of another `cfg_attr`. E.g.
+            //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
+            expanded_attrs.into_iter()
+            .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute {
                 id: attr::mk_attr_id(),
                 style: attr.style,
                 path,
                 tokens,
                 is_sugared_doc: false,
                 span,
-            })
+            }))
+            .collect()
         } else {
-            None
+            Vec::new()
         }
     }
 
-    // Determine if a node with the given attributes should be included in this configuration.
+    /// Determine if a node with the given attributes should be included in this configuration.
     pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
         attrs.iter().all(|attr| {
             if !is_cfg(attr) {
@@ -165,7 +226,7 @@ impl<'a> StripUnconfigured<'a> {
         })
     }
 
-    // Visit attributes on expression and statements (but not attributes on items in blocks).
+    /// Visit attributes on expression and statements (but not attributes on items in blocks).
     fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
         // flag the offending attributes
         for attr in attrs.iter() {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 24ee2464055..b86b92fb29e 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -433,9 +433,6 @@ declare_features! (
     // #[doc(alias = "...")]
     (active, doc_alias, "1.27.0", Some(50146), None),
 
-    // Scoped lints
-    (active, tool_lints, "1.28.0", Some(44690), None),
-
     // Allows irrefutable patterns in if-let and while-let statements (RFC 2086)
     (active, irrefutable_let_patterns, "1.27.0", Some(44495), None),
 
@@ -499,6 +496,9 @@ declare_features! (
 
     // Allows `impl Trait` in bindings (`let`, `const`, `static`)
     (active, impl_trait_in_bindings, "1.30.0", Some(34511), None),
+
+    // #[cfg_attr(predicate, multiple, attributes, here)]
+    (active, cfg_attr_multi, "1.31.0", Some(54881), None),
 );
 
 declare_features! (
@@ -679,6 +679,8 @@ declare_features! (
     (accepted, pattern_parentheses, "1.31.0", Some(51087), None),
     // Allows the definition of `const fn` functions.
     (accepted, min_const_fn, "1.31.0", Some(53555), None),
+    // Scoped lints
+    (accepted, tool_lints, "1.31.0", Some(44690), None),
 );
 
 // If you change this, please modify src/doc/unstable-book as well. You must
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index d653ed819fd..3099b2a3e8e 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -678,7 +678,7 @@ impl<'a> Parser<'a> {
     /// Expect next token to be edible or inedible token.  If edible,
     /// then consume it; if inedible, then return without consuming
     /// anything.  Signal a fatal error if next token is unexpected.
-    fn expect_one_of(&mut self,
+    pub fn expect_one_of(&mut self,
                          edible: &[token::Token],
                          inedible: &[token::Token]) -> PResult<'a,  ()>{
         fn tokens_to_string(tokens: &[TokenType]) -> String {
@@ -3866,6 +3866,9 @@ impl<'a> Parser<'a> {
             // check that a comma comes after every field
             if !ate_comma {
                 let err = self.struct_span_err(self.prev_span, "expected `,`");
+                if let Some(mut delayed) = delayed_err {
+                    delayed.emit();
+                }
                 return Err(err);
             }
             ate_comma = false;
diff --git a/src/test/run-pass/tool_lints.rs b/src/test/run-pass/tool_lints.rs
index 24ec43b12f6..2705c03598a 100644
--- a/src/test/run-pass/tool_lints.rs
+++ b/src/test/run-pass/tool_lints.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(tool_lints)]
+
 #![deny(unknown_lints)]
 
 #[allow(clippy::almost_swapped)]
diff --git a/src/test/run-pass/tool_lints_2018_preview.rs b/src/test/run-pass/tool_lints_2018_preview.rs
index 6cd57eaa195..57df3e072a8 100644
--- a/src/test/run-pass/tool_lints_2018_preview.rs
+++ b/src/test/run-pass/tool_lints_2018_preview.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(tool_lints)]
+
 #![feature(rust_2018_preview)]
 #![deny(unknown_lints)]
 
diff --git a/src/test/ui-fulldeps/lint_tool_test.rs b/src/test/ui-fulldeps/lint_tool_test.rs
index ebe10b3714f..11b70d1d780 100644
--- a/src/test/ui-fulldeps/lint_tool_test.rs
+++ b/src/test/ui-fulldeps/lint_tool_test.rs
@@ -11,8 +11,8 @@
 // aux-build:lint_tool_test.rs
 // ignore-stage1
 // compile-flags: --cfg foo
+
 #![feature(plugin)]
-#![feature(tool_lints)]
 #![plugin(lint_tool_test)]
 #![allow(dead_code)]
 #![cfg_attr(foo, warn(test_lint))]
diff --git a/src/test/ui/cfg-attr-trailing-comma.rs b/src/test/ui/cfg-attr-trailing-comma.rs
deleted file mode 100644
index 21e00544ca0..00000000000
--- a/src/test/ui/cfg-attr-trailing-comma.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// compile-flags: --cfg TRUE
-
-#[cfg_attr(TRUE, inline,)] // OK
-fn f() {}
-
-#[cfg_attr(FALSE, inline,)] // OK
-fn g() {}
-
-#[cfg_attr(TRUE, inline,,)] //~ ERROR expected `)`, found `,`
-fn h() {}
-
-#[cfg_attr(FALSE, inline,,)] //~ ERROR expected `)`, found `,`
-fn i() {}
diff --git a/src/test/ui/cfg-attr-trailing-comma.stderr b/src/test/ui/cfg-attr-trailing-comma.stderr
deleted file mode 100644
index 76a470417e9..00000000000
--- a/src/test/ui/cfg-attr-trailing-comma.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: expected `)`, found `,`
-  --> $DIR/cfg-attr-trailing-comma.rs:9:25
-   |
-LL | #[cfg_attr(TRUE, inline,,)] //~ ERROR expected `)`, found `,`
-   |                         ^ expected `)`
-
-error: expected `)`, found `,`
-  --> $DIR/cfg-attr-trailing-comma.rs:12:26
-   |
-LL | #[cfg_attr(FALSE, inline,,)] //~ ERROR expected `)`, found `,`
-   |                          ^ expected `)`
-
-error: aborting due to 2 previous errors
-
diff --git a/src/test/ui/chalkify/lower_trait.rs b/src/test/ui/chalkify/lower_trait.rs
index c5ba5beeca7..ba7d4ff0d9b 100644
--- a/src/test/ui/chalkify/lower_trait.rs
+++ b/src/test/ui/chalkify/lower_trait.rs
@@ -10,11 +10,12 @@
 
 #![feature(rustc_attrs)]
 
+trait Bar { }
+
 #[rustc_dump_program_clauses] //~ ERROR program clause dump
-trait Foo<S, T, U> {
-    fn s(_: S) -> S;
-    fn t(_: T) -> T;
-    fn u(_: U) -> U;
+trait Foo<S, T: ?Sized> {
+    #[rustc_dump_program_clauses] //~ ERROR program clause dump
+    type Assoc: Bar + ?Sized;
 }
 
 fn main() {
diff --git a/src/test/ui/chalkify/lower_trait.stderr b/src/test/ui/chalkify/lower_trait.stderr
index c4e768415d6..dc2375277e7 100644
--- a/src/test/ui/chalkify/lower_trait.stderr
+++ b/src/test/ui/chalkify/lower_trait.stderr
@@ -1,14 +1,23 @@
 error: program clause dump
-  --> $DIR/lower_trait.rs:13:1
+  --> $DIR/lower_trait.rs:15:1
    |
 LL | #[rustc_dump_program_clauses] //~ ERROR program clause dump
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: FromEnv(S: std::marker::Sized) :- FromEnv(Self: Foo<S, T, U>).
-   = note: FromEnv(T: std::marker::Sized) :- FromEnv(Self: Foo<S, T, U>).
-   = note: FromEnv(U: std::marker::Sized) :- FromEnv(Self: Foo<S, T, U>).
-   = note: Implemented(Self: Foo<S, T, U>) :- FromEnv(Self: Foo<S, T, U>).
-   = note: WellFormed(Self: Foo<S, T, U>) :- Implemented(Self: Foo<S, T, U>), WellFormed(S: std::marker::Sized), WellFormed(T: std::marker::Sized), WellFormed(U: std::marker::Sized).
+   = note: FromEnv(<Self as Foo<S, T>>::Assoc: Bar) :- FromEnv(Self: Foo<S, T>).
+   = note: FromEnv(S: std::marker::Sized) :- FromEnv(Self: Foo<S, T>).
+   = note: Implemented(Self: Foo<S, T>) :- FromEnv(Self: Foo<S, T>).
+   = note: WellFormed(Self: Foo<S, T>) :- Implemented(Self: Foo<S, T>), WellFormed(S: std::marker::Sized), WellFormed(<Self as Foo<S, T>>::Assoc: Bar).
 
-error: aborting due to previous error
+error: program clause dump
+  --> $DIR/lower_trait.rs:17:5
+   |
+LL |     #[rustc_dump_program_clauses] //~ ERROR program clause dump
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: FromEnv(Self: Foo<S, T>) :- FromEnv(Unnormalized(<Self as Foo<S, T>>::Assoc)).
+   = note: ProjectionEq(<Self as Foo<S, T>>::Assoc == Unnormalized(<Self as Foo<S, T>>::Assoc)).
+   = note: WellFormed(Unnormalized(<Self as Foo<S, T>>::Assoc)) :- Implemented(Self: Foo<S, T>).
+
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/auxiliary/namespaced_enums.rs b/src/test/ui/conditional-compilation/auxiliary/namespaced_enums.rs
index 3bf39b788db..3bf39b788db 100644
--- a/src/test/ui/auxiliary/namespaced_enums.rs
+++ b/src/test/ui/conditional-compilation/auxiliary/namespaced_enums.rs
diff --git a/src/test/ui/cfg-arg-invalid-1.rs b/src/test/ui/conditional-compilation/cfg-arg-invalid-1.rs
index 36dd78dd2b1..36dd78dd2b1 100644
--- a/src/test/ui/cfg-arg-invalid-1.rs
+++ b/src/test/ui/conditional-compilation/cfg-arg-invalid-1.rs
diff --git a/src/test/ui/cfg-arg-invalid-2.rs b/src/test/ui/conditional-compilation/cfg-arg-invalid-2.rs
index 48d656a4a28..48d656a4a28 100644
--- a/src/test/ui/cfg-arg-invalid-2.rs
+++ b/src/test/ui/conditional-compilation/cfg-arg-invalid-2.rs
diff --git a/src/test/ui/cfg-arg-invalid-3.rs b/src/test/ui/conditional-compilation/cfg-arg-invalid-3.rs
index 96ac7828c5c..96ac7828c5c 100644
--- a/src/test/ui/cfg-arg-invalid-3.rs
+++ b/src/test/ui/conditional-compilation/cfg-arg-invalid-3.rs
diff --git a/src/test/ui/cfg-arg-invalid-4.rs b/src/test/ui/conditional-compilation/cfg-arg-invalid-4.rs
index e7dfa17b4b6..e7dfa17b4b6 100644
--- a/src/test/ui/cfg-arg-invalid-4.rs
+++ b/src/test/ui/conditional-compilation/cfg-arg-invalid-4.rs
diff --git a/src/test/ui/cfg-arg-invalid-5.rs b/src/test/ui/conditional-compilation/cfg-arg-invalid-5.rs
index a939f451038..a939f451038 100644
--- a/src/test/ui/cfg-arg-invalid-5.rs
+++ b/src/test/ui/conditional-compilation/cfg-arg-invalid-5.rs
diff --git a/src/test/ui/cfg-attr-cfg-2.rs b/src/test/ui/conditional-compilation/cfg-attr-cfg-2.rs
index 58a62d45ea5..58a62d45ea5 100644
--- a/src/test/ui/cfg-attr-cfg-2.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-cfg-2.rs
diff --git a/src/test/ui/cfg-attr-cfg-2.stderr b/src/test/ui/conditional-compilation/cfg-attr-cfg-2.stderr
index db3c7acff15..db3c7acff15 100644
--- a/src/test/ui/cfg-attr-cfg-2.stderr
+++ b/src/test/ui/conditional-compilation/cfg-attr-cfg-2.stderr
diff --git a/src/test/ui/cfg-attr-crate-2.rs b/src/test/ui/conditional-compilation/cfg-attr-crate-2.rs
index a79c7663861..a79c7663861 100644
--- a/src/test/ui/cfg-attr-crate-2.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-crate-2.rs
diff --git a/src/test/ui/cfg-attr-crate-2.stderr b/src/test/ui/conditional-compilation/cfg-attr-crate-2.stderr
index 7b66c8f5e40..a730473f663 100644
--- a/src/test/ui/cfg-attr-crate-2.stderr
+++ b/src/test/ui/conditional-compilation/cfg-attr-crate-2.stderr
@@ -2,7 +2,7 @@ error[E0658]: no_core is experimental (see issue #29639)
   --> $DIR/cfg-attr-crate-2.rs:15:21
    |
 LL | #![cfg_attr(broken, no_core)] //~ ERROR no_core is experimental
-   |                     ^^^^^^^^
+   |                     ^^^^^^^
    |
    = help: add #![feature(no_core)] to the crate attributes to enable
 
diff --git a/src/test/ui/cfg-attr-invalid-predicate.rs b/src/test/ui/conditional-compilation/cfg-attr-invalid-predicate.rs
index 09fe6cec49c..09fe6cec49c 100644
--- a/src/test/ui/cfg-attr-invalid-predicate.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-invalid-predicate.rs
diff --git a/src/test/ui/cfg-attr-invalid-predicate.stderr b/src/test/ui/conditional-compilation/cfg-attr-invalid-predicate.stderr
index 5a89f9766d1..5a89f9766d1 100644
--- a/src/test/ui/cfg-attr-invalid-predicate.stderr
+++ b/src/test/ui/conditional-compilation/cfg-attr-invalid-predicate.stderr
diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-false.rs b/src/test/ui/conditional-compilation/cfg-attr-multi-false.rs
new file mode 100644
index 00000000000..84bd33fc0e7
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-false.rs
@@ -0,0 +1,20 @@
+// Test that cfg_attr doesn't emit any attributes when the
+// configuation variable is false. This mirrors `cfg-attr-multi-true.rs`
+
+// compile-pass
+
+#![warn(unused_must_use)]
+#![feature(cfg_attr_multi)]
+
+#[cfg_attr(any(), deprecated, must_use)]
+struct Struct {}
+
+impl Struct {
+    fn new() -> Struct {
+        Struct {}
+    }
+}
+
+fn main() {
+    Struct::new();
+}
diff --git a/src/test/ui/feature-gates/feature-gate-tool_lints-fail.rs b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.rs
index c311eb7ed7a..d4c3186a6eb 100644
--- a/src/test/ui/feature-gates/feature-gate-tool_lints-fail.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.rs
@@ -1,4 +1,4 @@
-// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -7,6 +7,10 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+//
+// compile-flags: --cfg broken
+
+#![feature(cfg_attr_multi)]
+#![cfg_attr(broken, no_core, no_std)] //~ ERROR no_core is experimental
 
-#[warn(clippy::assign_ops)] //~ ERROR scoped lint `clippy::assign_ops` is experimental
-fn main() {}
+fn main() { }
diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.stderr b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.stderr
new file mode 100644
index 00000000000..bf68d92cc0b
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.stderr
@@ -0,0 +1,11 @@
+error[E0658]: no_core is experimental (see issue #29639)
+  --> $DIR/cfg-attr-multi-invalid-1.rs:14:21
+   |
+LL | #![cfg_attr(broken, no_core, no_std)] //~ ERROR no_core is experimental
+   |                     ^^^^^^^
+   |
+   = help: add #![feature(no_core)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.rs b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.rs
new file mode 100644
index 00000000000..bee6b7d4886
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.rs
@@ -0,0 +1,16 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// compile-flags: --cfg broken
+
+#![feature(cfg_attr_multi)]
+#![cfg_attr(broken, no_std, no_core)] //~ ERROR no_core is experimental
+
+fn main() { }
diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.stderr b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.stderr
new file mode 100644
index 00000000000..5c72a400e0b
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-2.stderr
@@ -0,0 +1,11 @@
+error[E0658]: no_core is experimental (see issue #29639)
+  --> $DIR/cfg-attr-multi-invalid-2.rs:14:29
+   |
+LL | #![cfg_attr(broken, no_std, no_core)] //~ ERROR no_core is experimental
+   |                             ^^^^^^^
+   |
+   = help: add #![feature(no_core)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-true.rs b/src/test/ui/conditional-compilation/cfg-attr-multi-true.rs
new file mode 100644
index 00000000000..a31dde00c7c
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-true.rs
@@ -0,0 +1,22 @@
+// Test that cfg_attr with multiple attributes actually emits both attributes.
+// This is done by emitting two attributes that cause new warnings, and then
+// triggering those warnings.
+
+// compile-pass
+
+#![warn(unused_must_use)]
+#![feature(cfg_attr_multi)]
+
+#[cfg_attr(all(), deprecated, must_use)]
+struct MustUseDeprecated {}
+
+impl MustUseDeprecated { //~ warning: use of deprecated item
+    fn new() -> MustUseDeprecated { //~ warning: use of deprecated item
+        MustUseDeprecated {} //~ warning: use of deprecated item
+    }
+}
+
+fn main() {
+    MustUseDeprecated::new(); //~ warning: use of deprecated item
+    //| warning: unused `MustUseDeprecated` which must be used
+}
diff --git a/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr
new file mode 100644
index 00000000000..37cb3de06c0
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-true.stderr
@@ -0,0 +1,38 @@
+warning: use of deprecated item 'MustUseDeprecated'
+  --> $DIR/cfg-attr-multi-true.rs:13:6
+   |
+LL | impl MustUseDeprecated { //~ warning: use of deprecated item
+   |      ^^^^^^^^^^^^^^^^^
+   |
+   = note: #[warn(deprecated)] on by default
+
+warning: use of deprecated item 'MustUseDeprecated'
+  --> $DIR/cfg-attr-multi-true.rs:20:5
+   |
+LL |     MustUseDeprecated::new(); //~ warning: use of deprecated item
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+warning: use of deprecated item 'MustUseDeprecated'
+  --> $DIR/cfg-attr-multi-true.rs:14:17
+   |
+LL |     fn new() -> MustUseDeprecated { //~ warning: use of deprecated item
+   |                 ^^^^^^^^^^^^^^^^^
+
+warning: use of deprecated item 'MustUseDeprecated'
+  --> $DIR/cfg-attr-multi-true.rs:15:9
+   |
+LL |         MustUseDeprecated {} //~ warning: use of deprecated item
+   |         ^^^^^^^^^^^^^^^^^
+
+warning: unused `MustUseDeprecated` which must be used
+  --> $DIR/cfg-attr-multi-true.rs:20:5
+   |
+LL |     MustUseDeprecated::new(); //~ warning: use of deprecated item
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/cfg-attr-multi-true.rs:7:9
+   |
+LL | #![warn(unused_must_use)]
+   |         ^^^^^^^^^^^^^^^
+
diff --git a/src/test/ui/conditional-compilation/cfg-attr-parse.rs b/src/test/ui/conditional-compilation/cfg-attr-parse.rs
new file mode 100644
index 00000000000..eec0e8faca8
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-parse.rs
@@ -0,0 +1,45 @@
+// Parse `cfg_attr` with varying numbers of attributes and trailing commas
+
+#![feature(cfg_attr_multi)]
+
+// Completely empty `cfg_attr` input
+#[cfg_attr()] //~ error: expected identifier, found `)`
+struct NoConfigurationPredicate;
+
+// Zero attributes, zero trailing comma (comma manatory here)
+#[cfg_attr(all())] //~ error: expected `,`, found `)`
+struct A0C0;
+
+// Zero attributes, one trailing comma
+#[cfg_attr(all(),)] // Ok
+struct A0C1;
+
+// Zero attributes, two trailing commas
+#[cfg_attr(all(),,)] //~ ERROR expected identifier
+struct A0C2;
+
+// One attribute, no trailing comma
+#[cfg_attr(all(), must_use)] // Ok
+struct A1C0;
+
+// One attribute, one trailing comma
+#[cfg_attr(all(), must_use,)] // Ok
+struct A1C1;
+
+// One attribute, two trailing commas
+#[cfg_attr(all(), must_use,,)] //~ ERROR expected identifier
+struct A1C2;
+
+// Two attributes, no trailing comma
+#[cfg_attr(all(), must_use, deprecated)] // Ok
+struct A2C0;
+
+// Two attributes, one trailing comma
+#[cfg_attr(all(), must_use, deprecated,)] // Ok
+struct A2C1;
+
+// Two attributes, two trailing commas
+#[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
+struct A2C2;
+
+fn main() {}
diff --git a/src/test/ui/conditional-compilation/cfg-attr-parse.stderr b/src/test/ui/conditional-compilation/cfg-attr-parse.stderr
new file mode 100644
index 00000000000..553406b6dd8
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-parse.stderr
@@ -0,0 +1,32 @@
+error: expected identifier, found `)`
+  --> $DIR/cfg-attr-parse.rs:6:12
+   |
+LL | #[cfg_attr()] //~ error: expected identifier, found `)`
+   |            ^ expected identifier
+
+error: expected `,`, found `)`
+  --> $DIR/cfg-attr-parse.rs:10:17
+   |
+LL | #[cfg_attr(all())] //~ error: expected `,`, found `)`
+   |                 ^ expected `,`
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:18:18
+   |
+LL | #[cfg_attr(all(),,)] //~ ERROR expected identifier
+   |                  ^ expected identifier
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:30:28
+   |
+LL | #[cfg_attr(all(), must_use,,)] //~ ERROR expected identifier
+   |                            ^ expected identifier
+
+error: expected identifier, found `,`
+  --> $DIR/cfg-attr-parse.rs:42:40
+   |
+LL | #[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
+   |                                        ^ expected identifier
+
+error: aborting due to 5 previous errors
+
diff --git a/src/test/ui/cfg-attr-syntax-validation.rs b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs
index 06a22eff25c..06a22eff25c 100644
--- a/src/test/ui/cfg-attr-syntax-validation.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.rs
diff --git a/src/test/ui/cfg-attr-syntax-validation.stderr b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr
index 7773fdb8cf9..7773fdb8cf9 100644
--- a/src/test/ui/cfg-attr-syntax-validation.stderr
+++ b/src/test/ui/conditional-compilation/cfg-attr-syntax-validation.stderr
diff --git a/src/test/ui/cfg-attr-unknown-attribute-macro-expansion.rs b/src/test/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.rs
index afcb896b43c..afcb896b43c 100644
--- a/src/test/ui/cfg-attr-unknown-attribute-macro-expansion.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.rs
diff --git a/src/test/ui/cfg-attr-unknown-attribute-macro-expansion.stderr b/src/test/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.stderr
index 0f51c7d68c6..0f51c7d68c6 100644
--- a/src/test/ui/cfg-attr-unknown-attribute-macro-expansion.stderr
+++ b/src/test/ui/conditional-compilation/cfg-attr-unknown-attribute-macro-expansion.stderr
diff --git a/src/test/ui/cfg-empty-codemap.rs b/src/test/ui/conditional-compilation/cfg-empty-codemap.rs
index 5cf8135ca6b..5cf8135ca6b 100644
--- a/src/test/ui/cfg-empty-codemap.rs
+++ b/src/test/ui/conditional-compilation/cfg-empty-codemap.rs
diff --git a/src/test/ui/cfg-in-crate-1.rs b/src/test/ui/conditional-compilation/cfg-in-crate-1.rs
index bbccf2bcd0f..bbccf2bcd0f 100644
--- a/src/test/ui/cfg-in-crate-1.rs
+++ b/src/test/ui/conditional-compilation/cfg-in-crate-1.rs
diff --git a/src/test/ui/cfg-in-crate-1.stderr b/src/test/ui/conditional-compilation/cfg-in-crate-1.stderr
index c6d42c732c9..c6d42c732c9 100644
--- a/src/test/ui/cfg-in-crate-1.stderr
+++ b/src/test/ui/conditional-compilation/cfg-in-crate-1.stderr
diff --git a/src/test/ui/cfg-non-opt-expr.rs b/src/test/ui/conditional-compilation/cfg-non-opt-expr.rs
index 55eca7f45a5..55eca7f45a5 100644
--- a/src/test/ui/cfg-non-opt-expr.rs
+++ b/src/test/ui/conditional-compilation/cfg-non-opt-expr.rs
diff --git a/src/test/ui/cfg-non-opt-expr.stderr b/src/test/ui/conditional-compilation/cfg-non-opt-expr.stderr
index 1892cee113e..1892cee113e 100644
--- a/src/test/ui/cfg-non-opt-expr.stderr
+++ b/src/test/ui/conditional-compilation/cfg-non-opt-expr.stderr
diff --git a/src/test/ui/cfg_attr_path.rs b/src/test/ui/conditional-compilation/cfg_attr_path.rs
index 7d799850a65..7d799850a65 100644
--- a/src/test/ui/cfg_attr_path.rs
+++ b/src/test/ui/conditional-compilation/cfg_attr_path.rs
diff --git a/src/test/ui/cfg_attr_path.stderr b/src/test/ui/conditional-compilation/cfg_attr_path.stderr
index 67e59d054d5..67e59d054d5 100644
--- a/src/test/ui/cfg_attr_path.stderr
+++ b/src/test/ui/conditional-compilation/cfg_attr_path.stderr
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.rs b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.rs
new file mode 100644
index 00000000000..9515380bc28
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.rs
@@ -0,0 +1,5 @@
+// gate-test-cfg_attr_multi
+
+#![cfg_attr(all(), warn(nonstandard_style), allow(unused_attributes))]
+//~^ ERROR cfg_attr with zero or more than one attributes is experimental
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.stderr b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.stderr
new file mode 100644
index 00000000000..088e6df1a1a
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-1.stderr
@@ -0,0 +1,11 @@
+error[E0658]: cfg_attr with zero or more than one attributes is experimental (see issue #54881)
+  --> $DIR/feature-gate-cfg-attr-multi-1.rs:3:1
+   |
+LL | #![cfg_attr(all(), warn(nonstandard_style), allow(unused_attributes))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(cfg_attr_multi)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.rs b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.rs
new file mode 100644
index 00000000000..cf02432274b
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.rs
@@ -0,0 +1,3 @@
+#![cfg_attr(all(),)]
+//~^ ERROR cfg_attr with zero or more than one attributes is experimental
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.stderr b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.stderr
new file mode 100644
index 00000000000..a01876114dd
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-2.stderr
@@ -0,0 +1,11 @@
+error[E0658]: cfg_attr with zero or more than one attributes is experimental (see issue #54881)
+  --> $DIR/feature-gate-cfg-attr-multi-2.rs:1:1
+   |
+LL | #![cfg_attr(all(),)]
+   | ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(cfg_attr_multi)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-1.rs b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-1.rs
new file mode 100644
index 00000000000..e4737926e7a
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-1.rs
@@ -0,0 +1,7 @@
+// Test that settingt the featute gate while using its functionality doesn't error.
+
+// compile-pass
+
+#![cfg_attr(all(), feature(cfg_attr_multi), crate_type="bin")]
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-2.rs b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-2.rs
new file mode 100644
index 00000000000..df740541f55
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-cfg-attr-multi-bootstrap-2.rs
@@ -0,0 +1,9 @@
+// Test that settingt the featute gate while using its functionality doesn't error.
+// Specifically, if there's a cfg-attr *before* the feature gate.
+
+// compile-pass
+
+#![cfg_attr(all(),)]
+#![cfg_attr(all(), feature(cfg_attr_multi), crate_type="bin")]
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-tool_lints-fail.stderr b/src/test/ui/feature-gates/feature-gate-tool_lints-fail.stderr
deleted file mode 100644
index 33ee79cd201..00000000000
--- a/src/test/ui/feature-gates/feature-gate-tool_lints-fail.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error[E0658]: scoped lint `clippy::assign_ops` is experimental (see issue #44690)
-  --> $DIR/feature-gate-tool_lints-fail.rs:11:8
-   |
-LL | #[warn(clippy::assign_ops)] //~ ERROR scoped lint `clippy::assign_ops` is experimental
-   |        ^^^^^^^^^^^^^^^^^^
-   |
-   = help: add #![feature(tool_lints)] to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-tool_lints.stderr b/src/test/ui/feature-gates/feature-gate-tool_lints.stderr
deleted file mode 100644
index 8019b1e6a28..00000000000
--- a/src/test/ui/feature-gates/feature-gate-tool_lints.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error[E0658]: scoped lint `clippy::decimal_literal_representation` is experimental (see issue #44690)
-  --> $DIR/feature-gate-tool_lints.rs:11:8
-   |
-LL | #[warn(clippy::decimal_literal_representation)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: add #![feature(tool_lints)] to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/feature-gates/feature-gate-tool_lints.rs b/src/test/ui/resolve/issue-54379.rs
index 3ef67982be9..24aa758ea6c 100644
--- a/src/test/ui/feature-gates/feature-gate-tool_lints.rs
+++ b/src/test/ui/resolve/issue-54379.rs
@@ -7,9 +7,15 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+struct MyStruct {
+    pub s1: Option<String>,
+}
 
-#[warn(clippy::decimal_literal_representation)]
-//~^ ERROR scoped lint `clippy::decimal_literal_representation` is experimental
 fn main() {
-    let a = 65_535;
+    let thing = MyStruct { s1: None };
+
+    match thing {
+        MyStruct { .., Some(_) } => {},
+        _ => {}
+    }
 }
diff --git a/src/test/ui/resolve/issue-54379.stderr b/src/test/ui/resolve/issue-54379.stderr
new file mode 100644
index 00000000000..d1d693a3817
--- /dev/null
+++ b/src/test/ui/resolve/issue-54379.stderr
@@ -0,0 +1,24 @@
+error: expected `}`, found `,`
+  --> $DIR/issue-54379.rs:18:22
+   |
+LL |         MyStruct { .., Some(_) } => {},
+   |                    --^
+   |                    | |
+   |                    | expected `}`
+   |                    `..` must be at the end and cannot have a trailing comma
+
+error: expected `,`
+  --> $DIR/issue-54379.rs:18:24
+   |
+LL |         MyStruct { .., Some(_) } => {},
+   |                        ^^^^
+
+error[E0027]: pattern does not mention field `s1`
+  --> $DIR/issue-54379.rs:18:9
+   |
+LL |         MyStruct { .., Some(_) } => {},
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ missing field `s1`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0027`.
diff --git a/src/test/ui/tool_lints-fail.rs b/src/test/ui/tool_lints-fail.rs
index ea1efab4cb6..4134fca1ce6 100644
--- a/src/test/ui/tool_lints-fail.rs
+++ b/src/test/ui/tool_lints-fail.rs
@@ -10,7 +10,7 @@
 
 // Don't allow tool_lints, which aren't scoped
 
-#![feature(tool_lints)]
+
 #![deny(unknown_lints)]
 
 #![deny(clippy)] //~ ERROR: unknown lint: `clippy`
diff --git a/src/test/ui/tool_lints.rs b/src/test/ui/tool_lints.rs
index 71f90b17c18..001f2f11e5c 100644
--- a/src/test/ui/tool_lints.rs
+++ b/src/test/ui/tool_lints.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(tool_lints)]
+
 
 #[warn(foo::bar)]
 //~^ ERROR an unknown tool name found in scoped lint: `foo::bar`
diff --git a/src/test/ui/unknown-lint-tool-name.rs b/src/test/ui/unknown-lint-tool-name.rs
index 78b736edceb..a1d6c27e518 100644
--- a/src/test/ui/unknown-lint-tool-name.rs
+++ b/src/test/ui/unknown-lint-tool-name.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(tool_lints)]
+
 
 #![deny(foo::bar)] //~ ERROR an unknown tool name found in scoped lint: `foo::bar`