@@ -36,6 +36,7 @@ use crate::base::{
3636} ;
3737use crate :: expand:: { AstFragment , AstFragmentKind , ensure_complete_parse, parse_ast_fragment} ;
3838use crate :: mbe:: macro_parser:: { Error , ErrorReported , Failure , MatcherLoc , Success , TtParser } ;
39+ use crate :: mbe:: quoted:: { RulePart , parse_one_tt} ;
3940use crate :: mbe:: transcribe:: transcribe;
4041use crate :: mbe:: { self , KleeneOp , macro_check} ;
4142
@@ -97,13 +98,18 @@ impl<'a> ParserAnyMacro<'a> {
9798 }
9899}
99100
101+ pub ( super ) struct MacroRule {
102+ pub ( super ) lhs : Vec < MatcherLoc > ,
103+ lhs_span : Span ,
104+ rhs : mbe:: TokenTree ,
105+ }
106+
100107struct MacroRulesMacroExpander {
101108 node_id : NodeId ,
102109 name : Ident ,
103110 span : Span ,
104111 transparency : Transparency ,
105- lhses : Vec < Vec < MatcherLoc > > ,
106- rhses : Vec < mbe:: TokenTree > ,
112+ rules : Vec < MacroRule > ,
107113}
108114
109115impl TTMacroExpander for MacroRulesMacroExpander {
@@ -121,10 +127,15 @@ impl TTMacroExpander for MacroRulesMacroExpander {
121127 self . name ,
122128 self . transparency ,
123129 input,
124- & self . lhses ,
125- & self . rhses ,
130+ & self . rules ,
126131 ) )
127132 }
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+ }
128139}
129140
130141struct DummyExpander ( ErrorGuaranteed ) ;
@@ -183,9 +194,8 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
183194 }
184195}
185196
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) ) ]
189199fn expand_macro < ' cx > (
190200 cx : & ' cx mut ExtCtxt < ' _ > ,
191201 sp : Span ,
@@ -194,8 +204,7 @@ fn expand_macro<'cx>(
194204 name : Ident ,
195205 transparency : Transparency ,
196206 arg : TokenStream ,
197- lhses : & [ Vec < MatcherLoc > ] ,
198- rhses : & [ mbe:: TokenTree ] ,
207+ rules : & [ MacroRule ] ,
199208) -> Box < dyn MacResult + ' cx > {
200209 let psess = & cx. sess . psess ;
201210 // Macros defined in the current crate have a real node id,
@@ -208,15 +217,14 @@ fn expand_macro<'cx>(
208217 }
209218
210219 // 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 ) ;
212221
213222 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" ) ;
218226 } ;
219- let arm_span = rhses [ i ] . span ( ) ;
227+ let arm_span = rule . rhs . span ( ) ;
220228
221229 // rhs has holes ( `$id` and `$(...)` that need filled)
222230 let id = cx. current_expansion . id ;
@@ -262,7 +270,7 @@ fn expand_macro<'cx>(
262270 Err ( CanRetry :: Yes ) => {
263271 // Retry and emit a better error.
264272 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 ) ;
266274 cx. trace_macros_diag ( ) ;
267275 DummyResult :: any ( span, guar)
268276 }
@@ -278,14 +286,14 @@ pub(super) enum CanRetry {
278286/// Try expanding the macro. Returns the index of the successful arm and its named_matches if it was successful,
279287/// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors
280288/// 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( ) ) ) ]
282290pub ( super ) fn try_match_macro < ' matcher , T : Tracker < ' matcher > > (
283291 psess : & ParseSess ,
284292 name : Ident ,
285293 arg : & TokenStream ,
286- lhses : & ' matcher [ Vec < MatcherLoc > ] ,
294+ rules : & ' matcher [ MacroRule ] ,
287295 track : & mut T ,
288- ) -> Result < ( usize , NamedMatches ) , CanRetry > {
296+ ) -> Result < ( usize , & ' matcher MacroRule , NamedMatches ) , CanRetry > {
289297 // We create a base parser that can be used for the "black box" parts.
290298 // Every iteration needs a fresh copy of that parser. However, the parser
291299 // 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>>(
308316 let parser = parser_from_cx ( psess, arg. clone ( ) , T :: recovery ( ) ) ;
309317 // Try each arm's matchers.
310318 let mut tt_parser = TtParser :: new ( name) ;
311- for ( i, lhs ) in lhses . iter ( ) . enumerate ( ) {
319+ for ( i, rule ) in rules . iter ( ) . enumerate ( ) {
312320 let _tracing_span = trace_span ! ( "Matching arm" , %i) ;
313321
314322 // 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>>(
317325 // are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
318326 let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
319327
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) ;
321329
322330 track. after_arm ( & result) ;
323331
@@ -328,7 +336,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
328336 // Merge the gated spans from parsing the matcher with the preexisting ones.
329337 psess. gated_spans . merge ( gated_spans_snapshot) ;
330338
331- return Ok ( ( i, named_matches) ) ;
339+ return Ok ( ( i, rule , named_matches) ) ;
332340 }
333341 Failure ( _) => {
334342 trace ! ( "Failed to match arm, trying the next one" ) ;
@@ -364,7 +372,7 @@ pub fn compile_declarative_macro(
364372 span : Span ,
365373 node_id : NodeId ,
366374 edition : Edition ,
367- ) -> ( SyntaxExtension , Vec < ( usize , Span ) > ) {
375+ ) -> ( SyntaxExtension , usize ) {
368376 let mk_syn_ext = |expander| {
369377 SyntaxExtension :: new (
370378 sess,
@@ -377,7 +385,7 @@ pub fn compile_declarative_macro(
377385 node_id != DUMMY_NODE_ID ,
378386 )
379387 } ;
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 ) ;
381389
382390 let macro_rules = macro_def. macro_rules ;
383391 let exp_sep = if macro_rules { exp ! ( Semi ) } else { exp ! ( Comma ) } ;
@@ -389,43 +397,40 @@ pub fn compile_declarative_macro(
389397 let mut guar = None ;
390398 let mut check_emission = |ret : Result < ( ) , ErrorGuaranteed > | guar = guar. or ( ret. err ( ) ) ;
391399
392- let mut lhses = Vec :: new ( ) ;
393- let mut rhses = Vec :: new ( ) ;
400+ let mut rules = Vec :: new ( ) ;
394401
395402 while p. token != token:: Eof {
396403 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) ;
407405 // We don't handle errors here, the driver will abort after parsing/expansion. We can
408406 // report every error in every macro this way.
409407 check_emission ( check_lhs_nt_follows ( sess, node_id, & lhs_tt) ) ;
410408 check_emission ( check_lhs_no_empty_seq ( sess, slice:: from_ref ( & lhs_tt) ) ) ;
411409 if let Err ( e) = p. expect ( exp ! ( FatArrow ) ) {
412410 return dummy_syn_ext ( e. emit ( ) ) ;
413411 }
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+ }
414421 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) ;
425423 check_emission ( check_rhs ( sess, & rhs_tt) ) ;
426424 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 } ) ;
429434 if p. token == token:: Eof {
430435 break ;
431436 }
@@ -434,7 +439,7 @@ pub fn compile_declarative_macro(
434439 }
435440 }
436441
437- if lhses . is_empty ( ) {
442+ if rules . is_empty ( ) {
438443 let guar = sess. dcx ( ) . span_err ( span, "macros must contain at least one rule" ) ;
439444 return dummy_syn_ext ( guar) ;
440445 }
@@ -448,48 +453,12 @@ pub fn compile_declarative_macro(
448453 return dummy_syn_ext ( guar) ;
449454 }
450455
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 } ;
468458
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)
493462}
494463
495464fn check_lhs_nt_follows (
0 commit comments