@@ -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