@@ -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,43 +397,40 @@ 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) ) ;
410
408
check_emission ( check_lhs_no_empty_seq ( sess, slice:: from_ref ( & lhs_tt) ) ) ;
411
409
if let Err ( e) = p. expect ( exp ! ( FatArrow ) ) {
412
410
return dummy_syn_ext ( e. emit ( ) ) ;
413
411
}
412
+ if p. token == token:: Eof {
413
+ let err_sp = p. token . span . shrink_to_hi ( ) ;
414
+ let guar = sess
415
+ . dcx ( )
416
+ . struct_span_err ( err_sp, "macro definition ended unexpectedly" )
417
+ . with_span_label ( err_sp, "expected right-hand side of macro rule" )
418
+ . emit ( ) ;
419
+ return dummy_syn_ext ( guar) ;
420
+ }
414
421
let rhs_tt = p. parse_token_tree ( ) ;
415
- let rhs_tt = mbe:: quoted:: parse (
416
- & TokenStream :: new ( vec ! [ rhs_tt] ) ,
417
- false , // RHS
418
- sess,
419
- node_id,
420
- features,
421
- edition,
422
- )
423
- . pop ( )
424
- . unwrap ( ) ;
422
+ let rhs_tt = parse_one_tt ( rhs_tt, RulePart :: Body , sess, node_id, features, edition) ;
425
423
check_emission ( check_rhs ( sess, & rhs_tt) ) ;
426
424
check_emission ( macro_check:: check_meta_variables ( & sess. psess , node_id, & lhs_tt, & rhs_tt) ) ;
427
- lhses. push ( lhs_tt) ;
428
- 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 } ) ;
429
434
if p. token == token:: Eof {
430
435
break ;
431
436
}
@@ -434,7 +439,7 @@ pub fn compile_declarative_macro(
434
439
}
435
440
}
436
441
437
- if lhses . is_empty ( ) {
442
+ if rules . is_empty ( ) {
438
443
let guar = sess. dcx ( ) . span_err ( span, "macros must contain at least one rule" ) ;
439
444
return dummy_syn_ext ( guar) ;
440
445
}
@@ -448,48 +453,12 @@ pub fn compile_declarative_macro(
448
453
return dummy_syn_ext ( guar) ;
449
454
}
450
455
451
- // Compute the spans of the macro rules for unused rule linting.
452
- // Also, we are only interested in non-foreign macros.
453
- let rule_spans = if node_id != DUMMY_NODE_ID {
454
- lhses
455
- . iter ( )
456
- . zip ( rhses. iter ( ) )
457
- . enumerate ( )
458
- // If the rhs contains an invocation like compile_error!,
459
- // don't consider the rule for the unused rule lint.
460
- . filter ( |( _idx, ( _lhs, rhs) ) | !has_compile_error_macro ( rhs) )
461
- // We only take the span of the lhs here,
462
- // so that the spans of created warnings are smaller.
463
- . map ( |( idx, ( lhs, _rhs) ) | ( idx, lhs. span ( ) ) )
464
- . collect :: < Vec < _ > > ( )
465
- } else {
466
- Vec :: new ( )
467
- } ;
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 } ;
468
458
469
- // Convert the lhses into `MatcherLoc` form, which is better for doing the
470
- // actual matching.
471
- let lhses = lhses
472
- . iter ( )
473
- . map ( |lhs| {
474
- // Ignore the delimiters around the matcher.
475
- match lhs {
476
- mbe:: TokenTree :: Delimited ( .., delimited) => {
477
- mbe:: macro_parser:: compute_locs ( & delimited. tts )
478
- }
479
- _ => sess. dcx ( ) . span_bug ( span, "malformed macro lhs" ) ,
480
- }
481
- } )
482
- . collect ( ) ;
483
-
484
- let expander = Arc :: new ( MacroRulesMacroExpander {
485
- name : ident,
486
- span,
487
- node_id,
488
- transparency,
489
- lhses,
490
- rhses,
491
- } ) ;
492
- ( 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)
493
462
}
494
463
495
464
fn check_lhs_nt_follows (
0 commit comments