@@ -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;
34
34
use rustc_session:: { Limit , Session } ;
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 ( _)
@@ -1627,33 +1716,78 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
1627
1716
/// its position and derives following it. We have to collect the derives in order to resolve
1628
1717
/// legacy derive helpers (helpers written before derives that introduce them).
1629
1718
fn take_first_attr (
1630
- & self ,
1719
+ & mut self ,
1631
1720
item : & mut impl HasAttrs ,
1632
1721
) -> Option < ( ast:: Attribute , usize , Vec < ast:: Path > ) > {
1633
- let mut attr = None ;
1634
-
1635
- let mut cfg_pos = None ;
1636
- let mut attr_pos = None ;
1637
- for ( pos, attr) in item. attrs ( ) . iter ( ) . enumerate ( ) {
1638
- if !attr. is_doc_comment ( ) && !self . cx . expanded_inert_attrs . is_marked ( attr) {
1722
+ loop {
1723
+ let mut cfg_pos = None ;
1724
+ let mut attr_pos = None ;
1725
+ let mut attr_is_builtin = false ;
1726
+ for ( pos, attr) in item. attrs ( ) . iter ( ) . enumerate ( ) {
1727
+ if attr. is_doc_comment ( ) || self . cx . expanded_inert_attrs . is_marked ( attr) {
1728
+ continue ;
1729
+ }
1639
1730
let name = attr. ident ( ) . map ( |ident| ident. name ) ;
1640
1731
if name == Some ( sym:: cfg) || name == Some ( sym:: cfg_attr) {
1641
1732
cfg_pos = Some ( pos) ; // a cfg attr found, no need to search anymore
1642
1733
break ;
1643
1734
} else if attr_pos. is_none ( )
1644
- && !name. is_some_and ( rustc_feature:: is_builtin_attr_name)
1735
+ && match name {
1736
+ // User-defined attribute invoked using a single identifier.
1737
+ Some ( name) if !rustc_feature:: is_builtin_attr_name ( name) => true ,
1738
+ // A subset of builtin attributes, like `stable`, which expand
1739
+ // nested macro calls within the attribute arguments.
1740
+ Some ( name) if rustc_feature:: expand_nested_meta ( name) => {
1741
+ attr_is_builtin = true ;
1742
+ true
1743
+ }
1744
+ // Built-in inert attribute.
1745
+ Some ( _) => false ,
1746
+ // Attribute path longer than one identifier. These are
1747
+ // user-defined attribute macros or tool attributes.
1748
+ None => true ,
1749
+ }
1645
1750
{
1646
1751
attr_pos = Some ( pos) ; // a non-cfg attr found, still may find a cfg attr
1647
1752
}
1648
1753
}
1649
- }
1650
1754
1651
- item. visit_attrs ( |attrs| {
1652
- attr = Some ( match ( cfg_pos, attr_pos) {
1653
- ( Some ( pos) , _) => ( attrs. remove ( pos) , pos, Vec :: new ( ) ) ,
1654
- ( _, Some ( pos) ) => {
1655
- let attr = attrs. remove ( pos) ;
1656
- let following_derives = attrs[ pos..]
1755
+ let mut control_flow = ControlFlow :: Break ( None ) ;
1756
+ item. visit_attrs ( |attrs| match ( cfg_pos, attr_pos) {
1757
+ ( Some ( cfg_pos) , _) => {
1758
+ let cfg = attrs. remove ( cfg_pos) ;
1759
+ let following_derives = Vec :: new ( ) ;
1760
+ control_flow = ControlFlow :: Break ( Some ( ( cfg, cfg_pos, following_derives) ) ) ;
1761
+ }
1762
+ ( None , Some ( attr_pos) ) if attr_is_builtin => {
1763
+ // A built-in attribute such as #[stable(feature = f!($x))].
1764
+ // Eagerly expand its arguments here and now.
1765
+ //
1766
+ // This does not get a LocalExpnId because nothing else in
1767
+ // `item` is affected by this expansion, unlike attribute
1768
+ // macros which replace `item` with their own output. If a
1769
+ // subsequent expansion within `item` fails, there is no
1770
+ // need to show `stable` in that diagnostic's macro
1771
+ // backtrace.
1772
+ //
1773
+ // Also, this expansion does not go through the placeholder
1774
+ // system and PlaceholderExpander because there is no
1775
+ // reliance on the Resolver to look up the name of this
1776
+ // attribute. Since we know here it's a built-in attribute,
1777
+ // there is no possibility that name resolution would be
1778
+ // indeterminate and we'd need to defer the expansion until
1779
+ // after some other one.
1780
+ let attr = & mut attrs[ attr_pos] ;
1781
+ MacroExpander :: new ( self . cx , self . monotonic ) . expand_nested_meta ( attr) ;
1782
+ self . cx . expanded_inert_attrs . mark ( attr) ;
1783
+
1784
+ // Now loop back to the top of `take_first_attr` in search
1785
+ // of a more interesting attribute to return to the caller.
1786
+ control_flow = ControlFlow :: Continue ( ( ) ) ;
1787
+ }
1788
+ ( None , Some ( attr_pos) ) => {
1789
+ let attr = attrs. remove ( attr_pos) ;
1790
+ let following_derives = attrs[ attr_pos..]
1657
1791
. iter ( )
1658
1792
. filter ( |a| a. has_name ( sym:: derive) )
1659
1793
. flat_map ( |a| a. meta_item_list ( ) . unwrap_or_default ( ) )
@@ -1667,13 +1801,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
1667
1801
} )
1668
1802
. collect ( ) ;
1669
1803
1670
- ( attr, pos , following_derives)
1804
+ control_flow = ControlFlow :: Break ( Some ( ( attr, attr_pos , following_derives) ) ) ;
1671
1805
}
1672
- _ => return ,
1806
+ ( None , None ) => { }
1673
1807
} ) ;
1674
- } ) ;
1675
1808
1676
- attr
1809
+ if let ControlFlow :: Break ( attr) = control_flow {
1810
+ return attr;
1811
+ }
1812
+ }
1677
1813
}
1678
1814
1679
1815
// Detect use of feature-gated or invalid attributes on macro invocations
0 commit comments