@@ -36,6 +36,7 @@ use crate::base::{
36
36
} ;
37
37
use crate :: expand:: { AstFragment , AstFragmentKind , ensure_complete_parse, parse_ast_fragment} ;
38
38
use crate :: mbe:: macro_parser:: { Error , ErrorReported , Failure , MatcherLoc , Success , TtParser } ;
39
+ use crate :: mbe:: quoted:: { RulePart , parse_one_tt} ;
39
40
use crate :: mbe:: transcribe:: transcribe;
40
41
use crate :: mbe:: { self , KleeneOp , macro_check} ;
41
42
@@ -97,13 +98,18 @@ impl<'a> ParserAnyMacro<'a> {
97
98
}
98
99
}
99
100
101
+ pub ( super ) struct MacroRule {
102
+ pub ( super ) lhs : Vec < MatcherLoc > ,
103
+ lhs_span : Span ,
104
+ rhs : mbe:: TokenTree ,
105
+ }
106
+
100
107
struct MacroRulesMacroExpander {
101
108
node_id : NodeId ,
102
109
name : Ident ,
103
110
span : Span ,
104
111
transparency : Transparency ,
105
- lhses : Vec < Vec < MatcherLoc > > ,
106
- rhses : Vec < mbe:: TokenTree > ,
112
+ rules : Vec < MacroRule > ,
107
113
}
108
114
109
115
impl TTMacroExpander for MacroRulesMacroExpander {
@@ -121,10 +127,15 @@ impl TTMacroExpander for MacroRulesMacroExpander {
121
127
self . name ,
122
128
self . transparency ,
123
129
input,
124
- & self . lhses ,
125
- & self . rhses ,
130
+ & self . rules ,
126
131
) )
127
132
}
133
+
134
+ fn get_unused_rule ( & self , rule_i : usize ) -> Option < ( & Ident , Span ) > {
135
+ // If the rhs contains an invocation like `compile_error!`, don't report it as unused.
136
+ let rule = & self . rules [ rule_i] ;
137
+ if has_compile_error_macro ( & rule. rhs ) { None } else { Some ( ( & self . name , rule. lhs_span ) ) }
138
+ }
128
139
}
129
140
130
141
struct DummyExpander ( ErrorGuaranteed ) ;
@@ -183,9 +194,8 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
183
194
}
184
195
}
185
196
186
- /// Expands the rules based macro defined by `lhses` and `rhses` for a given
187
- /// input `arg`.
188
- #[ instrument( skip( cx, transparency, arg, lhses, rhses) ) ]
197
+ /// Expands the rules based macro defined by `rules` for a given input `arg`.
198
+ #[ instrument( skip( cx, transparency, arg, rules) ) ]
189
199
fn expand_macro < ' cx > (
190
200
cx : & ' cx mut ExtCtxt < ' _ > ,
191
201
sp : Span ,
@@ -194,8 +204,7 @@ fn expand_macro<'cx>(
194
204
name : Ident ,
195
205
transparency : Transparency ,
196
206
arg : TokenStream ,
197
- lhses : & [ Vec < MatcherLoc > ] ,
198
- rhses : & [ mbe:: TokenTree ] ,
207
+ rules : & [ MacroRule ] ,
199
208
) -> Box < dyn MacResult + ' cx > {
200
209
let psess = & cx. sess . psess ;
201
210
// Macros defined in the current crate have a real node id,
@@ -208,15 +217,14 @@ fn expand_macro<'cx>(
208
217
}
209
218
210
219
// Track nothing for the best performance.
211
- let try_success_result = try_match_macro ( psess, name, & arg, lhses , & mut NoopTracker ) ;
220
+ let try_success_result = try_match_macro ( psess, name, & arg, rules , & mut NoopTracker ) ;
212
221
213
222
match try_success_result {
214
- Ok ( ( i, named_matches) ) => {
215
- let ( rhs, rhs_span) : ( & mbe:: Delimited , DelimSpan ) = match & rhses[ i] {
216
- mbe:: TokenTree :: Delimited ( span, _, delimited) => ( & delimited, * span) ,
217
- _ => cx. dcx ( ) . span_bug ( sp, "malformed macro rhs" ) ,
223
+ Ok ( ( i, rule, named_matches) ) => {
224
+ let mbe:: TokenTree :: Delimited ( rhs_span, _, ref rhs) = rule. rhs else {
225
+ cx. dcx ( ) . span_bug ( sp, "malformed macro rhs" ) ;
218
226
} ;
219
- let arm_span = rhses [ i ] . span ( ) ;
227
+ let arm_span = rule . rhs . span ( ) ;
220
228
221
229
// rhs has holes ( `$id` and `$(...)` that need filled)
222
230
let id = cx. current_expansion . id ;
@@ -262,7 +270,7 @@ fn expand_macro<'cx>(
262
270
Err ( CanRetry :: Yes ) => {
263
271
// Retry and emit a better error.
264
272
let ( span, guar) =
265
- diagnostics:: failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, arg, lhses ) ;
273
+ diagnostics:: failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, arg, rules ) ;
266
274
cx. trace_macros_diag ( ) ;
267
275
DummyResult :: any ( span, guar)
268
276
}
@@ -278,14 +286,14 @@ pub(super) enum CanRetry {
278
286
/// Try expanding the macro. Returns the index of the successful arm and its named_matches if it was successful,
279
287
/// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors
280
288
/// correctly.
281
- #[ instrument( level = "debug" , skip( psess, arg, lhses , track) , fields( tracking = %T :: description( ) ) ) ]
289
+ #[ instrument( level = "debug" , skip( psess, arg, rules , track) , fields( tracking = %T :: description( ) ) ) ]
282
290
pub ( super ) fn try_match_macro < ' matcher , T : Tracker < ' matcher > > (
283
291
psess : & ParseSess ,
284
292
name : Ident ,
285
293
arg : & TokenStream ,
286
- lhses : & ' matcher [ Vec < MatcherLoc > ] ,
294
+ rules : & ' matcher [ MacroRule ] ,
287
295
track : & mut T ,
288
- ) -> Result < ( usize , NamedMatches ) , CanRetry > {
296
+ ) -> Result < ( usize , & ' matcher MacroRule , NamedMatches ) , CanRetry > {
289
297
// We create a base parser that can be used for the "black box" parts.
290
298
// Every iteration needs a fresh copy of that parser. However, the parser
291
299
// is not mutated on many of the iterations, particularly when dealing with
@@ -308,7 +316,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
308
316
let parser = parser_from_cx ( psess, arg. clone ( ) , T :: recovery ( ) ) ;
309
317
// Try each arm's matchers.
310
318
let mut tt_parser = TtParser :: new ( name) ;
311
- for ( i, lhs ) in lhses . iter ( ) . enumerate ( ) {
319
+ for ( i, rule ) in rules . iter ( ) . enumerate ( ) {
312
320
let _tracing_span = trace_span ! ( "Matching arm" , %i) ;
313
321
314
322
// Take a snapshot of the state of pre-expansion gating at this point.
@@ -317,7 +325,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
317
325
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
318
326
let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
319
327
320
- let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, track) ;
328
+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & rule . lhs , track) ;
321
329
322
330
track. after_arm ( & result) ;
323
331
@@ -328,7 +336,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
328
336
// Merge the gated spans from parsing the matcher with the preexisting ones.
329
337
psess. gated_spans . merge ( gated_spans_snapshot) ;
330
338
331
- return Ok ( ( i, named_matches) ) ;
339
+ return Ok ( ( i, rule , named_matches) ) ;
332
340
}
333
341
Failure ( _) => {
334
342
trace ! ( "Failed to match arm, trying the next one" ) ;
@@ -364,7 +372,7 @@ pub fn compile_declarative_macro(
364
372
span : Span ,
365
373
node_id : NodeId ,
366
374
edition : Edition ,
367
- ) -> ( SyntaxExtension , Vec < ( usize , Span ) > ) {
375
+ ) -> ( SyntaxExtension , usize ) {
368
376
let mk_syn_ext = |expander| {
369
377
SyntaxExtension :: new (
370
378
sess,
@@ -377,7 +385,7 @@ pub fn compile_declarative_macro(
377
385
node_id != DUMMY_NODE_ID ,
378
386
)
379
387
} ;
380
- let dummy_syn_ext = |guar| ( mk_syn_ext ( Arc :: new ( DummyExpander ( guar) ) ) , Vec :: new ( ) ) ;
388
+ let dummy_syn_ext = |guar| ( mk_syn_ext ( Arc :: new ( DummyExpander ( guar) ) ) , 0 ) ;
381
389
382
390
let macro_rules = macro_def. macro_rules ;
383
391
let exp_sep = if macro_rules { exp ! ( Semi ) } else { exp ! ( Comma ) } ;
@@ -389,21 +397,11 @@ pub fn compile_declarative_macro(
389
397
let mut guar = None ;
390
398
let mut check_emission = |ret : Result < ( ) , ErrorGuaranteed > | guar = guar. or ( ret. err ( ) ) ;
391
399
392
- let mut lhses = Vec :: new ( ) ;
393
- let mut rhses = Vec :: new ( ) ;
400
+ let mut rules = Vec :: new ( ) ;
394
401
395
402
while p. token != token:: Eof {
396
403
let lhs_tt = p. parse_token_tree ( ) ;
397
- let lhs_tt = mbe:: quoted:: parse (
398
- & TokenStream :: new ( vec ! [ lhs_tt] ) ,
399
- true , // LHS
400
- sess,
401
- node_id,
402
- features,
403
- edition,
404
- )
405
- . pop ( )
406
- . unwrap ( ) ;
404
+ let lhs_tt = parse_one_tt ( lhs_tt, RulePart :: Pattern , sess, node_id, features, edition) ;
407
405
// We don't handle errors here, the driver will abort after parsing/expansion. We can
408
406
// report every error in every macro this way.
409
407
check_emission ( check_lhs_nt_follows ( sess, node_id, & lhs_tt) ) ;
@@ -421,20 +419,18 @@ pub fn compile_declarative_macro(
421
419
return dummy_syn_ext ( guar) ;
422
420
}
423
421
let rhs_tt = p. parse_token_tree ( ) ;
424
- let rhs_tt = mbe:: quoted:: parse (
425
- & TokenStream :: new ( vec ! [ rhs_tt] ) ,
426
- false , // RHS
427
- sess,
428
- node_id,
429
- features,
430
- edition,
431
- )
432
- . pop ( )
433
- . unwrap ( ) ;
422
+ let rhs_tt = parse_one_tt ( rhs_tt, RulePart :: Body , sess, node_id, features, edition) ;
434
423
check_emission ( check_rhs ( sess, & rhs_tt) ) ;
435
424
check_emission ( macro_check:: check_meta_variables ( & sess. psess , node_id, & lhs_tt, & rhs_tt) ) ;
436
- lhses. push ( lhs_tt) ;
437
- rhses. push ( rhs_tt) ;
425
+ let lhs_span = lhs_tt. span ( ) ;
426
+ // Convert the lhs into `MatcherLoc` form, which is better for doing the
427
+ // actual matching.
428
+ let lhs = if let mbe:: TokenTree :: Delimited ( .., delimited) = lhs_tt {
429
+ mbe:: macro_parser:: compute_locs ( & delimited. tts )
430
+ } else {
431
+ return dummy_syn_ext ( guar. unwrap ( ) ) ;
432
+ } ;
433
+ rules. push ( MacroRule { lhs, lhs_span, rhs : rhs_tt } ) ;
438
434
if p. token == token:: Eof {
439
435
break ;
440
436
}
@@ -443,7 +439,7 @@ pub fn compile_declarative_macro(
443
439
}
444
440
}
445
441
446
- if lhses . is_empty ( ) {
442
+ if rules . is_empty ( ) {
447
443
let guar = sess. dcx ( ) . span_err ( span, "macros must contain at least one rule" ) ;
448
444
return dummy_syn_ext ( guar) ;
449
445
}
@@ -457,48 +453,12 @@ pub fn compile_declarative_macro(
457
453
return dummy_syn_ext ( guar) ;
458
454
}
459
455
460
- // Compute the spans of the macro rules for unused rule linting.
461
- // Also, we are only interested in non-foreign macros.
462
- let rule_spans = if node_id != DUMMY_NODE_ID {
463
- lhses
464
- . iter ( )
465
- . zip ( rhses. iter ( ) )
466
- . enumerate ( )
467
- // If the rhs contains an invocation like compile_error!,
468
- // don't consider the rule for the unused rule lint.
469
- . filter ( |( _idx, ( _lhs, rhs) ) | !has_compile_error_macro ( rhs) )
470
- // We only take the span of the lhs here,
471
- // so that the spans of created warnings are smaller.
472
- . map ( |( idx, ( lhs, _rhs) ) | ( idx, lhs. span ( ) ) )
473
- . collect :: < Vec < _ > > ( )
474
- } else {
475
- Vec :: new ( )
476
- } ;
456
+ // Return the number of rules for unused rule linting, if this is a local macro.
457
+ let nrules = if node_id != DUMMY_NODE_ID { rules. len ( ) } else { 0 } ;
477
458
478
- // Convert the lhses into `MatcherLoc` form, which is better for doing the
479
- // actual matching.
480
- let lhses = lhses
481
- . iter ( )
482
- . map ( |lhs| {
483
- // Ignore the delimiters around the matcher.
484
- match lhs {
485
- mbe:: TokenTree :: Delimited ( .., delimited) => {
486
- mbe:: macro_parser:: compute_locs ( & delimited. tts )
487
- }
488
- _ => sess. dcx ( ) . span_bug ( span, "malformed macro lhs" ) ,
489
- }
490
- } )
491
- . collect ( ) ;
492
-
493
- let expander = Arc :: new ( MacroRulesMacroExpander {
494
- name : ident,
495
- span,
496
- node_id,
497
- transparency,
498
- lhses,
499
- rhses,
500
- } ) ;
501
- ( mk_syn_ext ( expander) , rule_spans)
459
+ let expander =
460
+ Arc :: new ( MacroRulesMacroExpander { name : ident, span, node_id, transparency, rules } ) ;
461
+ ( mk_syn_ext ( expander) , nrules)
502
462
}
503
463
504
464
fn check_lhs_nt_follows (
0 commit comments