@@ -141,31 +141,40 @@ fn trace_macros_note(cx_expansions: &mut FxIndexMap<Span, Vec<String>>, sp: Span
141141}
142142
143143pub ( super ) trait Tracker < ' matcher > {
144+ /// The contents of `ParseResult::Failure`.
145+ type Failure ;
146+
147+ /// Arm failed to match. If the token is `token::Eof`, it indicates an unexpected
148+ /// end of macro invocation. Otherwise, it indicates that no rules expected the given token.
149+ /// The usize is the approximate position of the token in the input token stream.
150+ fn build_failure ( tok : Token , position : usize , msg : & ' static str ) -> Self :: Failure ;
151+
144152 /// This is called before trying to match next MatcherLoc on the current token.
145- fn before_match_loc ( & mut self , parser : & TtParser , matcher : & ' matcher MatcherLoc ) ;
153+ fn before_match_loc ( & mut self , _parser : & TtParser , _matcher : & ' matcher MatcherLoc ) { }
146154
147155 /// This is called after an arm has been parsed, either successfully or unsuccessfully. When this is called,
148156 /// `before_match_loc` was called at least once (with a `MatcherLoc::Eof`).
149- fn after_arm ( & mut self , result : & NamedParseResult ) ;
157+ fn after_arm ( & mut self , _result : & NamedParseResult < Self :: Failure > ) { }
150158
151159 /// For tracing.
152160 fn description ( ) -> & ' static str ;
153161
154- fn recovery ( ) -> Recovery ;
162+ fn recovery ( ) -> Recovery {
163+ Recovery :: Forbidden
164+ }
155165}
156166
157167/// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to monomorphization.
158168pub ( super ) struct NoopTracker ;
159169
160170impl < ' matcher > Tracker < ' matcher > for NoopTracker {
161- fn before_match_loc ( & mut self , _: & TtParser , _: & ' matcher MatcherLoc ) { }
162- fn after_arm ( & mut self , _: & NamedParseResult ) { }
171+ type Failure = ( ) ;
172+
173+ fn build_failure ( _tok : Token , _position : usize , _msg : & ' static str ) -> Self :: Failure { }
174+
163175 fn description ( ) -> & ' static str {
164176 "none"
165177 }
166- fn recovery ( ) -> Recovery {
167- Recovery :: Forbidden
168- }
169178}
170179
171180/// Expands the rules based macro defined by `lhses` and `rhses` for a given
@@ -326,8 +335,8 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
326335
327336 return Ok ( ( i, named_matches) ) ;
328337 }
329- Failure ( _, reached_position , _ ) => {
330- trace ! ( %reached_position , "Failed to match arm, trying the next one" ) ;
338+ Failure ( _) => {
339+ trace ! ( "Failed to match arm, trying the next one" ) ;
331340 // Try the next arm.
332341 }
333342 Error ( _, _) => {
@@ -381,11 +390,13 @@ pub fn compile_declarative_macro(
381390 let rhs_nm = Ident :: new ( sym:: rhs, def. span ) ;
382391 let tt_spec = Some ( NonterminalKind :: TT ) ;
383392
384- // Parse the macro_rules! invocation
385- let ( macro_rules, body) = match & def. kind {
386- ast:: ItemKind :: MacroDef ( def) => ( def. macro_rules , def. body . tokens . clone ( ) ) ,
393+ let macro_def = match & def. kind {
394+ ast:: ItemKind :: MacroDef ( def) => def,
387395 _ => unreachable ! ( ) ,
388396 } ;
397+ let macro_rules = macro_def. macro_rules ;
398+
399+ // Parse the macro_rules! invocation
389400
390401 // The pattern that macro_rules matches.
391402 // The grammar for macro_rules! is:
@@ -426,13 +437,32 @@ pub fn compile_declarative_macro(
426437 // Convert it into `MatcherLoc` form.
427438 let argument_gram = mbe:: macro_parser:: compute_locs ( & argument_gram) ;
428439
429- let parser = Parser :: new ( & sess. parse_sess , body, true , rustc_parse:: MACRO_ARGUMENTS ) ;
440+ let create_parser = || {
441+ let body = macro_def. body . tokens . clone ( ) ;
442+ Parser :: new ( & sess. parse_sess , body, true , rustc_parse:: MACRO_ARGUMENTS )
443+ } ;
444+
445+ let parser = create_parser ( ) ;
430446 let mut tt_parser =
431447 TtParser :: new ( Ident :: with_dummy_span ( if macro_rules { kw:: MacroRules } else { kw:: Macro } ) ) ;
432448 let argument_map =
433449 match tt_parser. parse_tt ( & mut Cow :: Owned ( parser) , & argument_gram, & mut NoopTracker ) {
434450 Success ( m) => m,
435- Failure ( token, _, msg) => {
451+ Failure ( ( ) ) => {
452+ // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it with another one
453+ // that gives us the information we need.
454+ // For this we need to reclone the macro body as the previous parser consumed it.
455+ let retry_parser = create_parser ( ) ;
456+
457+ let parse_result = tt_parser. parse_tt (
458+ & mut Cow :: Owned ( retry_parser) ,
459+ & argument_gram,
460+ & mut diagnostics:: FailureForwarder ,
461+ ) ;
462+ let Failure ( ( token, _, msg) ) = parse_result else {
463+ unreachable ! ( "matcher returned something other than Failure after retry" ) ;
464+ } ;
465+
436466 let s = parse_failure_msg ( & token) ;
437467 let sp = token. span . substitute_dummy ( def. span ) ;
438468 let mut err = sess. parse_sess . span_diagnostic . struct_span_err ( sp, & s) ;
0 commit comments