@@ -2,7 +2,7 @@ use crate::base::*;
2
2
use crate :: config:: StripUnconfigured ;
3
3
use crate :: errors:: {
4
4
IncompleteParse , RecursionLimitReached , RemoveExprNotSupported , RemoveNodeNotSupported ,
5
- UnsupportedKeyValue , WrongFragmentKind ,
5
+ UnsupportedExprInKeyValue , UnsupportedKeyValue , WrongFragmentKind ,
6
6
} ;
7
7
use crate :: hygiene:: SyntaxContext ;
8
8
use crate :: mbe:: diagnostics:: annotate_err_with_kind;
@@ -12,11 +12,11 @@ use crate::placeholders::{placeholder, PlaceholderExpander};
12
12
use rustc_ast as ast;
13
13
use rustc_ast:: mut_visit:: * ;
14
14
use 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 } ;
17
17
use 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 } ;
20
20
use rustc_ast:: { Inline , ItemKind , MacStmtStyle , MetaItemKind , ModKind } ;
21
21
use rustc_ast:: { NestedMetaItem , NodeId , PatKind , StmtKind , TyKind } ;
22
22
use rustc_ast_pretty:: pprust;
@@ -32,11 +32,11 @@ use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
32
32
use rustc_session:: lint:: BuiltinLintDiagnostics ;
33
33
use rustc_session:: parse:: { feature_err, ParseSess } ;
34
34
use rustc_session:: Limit ;
35
- use rustc_span:: symbol:: { sym, Ident } ;
35
+ use rustc_span:: symbol:: { kw , sym, Ident } ;
36
36
use rustc_span:: { FileName , LocalExpnId , Span } ;
37
37
38
38
use smallvec:: SmallVec ;
39
- use std:: ops:: Deref ;
39
+ use std:: ops:: { ControlFlow , Deref } ;
40
40
use std:: path:: PathBuf ;
41
41
use std:: rc:: Rc ;
42
42
use std:: { iter, mem} ;
@@ -772,6 +772,93 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
772
772
} )
773
773
}
774
774
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
+
775
862
fn gate_proc_macro_attr_item ( & self , span : Span , item : & Annotatable ) {
776
863
let kind = match item {
777
864
Annotatable :: Item ( _)
@@ -1625,33 +1712,78 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
1625
1712
/// its position and derives following it. We have to collect the derives in order to resolve
1626
1713
/// legacy derive helpers (helpers written before derives that introduce them).
1627
1714
fn take_first_attr (
1628
- & self ,
1715
+ & mut self ,
1629
1716
item : & mut impl HasAttrs ,
1630
1717
) -> 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
+ }
1637
1726
let name = attr. ident ( ) . map ( |ident| ident. name ) ;
1638
1727
if name == Some ( sym:: cfg) || name == Some ( sym:: cfg_attr) {
1639
1728
cfg_pos = Some ( pos) ; // a cfg attr found, no need to search anymore
1640
1729
break ;
1641
1730
} 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
+ }
1643
1746
{
1644
1747
attr_pos = Some ( pos) ; // a non-cfg attr found, still may find a cfg attr
1645
1748
}
1646
1749
}
1647
- }
1648
1750
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..]
1655
1787
. iter ( )
1656
1788
. filter ( |a| a. has_name ( sym:: derive) )
1657
1789
. flat_map ( |a| a. meta_item_list ( ) . unwrap_or_default ( ) )
@@ -1665,13 +1797,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
1665
1797
} )
1666
1798
. collect ( ) ;
1667
1799
1668
- ( attr, pos , following_derives)
1800
+ control_flow = ControlFlow :: Break ( Some ( ( attr, attr_pos , following_derives) ) ) ;
1669
1801
}
1670
- _ => return ,
1802
+ ( None , None ) => { }
1671
1803
} ) ;
1672
- } ) ;
1673
1804
1674
- attr
1805
+ if let ControlFlow :: Break ( attr) = control_flow {
1806
+ return attr;
1807
+ }
1808
+ }
1675
1809
}
1676
1810
1677
1811
// Detect use of feature-gated or invalid attributes on macro invocations
0 commit comments