about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_expand/src/config.rs178
-rw-r--r--compiler/rustc_expand/src/expand.rs180
-rw-r--r--compiler/rustc_passes/src/check_attr.rs11
3 files changed, 228 insertions, 141 deletions
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index e0bdeb30dc8..5fa7ffd554e 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -328,6 +328,10 @@ impl<'a> StripUnconfigured<'a> {
         });
     }
 
+    fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> {
+        if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { vec![attr] }
+    }
+
     /// 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.
@@ -335,11 +339,7 @@ impl<'a> StripUnconfigured<'a> {
     /// Gives a compiler warning when the `cfg_attr` contains no attributes and
     /// is in the original source file. Gives a compiler error if the syntax of
     /// the attribute is incorrect.
-    fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> {
-        if !attr.has_name(sym::cfg_attr) {
-            return vec![attr];
-        }
-
+    crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> {
         let (cfg_predicate, expanded_attrs) =
             match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) {
                 None => return vec![],
@@ -348,95 +348,109 @@ impl<'a> StripUnconfigured<'a> {
 
         // Lint on zero attributes in source.
         if expanded_attrs.is_empty() {
-            return vec![attr];
+            self.sess.parse_sess.buffer_lint(
+                rustc_lint_defs::builtin::UNUSED_ATTRIBUTES,
+                attr.span,
+                ast::CRATE_NODE_ID,
+                "`#[cfg_attr]` does not expand to any attributes",
+            );
         }
 
         if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
             return vec![];
         }
 
-        // 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(|(item, span)| {
-                let orig_tokens = attr.tokens().to_tokenstream();
-
-                // We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
-                // and producing an attribute of the form `#[attr]`. We
-                // have captured tokens for `attr` itself, but we need to
-                // synthesize tokens for the wrapper `#` and `[]`, which
-                // we do below.
-
-                // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
-                // for `attr` when we expand it to `#[attr]`
-                let mut orig_trees = orig_tokens.trees();
-                let pound_token = match orig_trees.next().unwrap() {
-                    TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
-                    _ => panic!("Bad tokens for attribute {:?}", attr),
-                };
-                let pound_span = pound_token.span;
-
-                let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
-                if attr.style == AttrStyle::Inner {
-                    // For inner attributes, we do the same thing for the `!` in `#![some_attr]`
-                    let bang_token = match orig_trees.next().unwrap() {
-                        TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
-                        _ => panic!("Bad tokens for attribute {:?}", attr),
-                    };
-                    trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
-                }
-                // We don't really have a good span to use for the syntheized `[]`
-                // in `#[attr]`, so just use the span of the `#` token.
-                let bracket_group = AttrAnnotatedTokenTree::Delimited(
-                    DelimSpan::from_single(pound_span),
-                    DelimToken::Bracket,
-                    item.tokens
-                        .as_ref()
-                        .unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
-                        .create_token_stream(),
-                );
-                trees.push((bracket_group, Spacing::Alone));
-                let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
-                let attr = attr::mk_attr_from_item(item, tokens, attr.style, span);
-                if attr.has_name(sym::crate_type) {
-                    self.sess.parse_sess.buffer_lint(
-                        rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
-                        attr.span,
-                        ast::CRATE_NODE_ID,
-                        "`crate_type` within an `#![cfg_attr] attribute is deprecated`",
-                    );
-                }
-                if attr.has_name(sym::crate_name) {
-                    self.sess.parse_sess.buffer_lint(
-                        rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
-                        attr.span,
-                        ast::CRATE_NODE_ID,
-                        "`crate_name` within an `#![cfg_attr] attribute is deprecated`",
-                    );
-                }
-                self.process_cfg_attr(attr)
-            })
-            .collect()
+        if recursive {
+            // 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(|item| self.process_cfg_attr(self.expand_cfg_attr_item(&attr, item)))
+                .collect()
+        } else {
+            expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(&attr, item)).collect()
+        }
+    }
+
+    fn expand_cfg_attr_item(
+        &self,
+        attr: &Attribute,
+        (item, item_span): (ast::AttrItem, Span),
+    ) -> Attribute {
+        let orig_tokens = attr.tokens().to_tokenstream();
+
+        // We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
+        // and producing an attribute of the form `#[attr]`. We
+        // have captured tokens for `attr` itself, but we need to
+        // synthesize tokens for the wrapper `#` and `[]`, which
+        // we do below.
+
+        // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
+        // for `attr` when we expand it to `#[attr]`
+        let mut orig_trees = orig_tokens.trees();
+        let pound_token = match orig_trees.next().unwrap() {
+            TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
+            _ => panic!("Bad tokens for attribute {:?}", attr),
+        };
+        let pound_span = pound_token.span;
+
+        let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
+        if attr.style == AttrStyle::Inner {
+            // For inner attributes, we do the same thing for the `!` in `#![some_attr]`
+            let bang_token = match orig_trees.next().unwrap() {
+                TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
+                _ => panic!("Bad tokens for attribute {:?}", attr),
+            };
+            trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
+        }
+        // We don't really have a good span to use for the syntheized `[]`
+        // in `#[attr]`, so just use the span of the `#` token.
+        let bracket_group = AttrAnnotatedTokenTree::Delimited(
+            DelimSpan::from_single(pound_span),
+            DelimToken::Bracket,
+            item.tokens
+                .as_ref()
+                .unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
+                .create_token_stream(),
+        );
+        trees.push((bracket_group, Spacing::Alone));
+        let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
+        let attr = attr::mk_attr_from_item(item, tokens, attr.style, item_span);
+        if attr.has_name(sym::crate_type) {
+            self.sess.parse_sess.buffer_lint(
+                rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+                attr.span,
+                ast::CRATE_NODE_ID,
+                "`crate_type` within an `#![cfg_attr] attribute is deprecated`",
+            );
+        }
+        if attr.has_name(sym::crate_name) {
+            self.sess.parse_sess.buffer_lint(
+                rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+                attr.span,
+                ast::CRATE_NODE_ID,
+                "`crate_name` within an `#![cfg_attr] attribute is deprecated`",
+            );
+        }
+        attr
     }
 
     /// Determines if a node with the given attributes should be included in this configuration.
     fn in_cfg(&self, attrs: &[Attribute]) -> bool {
-        attrs.iter().all(|attr| {
-            if !is_cfg(attr) {
+        attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr))
+    }
+
+    crate fn cfg_true(&self, attr: &Attribute) -> bool {
+        let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
+            Ok(meta_item) => meta_item,
+            Err(mut err) => {
+                err.emit();
                 return true;
             }
-            let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
-                Ok(meta_item) => meta_item,
-                Err(mut err) => {
-                    err.emit();
-                    return true;
-                }
-            };
-            parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
-                attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
-            })
+        };
+        parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
+            attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features)
         })
     }
 
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 07ce901fb41..7604a464be2 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -15,7 +15,6 @@ use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, For
 use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
 use rustc_ast::{NodeId, PatKind, StmtKind, TyKind};
 use rustc_ast_pretty::pprust;
