@@ -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,95 @@ 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
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
+
775
864
fn gate_proc_macro_attr_item ( & self , span : Span , item : & Annotatable ) {
776
865
let kind = match item {
777
866
Annotatable :: Item ( _)
@@ -1628,33 +1717,78 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
1628
1717
/// its position and derives following it. We have to collect the derives in order to resolve
1629
1718
/// legacy derive helpers (helpers written before derives that introduce them).
1630
1719
fn take_first_attr (
1631
- & self ,
1720
+ & mut self ,
1632
1721
item : & mut impl HasAttrs ,
1633
1722
) -> 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
+ }
1640
1731
let name = attr. ident ( ) . map ( |ident| ident. name ) ;
1641
1732
if name == Some ( sym:: cfg) || name == Some ( sym:: cfg_attr) {
1642
1733
cfg_pos = Some ( pos) ; // a cfg attr found, no need to search anymore
1643
1734
break ;
1644
1735
} 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
+ }
1646
1751
{
1647
1752
attr_pos = Some ( pos) ; // a non-cfg attr found, still may find a cfg attr
1648
1753
}
1649
1754
}
1650
- }
1651
1755
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..]
1658
1792
. iter ( )
1659
1793
. filter ( |a| a. has_name ( sym:: derive) )
1660
1794
. flat_map ( |a| a. meta_item_list ( ) . unwrap_or_default ( ) )
@@ -1668,13 +1802,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
1668
1802
} )
1669
1803
. collect ( ) ;
1670
1804
1671
- ( attr, pos , following_derives)
1805
+ control_flow = ControlFlow :: Break ( Some ( ( attr, attr_pos , following_derives) ) ) ;
1672
1806
}
1673
- _ => return ,
1807
+ ( None , None ) => { }
1674
1808
} ) ;
1675
- } ) ;
1676
1809
1677
- attr
1810
+ if let ControlFlow :: Break ( attr) = control_flow {
1811
+ return attr;
1812
+ }
1813
+ }
1678
1814
}
1679
1815
1680
1816
// Detect use of feature-gated or invalid attributes on macro invocations
0 commit comments