about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2018-10-10 15:58:40 -0700
committerManish Goregaokar <manishsmail@gmail.com>2018-10-10 15:58:40 -0700
commit8ebc6d6dbbfe1ccc9a72d003d13c3d267c91c742 (patch)
tree9a9bf81b322ac5fe909138a79032ad7fb680cad9
parenta267b3a9ff041c92c3ce71a285512d97ae8f208b (diff)
parentbbe832d570e826b2012c09869aa77d6201932730 (diff)
downloadrust-8ebc6d6dbbfe1ccc9a72d003d13c3d267c91c742.tar.gz
rust-8ebc6d6dbbfe1ccc9a72d003d13c3d267c91c742.zip
Rollup merge of #54862 - Havvy:cfg_attr_multi, r=petrochenkov
Fixes #47311.
r? @nrc
-rw-r--r--src/doc/unstable-book/src/language-features/cfg-attr-multi.md20
-rw-r--r--src/libsyntax/config.rs95
-rw-r--r--src/libsyntax/feature_gate.rs3
-rw-r--r--src/libsyntax/parse/parser.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/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.rs16
-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
44 files changed, 360 insertions, 46 deletions
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/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..c786691ac1b 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -499,6 +499,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! (
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index d653ed819fd..0da7a61a7f2 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 {
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/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/conditional-compilation/cfg-attr-multi-invalid-1.rs b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.rs
new file mode 100644
index 00000000000..d4c3186a6eb
--- /dev/null
+++ b/src/test/ui/conditional-compilation/cfg-attr-multi-invalid-1.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_core, no_std)] //~ ERROR no_core is experimental
+
+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() {}