-use rustc_attr::is_builtin_attr;
 use rustc_data_structures::map_in_place::MapInPlace;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, PResult};
@@ -1014,6 +1013,9 @@ trait InvocationCollectorNode: AstLike {
     fn to_annotatable(self) -> Annotatable;
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy;
     fn id(&mut self) -> &mut NodeId;
+    fn descr() -> &'static str {
+        unreachable!()
+    }
     fn noop_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy {
         unreachable!()
     }
@@ -1477,6 +1479,9 @@ impl InvocationCollectorNode for P<ast::Expr> {
     fn id(&mut self) -> &mut NodeId {
         &mut self.id
     }
+    fn descr() -> &'static str {
+        "an expression"
+    }
     fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
         noop_visit_expr(self, visitor)
     }
@@ -1576,13 +1581,28 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     ) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> {
         let mut attr = None;
 
+        let mut cfg_pos = None;
+        let mut attr_pos = None;
+        for (pos, attr) in item.attrs().iter().enumerate() {
+            if !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) {
+                let name = attr.ident().map(|ident| ident.name);
+                if name == Some(sym::cfg) || name == Some(sym::cfg_attr) {
+                    cfg_pos = Some(pos); // a cfg attr found, no need to search anymore
+                    break;
+                } else if attr_pos.is_none()
+                    && !name.map_or(false, rustc_feature::is_builtin_attr_name)
+                {
+                    attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr
+                }
+            }
+        }
+
         item.visit_attrs(|attrs| {
-            attr = attrs
-                .iter()
-                .position(|a| !self.cx.expanded_inert_attrs.is_marked(a) && !is_builtin_attr(a))
-                .map(|attr_pos| {
-                    let attr = attrs.remove(attr_pos);
-                    let following_derives = attrs[attr_pos..]
+            attr = Some(match (cfg_pos, attr_pos) {
+                (Some(pos), _) => (attrs.remove(pos), pos, Vec::new()),
+                (_, Some(pos)) => {
+                    let attr = attrs.remove(pos);
+                    let following_derives = attrs[pos..]
                         .iter()
                         .filter(|a| a.has_name(sym::derive))
                         .flat_map(|a| a.meta_item_list().unwrap_or_default())
@@ -1596,17 +1616,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                         })
                         .collect();
 
-                    (attr, attr_pos, following_derives)
-                })
+                    (attr, pos, following_derives)
+                }
+                _ => return,
+            });
         });
 
         attr
     }
 
