11use rustc_ast:: tokenstream:: TokenStream ;
2+ use rustc_ast:: { Expr , ast, token} ;
3+ use rustc_ast_pretty:: pprust;
24use rustc_attr_parsing as attr;
35use rustc_attr_parsing:: {
46 CfgSelectBranches , CfgSelectPredicate , EvalConfigResult , parse_cfg_select,
57} ;
6- use rustc_expand:: base:: { DummyResult , ExpandResult , ExtCtxt , MacroExpanderResult } ;
7- use rustc_span:: { Ident , Span , sym} ;
8+ use rustc_expand:: base:: { DummyResult , ExpandResult , ExtCtxt , MacResult , MacroExpanderResult } ;
9+ use rustc_parse:: parser:: ForceCollect ;
10+ use rustc_span:: Span ;
11+ use smallvec:: SmallVec ;
812
13+ use crate :: errors;
914use crate :: errors:: { CfgSelectNoMatches , CfgSelectUnreachable } ;
1015
11- /// Selects the first arm whose predicate evaluates to true.
12- fn select_arm ( ecx : & ExtCtxt < ' _ > , branches : CfgSelectBranches ) -> Option < ( TokenStream , Span ) > {
13- for ( cfg, tt, arm_span) in branches. reachable {
14- if let EvalConfigResult :: True = attr:: eval_config_entry ( & ecx. sess , & cfg) {
15- return Some ( ( tt, arm_span) ) ;
16+ /// This intermediate structure is used to emit parse errors for the branches that are not chosen.
17+ /// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only
18+ /// keeps the parse result for the selected branch.
19+ struct CfgSelectResult < ' cx , ' sess > {
20+ ecx : & ' cx mut ExtCtxt < ' sess > ,
21+ selected_tts : TokenStream ,
22+ other_branches : CfgSelectBranches ,
23+ }
24+
25+ impl < ' cx , ' sess > MacResult for CfgSelectResult < ' cx , ' sess > {
26+ fn make_expr ( self : Box < Self > ) -> Option < Box < Expr > > {
27+ for tts in self . other_branches . into_iter_tts ( ) {
28+ let mut p = self . ecx . new_parser_from_tts ( tts) ;
29+ if let Err ( diag) = p. parse_expr ( ) {
30+ diag. emit ( ) ;
31+ return None ;
32+ }
33+ }
34+
35+ let mut p = self . ecx . new_parser_from_tts ( self . selected_tts ) ;
36+ p. parse_expr ( ) . ok ( )
37+ }
38+
39+ fn make_items ( self : Box < Self > ) -> Option < SmallVec < [ Box < ast:: Item > ; 1 ] > > {
40+ for tts in self . other_branches . into_iter_tts ( ) {
41+ let _ = make_items ( self . ecx , tts, false ) ;
1642 }
43+
44+ make_items ( self . ecx , self . selected_tts , true )
1745 }
46+ }
1847
19- branches. wildcard . map ( |( _, tt, span) | ( tt, span) )
48+ fn make_items < ' cx > (
49+ ecx : & ' cx mut ExtCtxt < ' _ > ,
50+ tts : TokenStream ,
51+ keep : bool ,
52+ ) -> Option < SmallVec < [ Box < ast:: Item > ; 1 ] > > {
53+ let mut p = ecx. new_parser_from_tts ( tts) ;
54+ let mut ret = SmallVec :: new ( ) ;
55+ loop {
56+ match p. parse_item ( ForceCollect :: No ) {
57+ Err ( err) => {
58+ err. emit ( ) ;
59+ break ;
60+ }
61+ Ok ( Some ( item) ) => {
62+ if keep {
63+ ret. push ( item)
64+ }
65+ }
66+ Ok ( None ) => {
67+ if p. token != token:: Eof {
68+ p. dcx ( ) . emit_err ( errors:: ExpectedItem {
69+ span : p. token . span ,
70+ token : & pprust:: token_to_string ( & p. token ) ,
71+ } ) ;
72+ }
73+
74+ break ;
75+ }
76+ }
77+ }
78+ Some ( ret)
2079}
2180
2281pub ( super ) fn expand_cfg_select < ' cx > (
@@ -31,7 +90,7 @@ pub(super) fn expand_cfg_select<'cx>(
3190 Some ( ecx. ecfg . features ) ,
3291 ecx. current_expansion . lint_node_id ,
3392 ) {
34- Ok ( branches) => {
93+ Ok ( mut branches) => {
3594 if let Some ( ( underscore, _, _) ) = branches. wildcard {
3695 // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
3796 for ( predicate, _, _) in & branches. unreachable {
@@ -44,14 +103,11 @@ pub(super) fn expand_cfg_select<'cx>(
44103 }
45104 }
46105
47- if let Some ( ( tts, arm_span) ) = select_arm ( ecx, branches) {
48- return ExpandResult :: from_tts (
49- ecx,
50- tts,
51- sp,
52- arm_span,
53- Ident :: with_dummy_span ( sym:: cfg_select) ,
54- ) ;
106+ if let Some ( selected_tts) = branches. pop_first_match ( |cfg| {
107+ matches ! ( attr:: eval_config_entry( & ecx. sess, cfg) , EvalConfigResult :: True )
108+ } ) {
109+ let mac = CfgSelectResult { ecx, selected_tts, other_branches : branches } ;
110+ return ExpandResult :: Ready ( Box :: new ( mac) ) ;
55111 } else {
56112 // Emit a compiler error when none of the predicates matched.
57113 let guar = ecx. dcx ( ) . emit_err ( CfgSelectNoMatches { span : sp } ) ;
0 commit comments