@@ -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,95 @@ 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
826+ . dcx ( )
827+ . emit_err ( UnsupportedExprInKeyValue { span : expr_span } ) ;
828+ Lit :: new ( LitKind :: Err , kw:: Empty , None )
829+ }
830+ }
831+ _ => {
832+ modified = true ;
833+ self . cx . dcx ( ) . emit_err ( UnsupportedExprInKeyValue { span : expr_span } ) ;
834+ Lit :: new ( LitKind :: Err , kw:: Empty , None )
835+ }
836+ } ;
837+ let token = Token :: new ( TokenKind :: Literal ( lit) , expr_span) ;
838+ new_tokens. push ( TokenTree :: Token ( token, Spacing :: Alone ) ) ;
839+ }
840+ Err ( err) => {
841+ err. cancel ( ) ;
842+ return ;
843+ }
844+ } ;
845+
846+ // Comma separators, and optional trailing comma.
847+ if parser. token == token:: Eof {
848+ break ;
849+ } else if parser. token == token:: Comma {
850+ new_tokens. push ( TokenTree :: Token ( parser. token . clone ( ) , parser. token_spacing ) ) ;
851+ parser. bump ( ) ;
852+ } else {
853+ return ;
854+ }
855+ }
856+
857+ if modified {
858+ delim_args. tokens = TokenStream :: new ( new_tokens) ;
859+ normal_attr. tokens = None ;
860+ normal_attr. item . tokens = None ;
861+ }
862+ }
863+
775864 fn gate_proc_macro_attr_item ( & self , span : Span , item : & Annotatable ) {
776865 let kind = match item {
777866 Annotatable :: Item ( _)
@@ -1628,33 +1717,78 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
16281717 /// its position and derives following it. We have to collect the derives in order to resolve
16291718 /// legacy derive helpers (helpers written before derives that introduce them).
16301719 fn take_first_attr (
1631- & self ,
1720+ & mut self ,
16321721 item : & mut impl HasAttrs ,
16331722 ) -> Option < ( ast:: Attribute , usize , Vec < ast:: Path > ) > {
1634- let mut attr = None ;
1635-
1636- let mut cfg_pos = None ;
1637- let mut attr_pos = None ;
1638- for ( pos, attr) in item. attrs ( ) . iter ( ) . enumerate ( ) {
1639- if !attr. is_doc_comment ( ) && !self . cx . expanded_inert_attrs . is_marked ( attr) {
1723+ loop {
1724+ let mut cfg_pos = None ;
1725+ let mut attr_pos = None ;
1726+ let mut attr_is_builtin = false ;
1727+ for ( pos, attr) in item. attrs ( ) . iter ( ) . enumerate ( ) {
1728+ if attr. is_doc_comment ( ) || self . cx . expanded_inert_attrs . is_marked ( attr) {
1729+ continue ;
1730+ }
16401731 let name = attr. ident ( ) . map ( |ident| ident. name ) ;
16411732 if name == Some ( sym:: cfg) || name == Some ( sym:: cfg_attr) {
16421733 cfg_pos = Some ( pos) ; // a cfg attr found, no need to search anymore
16431734 break ;
16441735 } else if attr_pos. is_none ( )
1645- && !name. is_some_and ( rustc_feature:: is_builtin_attr_name)
1736+ && match name {
1737+ // User-defined attribute invoked using a single identifier.
1738+ Some ( name) if !rustc_feature:: is_builtin_attr_name ( name) => true ,
1739+ // A subset of builtin attributes, like `stable`, which expand
1740+ // nested macro calls within the attribute arguments.
1741+ Some ( name) if rustc_feature:: expand_nested_meta ( name) => {
1742+ attr_is_builtin = true ;
1743+ true
1744+ }
1745+ // Built-in inert attribute.
1746+ Some ( _) => false ,
1747+ // Attribute path longer than one identifier. These are
1748+ // user-defined attribute macros or tool attributes.
1749+ None => true ,
1750+ }
16461751 {
16471752 attr_pos = Some ( pos) ; // a non-cfg attr found, still may find a cfg attr
16481753 }
16491754 }
1650- }
16511755
1652- item. visit_attrs ( |attrs| {
1653- attr = Some ( match ( cfg_pos, attr_pos) {
1654- ( Some ( pos) , _) => ( attrs. remove ( pos) , pos, Vec :: new ( ) ) ,
1655- ( _, Some ( pos) ) => {
1656- let attr = attrs. remove ( pos) ;
1657- let following_derives = attrs[ pos..]
1756+ let mut control_flow = ControlFlow :: Break ( None ) ;
1757+ item. visit_attrs ( |attrs| match ( cfg_pos, attr_pos) {
1758+ ( Some ( cfg_pos) , _) => {
1759+ let cfg = attrs. remove ( cfg_pos) ;
1760+ let following_derives = Vec :: new ( ) ;
1761+ control_flow = ControlFlow :: Break ( Some ( ( cfg, cfg_pos, following_derives) ) ) ;
1762+ }
1763+ ( None , Some ( attr_pos) ) if attr_is_builtin => {
1764+ // A built-in attribute such as #[stable(feature = f!($x))].
1765+ // Eagerly expand its arguments here and now.
1766+ //
1767+ // This does not get a LocalExpnId because nothing else in
1768+ // `item` is affected by this expansion, unlike attribute
1769+ // macros which replace `item` with their own output. If a
1770+ // subsequent expansion within `item` fails, there is no
1771+ // need to show `stable` in that diagnostic's macro
1772+ // backtrace.
1773+ //
1774+ // Also, this expansion does not go through the placeholder
1775+ // system and PlaceholderExpander because there is no
1776+ // reliance on the Resolver to look up the name of this
1777+ // attribute. Since we know here it's a built-in attribute,
1778+ // there is no possibility that name resolution would be
1779+ // indeterminate and we'd need to defer the expansion until
1780+ // after some other one.
1781+ let attr = & mut attrs[ attr_pos] ;
1782+ MacroExpander :: new ( self . cx , self . monotonic ) . expand_nested_meta ( attr) ;
1783+ self . cx . expanded_inert_attrs . mark ( attr) ;
1784+
1785+ // Now loop back to the top of `take_first_attr` in search
1786+ // of a more interesting attribute to return to the caller.
1787+ control_flow = ControlFlow :: Continue ( ( ) ) ;
1788+ }
1789+ ( None , Some ( attr_pos) ) => {
1790+ let attr = attrs. remove ( attr_pos) ;
1791+ let following_derives = attrs[ attr_pos..]
16581792 . iter ( )
16591793 . filter ( |a| a. has_name ( sym:: derive) )
16601794 . flat_map ( |a| a. meta_item_list ( ) . unwrap_or_default ( ) )
@@ -1668,13 +1802,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
16681802 } )
16691803 . collect ( ) ;
16701804
1671- ( attr, pos , following_derives)
1805+ control_flow = ControlFlow :: Break ( Some ( ( attr, attr_pos , following_derives) ) ) ;
16721806 }
1673- _ => return ,
1807+ ( None , None ) => { }
16741808 } ) ;
1675- } ) ;
16761809
1677- attr
1810+ if let ControlFlow :: Break ( attr) = control_flow {
1811+ return attr;
1812+ }
1813+ }
16781814 }
16791815
16801816 // Detect use of feature-gated or invalid attributes on macro invocations
0 commit comments