-    fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
-        self.cfg.configure(node)
-    }
-
     // Detect use of feature-gated or invalid attributes on macro invocations
     // since they will not be detected after macro expansion.
     fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) {
@@ -1653,28 +1671,71 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         }
     }
 
+    fn expand_cfg_true(
+        &mut self,
+        node: &mut impl AstLike,
+        attr: ast::Attribute,
+        pos: usize,
+    ) -> bool {
+        let res = self.cfg.cfg_true(&attr);
+        if res {
+            // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion,
+            // and some tools like rustdoc and clippy rely on that. Find a way to remove them
+            // while keeping the tools working.
+            self.cx.expanded_inert_attrs.mark(&attr);
+            node.visit_attrs(|attrs| attrs.insert(pos, attr));
+        }
+        res
+    }
+
+    fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) {
+        node.visit_attrs(|attrs| {
+            attrs.splice(pos..pos, self.cfg.expand_cfg_attr(attr, false));
+        });
+    }
+
     fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
         &mut self,
-        node: Node,
+        mut node: Node,
     ) -> Node::OutputTy {
-        let mut node = configure!(self, node);
-
-        if let Some(attr) = self.take_first_attr(&mut node) {
-            Node::pre_flat_map_node_collect_attr(&self.cfg, &attr.0);
-            self.collect_attr(attr, node.to_annotatable(), Node::KIND).make_ast::<Node>()
-        } else if node.is_mac_call() {
-            let (mac, attrs, add_semicolon) = node.take_mac_call();
-            self.check_attributes(&attrs, &mac);
-            let mut res = self.collect_bang(mac, Node::KIND).make_ast::<Node>();
-            Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
-            res
-        } else {
-            match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| {
-                assign_id!(this, node.id(), || node.noop_flat_map(this))
-            }) {
-                Ok(output) => output,
-                Err(node) => self.flat_map_node(node),
-            }
+        loop {
+            return match self.take_first_attr(&mut node) {
+                Some((attr, pos, derives)) => match attr.name_or_empty() {
+                    sym::cfg => {
+                        if self.expand_cfg_true(&mut node, attr, pos) {
+                            continue;
+                        }
+                        Default::default()
+                    }
+                    sym::cfg_attr => {
+                        self.expand_cfg_attr(&mut node, attr, pos);
+                        continue;
+                    }
+                    _ => {
+                        Node::pre_flat_map_node_collect_attr(&self.cfg, &attr);
+                        self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND)
+                            .make_ast::<Node>()
+                    }
+                },
+                None if node.is_mac_call() => {
+                    let (mac, attrs, add_semicolon) = node.take_mac_call();
+                    self.check_attributes(&attrs, &mac);
+                    let mut res = self.collect_bang(mac, Node::KIND).make_ast::<Node>();
+                    Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
+                    res
+                }
+                None => {
+                    match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| {
+                        assign_id!(this, node.id(), || node.noop_flat_map(this))
+                    }) {
+                        Ok(output) => output,
+                        Err(returned_node) => {
+                            node = returned_node;
+                            continue;
+                        }
+                    }
+                }
+            };
         }
     }
 
