@@ -2,7 +2,7 @@ use crate::base::*;
22use crate::config::StripUnconfigured;
33use crate::errors::{
44 IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported,
5- UnsupportedKeyValue, WrongFragmentKind,
5+ UnsupportedExprInKeyValue, UnsupportedKeyValue, WrongFragmentKind,
66};
77use crate::hygiene::SyntaxContext;
88use crate::mbe::diagnostics::annotate_err_with_kind;
@@ -12,11 +12,11 @@ use crate::placeholders::{placeholder, PlaceholderExpander};
1212use rustc_ast as ast;
1313use rustc_ast::mut_visit::*;
1414use rustc_ast::ptr::P;
15- use rustc_ast::token::{self, Delimiter};
16- use rustc_ast::tokenstream::TokenStream;
15+ use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind };
16+ use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree} ;
1717use rustc_ast::visit::{self, AssocCtxt, Visitor};
18- use rustc_ast::{AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind };
19- use rustc_ast::{ForeignItemKind, HasAttrs, HasNodeId};
18+ use rustc_ast::{AssocItemKind, AstNodeWrapper, AttrArgs, AttrKind, AttrStyle };
19+ use rustc_ast::{AttrVec, ExprKind, ForeignItemKind, HasAttrs, HasNodeId};
2020use rustc_ast::{Inline, ItemKind, MacStmtStyle, MetaItemKind, ModKind};
2121use rustc_ast::{NestedMetaItem, NodeId, PatKind, StmtKind, TyKind};
2222use rustc_ast_pretty::pprust;
@@ -32,11 +32,11 @@ use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
3232use rustc_session::lint::BuiltinLintDiagnostics;
3333use rustc_session::parse::{feature_err, ParseSess};
3434use rustc_session::Limit;
35- use rustc_span::symbol::{sym, Ident};
35+ use rustc_span::symbol::{kw, sym, Ident};
3636use rustc_span::{FileName, LocalExpnId, Span};
3737
3838use smallvec::SmallVec;
39- use std::ops::Deref;
39+ use std::ops::{ControlFlow, Deref} ;
4040use std::path::PathBuf;
4141use std::rc::Rc;
4242use std::{iter, mem};
@@ -772,6 +772,93 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
772772 })
773773 }
774774
775+ /// Expand the macros in the values of an attribute such as:
776+ /// `#[stable(feature = get_feature_name!($signedness))]`
777+ fn expand_nested_meta(&mut self, attr: &mut ast::Attribute) {
778+ let AttrKind::Normal(normal_attr) = &mut attr.kind else { return };
779+ let AttrArgs::Delimited(delim_args) = &mut normal_attr.item.args else { return };
780+
781+ let tokens = delim_args.tokens.clone();
782+ let mut new_tokens = Vec::with_capacity(tokens.len());
783+ let subparser_name = Some("built-in attribute");
784+ let mut parser = Parser::new(self.cx.parse_sess(), tokens, subparser_name);
785+
786+ // Have any expansions occurred.
787+ let mut modified = false;
788+
789+ // If the attribute contains unrecognized syntax, just return early
790+ // without modifying `delim_args.tokens`. Whatever tries to parse it to
791+ // ast::MetaItem later will report its own error.
792+ while parser.token != token::Eof {
793+ // Parse name of a NameValue meta item.
794+ if parser.token.is_ident() {
795+ new_tokens.push(TokenTree::Token(parser.token.clone(), parser.token_spacing));
796+ parser.bump();
797+ } else {
798+ return;
799+ }
800+
801+ // Parse `=` between name and value.
802+ if parser.token == token::Eq {
803+ new_tokens.push(TokenTree::Token(parser.token.clone(), parser.token_spacing));
804+ parser.bump();
805+ } else {
806+ return;
807+ }
808+
809+ // Parse value expr, and if it's a macro call, then fully expand it
810+ // to a literal.
811+ match parser.parse_expr().map(P::into_inner) {
812+ Ok(mut expr) => {
813+ let expr_span = expr.span;
814+ let lit = match expr.kind {
815+ ExprKind::Lit(lit) => lit,
816+ ExprKind::MacCall(mac) => {
817+ modified = true;
818+ expr.kind = ExprKind::MacCall(mac);
819+ if let AstFragment::Expr(expr) =
820+ self.fully_expand_fragment(AstFragment::Expr(P(expr)))
821+ && let ExprKind::Lit(lit) = expr.kind
822+ {
823+ lit
824+ } else {
825+ self.cx.emit_err(UnsupportedExprInKeyValue { span: expr_span });
826+ Lit::new(LitKind::Err, kw::Empty, None)
827+ }
828+ }
829+ _ => {
830+ modified = true;
831+ self.cx.emit_err(UnsupportedExprInKeyValue { span: expr_span });
832+ Lit::new(LitKind::Err, kw::Empty, None)
833+ }
834+ };
835+ let token = Token::new(TokenKind::Literal(lit), expr_span);
836+ new_tokens.push(TokenTree::Token(token, Spacing::Alone));
837+ }
838+ Err(err) => {
839+ err.cancel();
840+ return;
841+ }
842+ };
843+
844+ // Comma separators, and optional trailing comma.
845+ if parser.token == token::Eof {
846+ break;
847+ } else if parser.token == token::Comma {
848+ new_tokens.push(TokenTree::Token(parser.token.clone(), parser.token_spacing));
849+ parser.bump();
850+ } else {
851+ return;
852+ }
853+ }
854+
855+ if modified {
856+ delim_args.tokens = TokenStream::new(new_tokens);
857+ normal_attr.tokens = None;
858+ normal_attr.item.tokens = None;
859+ }
860+ }
861+
775862 fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
776863 let kind = match item {
777864 Annotatable::Item(_)
@@ -1625,33 +1712,78 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
16251712 /// its position and derives following it. We have to collect the derives in order to resolve
16261713 /// legacy derive helpers (helpers written before derives that introduce them).
16271714 fn take_first_attr(
1628- &self,
1715+ &mut self,
16291716 item: &mut impl HasAttrs,
16301717 ) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> {
1631- let mut attr = None;
1632-
1633- let mut cfg_pos = None;
1634- let mut attr_pos = None;
1635- for (pos, attr) in item.attrs().iter().enumerate() {
1636- if !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) {
1718+ loop {
1719+ let mut cfg_pos = None;
1720+ let mut attr_pos = None;
1721+ let mut attr_is_builtin = false;
1722+ for (pos, attr) in item.attrs().iter().enumerate() {
1723+ if attr.is_doc_comment() || self.cx.expanded_inert_attrs.is_marked(attr) {
1724+ continue;
1725+ }
16371726 let name = attr.ident().map(|ident| ident.name);
16381727 if name == Some(sym::cfg) || name == Some(sym::cfg_attr) {
16391728 cfg_pos = Some(pos); // a cfg attr found, no need to search anymore
16401729 break;
16411730 } else if attr_pos.is_none()
1642- && !name.is_some_and(rustc_feature::is_builtin_attr_name)
1731+ && match name {
1732+ // User-defined attribute invoked using a single identifier.
1733+ Some(name) if !rustc_feature::is_builtin_attr_name(name) => true,
1734+ // A subset of builtin attributes, like `stable`, which expand
1735+ // nested macro calls within the attribute arguments.
1736+ Some(name) if rustc_feature::expand_nested_meta(name) => {
1737+ attr_is_builtin = true;
1738+ true
1739+ }
1740+ // Built-in inert attribute.
1741+ Some(_) => false,
1742+ // Attribute path longer than one identifier. These are
1743+ // user-defined attribute macros or tool attributes.
1744+ None => true,
1745+ }
16431746 {
16441747 attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr
16451748 }
16461749 }
1647- }
16481750
1649- item.visit_attrs(|attrs| {
1650- attr = Some(match (cfg_pos, attr_pos) {
1651- (Some(pos), _) => (attrs.remove(pos), pos, Vec::new()),
1652- (_, Some(pos)) => {
1653- let attr = attrs.remove(pos);
1654- let following_derives = attrs[pos..]
1751+ let mut control_flow = ControlFlow::Break(None);
1752+ item.visit_attrs(|attrs| match (cfg_pos, attr_pos) {
1753+ (Some(cfg_pos), _) => {
1754+ let cfg = attrs.remove(cfg_pos);
1755+ let following_derives = Vec::new();
1756+ control_flow = ControlFlow::Break(Some((cfg, cfg_pos, following_derives)));
1757+ }
1758+ (None, Some(attr_pos)) if attr_is_builtin => {
1759+ // A built-in attribute such as #[stable(feature = f!($x))].
1760+ // Eagerly expand its arguments here and now.
1761+ //
1762+ // This does not get a LocalExpnId because nothing else in
1763+ // `item` is affected by this expansion, unlike attribute
1764+ // macros which replace `item` with their own output. If a
1765+ // subsequent expansion within `item` fails, there is no
1766+ // need to show `stable` in that diagnostic's macro
1767+ // backtrace.
1768+ //
1769+ // Also, this expansion does not go through the placeholder
1770+ // system and PlaceholderExpander because there is no
1771+ // reliance on the Resolver to look up the name of this
1772+ // attribute. Since we know here it's a built-in attribute,
1773+ // there is no possibility that name resolution would be
1774+ // indeterminate and we'd need to defer the expansion until
1775+ // after some other one.
1776+ let attr = &mut attrs[attr_pos];
1777+ MacroExpander::new(self.cx, self.monotonic).expand_nested_meta(attr);
1778+ self.cx.expanded_inert_attrs.mark(attr);
1779+
1780+ // Now loop back to the top of `take_first_attr` in search
1781+ // of a more interesting attribute to return to the caller.
1782+ control_flow = ControlFlow::Continue(());
1783+ }
1784+ (None, Some(attr_pos)) => {
1785+ let attr = attrs.remove(attr_pos);
1786+ let following_derives = attrs[attr_pos..]
16551787 .iter()
16561788 .filter(|a| a.has_name(sym::derive))
16571789 .flat_map(|a| a.meta_item_list().unwrap_or_default())
@@ -1665,13 +1797,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
16651797 })
16661798 .collect();
16671799
1668- ( attr, pos , following_derives)
1800+ control_flow = ControlFlow::Break(Some(( attr, attr_pos , following_derives)));
16691801 }
1670- _ => return,
1802+ (None, None) => {}
16711803 });
1672- });
16731804
1674- attr
1805+ if let ControlFlow::Break(attr) = control_flow {
1806+ return attr;
1807+ }
1808+ }
16751809 }
16761810
16771811 // Detect use of feature-gated or invalid attributes on macro invocations
0 commit comments