@@ -1682,19 +1743,40 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
         &mut self,
         node: &mut Node,
     ) {
-        if let Some(attr) = self.take_first_attr(node) {
-            visit_clobber(node, |node| {
-                self.collect_attr(attr, node.to_annotatable(), Node::KIND).make_ast::<Node>()
-            })
-        } else if node.is_mac_call() {
-            visit_clobber(node, |node| {
-                // Do not clobber unless it's actually a macro (uncommon case).
-                let (mac, attrs, _) = node.take_mac_call();
-                self.check_attributes(&attrs, &mac);
-                self.collect_bang(mac, Node::KIND).make_ast::<Node>()
-            })
-        } else {
-            assign_id!(self, node.id(), || node.noop_visit(self))
+        loop {
+            return match self.take_first_attr(node) {
+                Some((attr, pos, derives)) => match attr.name_or_empty() {
+                    sym::cfg => {
+                        let span = attr.span;
+                        if self.expand_cfg_true(node, attr, pos) {
+                            continue;
+                        }
+                        let msg =
+                            format!("removing {} is not supported in this position", Node::descr());
+                        self.cx.span_err(span, &msg);
+                        continue;
+                    }
+                    sym::cfg_attr => {
+                        self.expand_cfg_attr(node, attr, pos);
+                        continue;
+                    }
+                    _ => visit_clobber(node, |node| {
+                        self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND)
+                            .make_ast::<Node>()
+                    }),
+                },
+                None if node.is_mac_call() => {
+                    visit_clobber(node, |node| {
+                        // Do not clobber unless it's actually a macro (uncommon case).
+                        let (mac, attrs, _) = node.take_mac_call();
+                        self.check_attributes(&attrs, &mac);
+                        self.collect_bang(mac, Node::KIND).make_ast::<Node>()
+                    })
+                }
+                None => {
+                    assign_id!(self, node.id(), || node.noop_visit(self))
+                }
+            };
         }
     }
 }
@@ -1750,7 +1832,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         self.flat_map_node(node)
     }
 
-    fn flat_map_stmt(&mut self, node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
+    fn flat_map_stmt(&mut self, mut node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
         // FIXME: invocations in semicolon-less expressions positions are expanded as expressions,
         // changing that requires some compatibility measures.
         if node.is_expr() {
@@ -1761,7 +1843,6 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
             // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
             // See #78991 for an investigation of treating macros in this position
             // as statements, rather than expressions, during parsing.
-            let mut node = configure!(self, node);
             return match &node.kind {
                 StmtKind::Expr(expr)
                     if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) =>
@@ -1793,7 +1874,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
     }
 
     fn visit_expr(&mut self, node: &mut P<ast::Expr>) {
-        self.cfg.configure_expr(node);
+        // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`.
+        if let Some(attr) = node.attrs.first() {
+            self.cfg.maybe_emit_expr_attr_err(attr);
+        }
         self.visit_node(node)
     }
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d7b00699491..6e52efe75c1 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -134,7 +134,6 @@ impl CheckAttrVisitor<'_> {
                 }
                 sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
                 sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
-                sym::cfg_attr => self.check_cfg_attr(hir_id, attr),
                 sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
                 sym::macro_export => self.check_macro_export(hir_id, attr, target),
                 sym::ignore | sym::should_panic | sym::proc_macro_derive => {
@@ -1823,16 +1822,6 @@ impl CheckAttrVisitor<'_> {
         }
     }
 
-    fn check_cfg_attr(&self, hir_id: HirId, attr: &Attribute) {
-        if let Some((_, attrs)) = rustc_parse::parse_cfg_attr(&attr, &self.tcx.sess.parse_sess) {
-            if attrs.is_empty() {
-                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
-                    lint.build("`#[cfg_attr]` does not expand to any attributes").emit();
-                });
-            }
-        }
-    }
-
     fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
         if target != Target::Fn {
             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {