@@ -316,16 +316,116 @@ fn expand_macro<'cx>(
316
316
is_local,
317
317
} ) ;
318
318
}
319
- Err ( ( ) ) => {
320
- todo ! ( "Retry macro invocation while tracking diagnostics info and emit error" ) ;
321
-
319
+ Err ( CanRetry :: No ( _) ) => {
320
+ debug ! ( "Will not retry matching as an error was emitted already" ) ;
322
321
return DummyResult :: any ( sp) ;
323
322
}
323
+ Err ( CanRetry :: Yes ) => {
324
+ // Retry and emit a better error below.
325
+ }
326
+ }
327
+
328
+ // An error occured, try the expansion again, tracking the expansion closely for better diagnostics
329
+ let mut tracker = CollectTrackerAndEmitter :: new ( cx, sp) ;
330
+
331
+ let try_success_result = try_match_macro ( sess, name, & arg, lhses, & mut tracker) ;
332
+ assert ! ( try_success_result. is_err( ) , "Macro matching returned a success on the second try" ) ;
333
+
334
+ if let Some ( result) = tracker. result {
335
+ // An irrecoverable error occured and has been emitted.
336
+ return result;
337
+ }
338
+
339
+ let Some ( ( token, label) ) = tracker. best_failure else {
340
+ return tracker. result . expect ( "must have encountered Error or ErrorReported" ) ;
341
+ } ;
342
+
343
+ let span = token. span . substitute_dummy ( sp) ;
344
+
345
+ let mut err = cx. struct_span_err ( span, & parse_failure_msg ( & token) ) ;
346
+ err. span_label ( span, label) ;
347
+ if !def_span. is_dummy ( ) && !cx. source_map ( ) . is_imported ( def_span) {
348
+ err. span_label ( cx. source_map ( ) . guess_head_span ( def_span) , "when calling this macro" ) ;
324
349
}
325
350
351
+ annotate_doc_comment ( & mut err, sess. source_map ( ) , span) ;
352
+
353
+ // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
354
+ if let Some ( ( arg, comma_span) ) = arg. add_comma ( ) {
355
+ for lhs in lhses {
356
+ let parser = parser_from_cx ( sess, arg. clone ( ) ) ;
357
+ let mut tt_parser = TtParser :: new ( name) ;
358
+
359
+ if let Success ( _) =
360
+ tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, & mut NoopTracker )
361
+ {
362
+ if comma_span. is_dummy ( ) {
363
+ err. note ( "you might be missing a comma" ) ;
364
+ } else {
365
+ err. span_suggestion_short (
366
+ comma_span,
367
+ "missing comma here" ,
368
+ ", " ,
369
+ Applicability :: MachineApplicable ,
370
+ ) ;
371
+ }
372
+ }
373
+ }
374
+ }
375
+ err. emit ( ) ;
376
+ cx. trace_macros_diag ( ) ;
326
377
DummyResult :: any ( sp)
327
378
}
328
379
380
+ /// The tracker used for the slow error path that collects useful info for diagnostics
381
+ struct CollectTrackerAndEmitter < ' a , ' cx > {
382
+ cx : & ' a mut ExtCtxt < ' cx > ,
383
+ /// Which arm's failure should we report? (the one furthest along)
384
+ best_failure : Option < ( Token , & ' static str ) > ,
385
+ root_span : Span ,
386
+ result : Option < Box < dyn MacResult + ' cx > > ,
387
+ }
388
+
389
+ impl < ' a , ' cx , ' matcher > Tracker < ' matcher > for CollectTrackerAndEmitter < ' a , ' cx > {
390
+ fn before_match_loc ( & mut self , _parser : & TtParser , _matcher : & ' matcher MatcherLoc ) {
391
+ // Empty for now.
392
+ }
393
+
394
+ fn after_arm ( & mut self , result : & NamedParseResult ) {
395
+ match result {
396
+ Success ( _) => {
397
+ unreachable ! ( "should not collect detailed info for successful macro match" ) ;
398
+ }
399
+ Failure ( token, msg) => match self . best_failure {
400
+ Some ( ( ref best_token, _) ) if best_token. span . lo ( ) >= token. span . lo ( ) => { }
401
+ _ => self . best_failure = Some ( ( token. clone ( ) , msg) ) ,
402
+ } ,
403
+ Error ( err_sp, msg) => {
404
+ let span = err_sp. substitute_dummy ( self . root_span ) ;
405
+ self . cx . struct_span_err ( span, msg) . emit ( ) ;
406
+ self . result = Some ( DummyResult :: any ( span) ) ;
407
+ }
408
+ ErrorReported ( _) => self . result = Some ( DummyResult :: any ( self . root_span ) ) ,
409
+ }
410
+ }
411
+
412
+ fn description ( ) -> & ' static str {
413
+ "detailed"
414
+ }
415
+ }
416
+
417
+ impl < ' a , ' cx > CollectTrackerAndEmitter < ' a , ' cx > {
418
+ fn new ( cx : & ' a mut ExtCtxt < ' cx > , root_span : Span ) -> Self {
419
+ Self { cx, best_failure : None , root_span, result : None }
420
+ }
421
+ }
422
+
423
+ enum CanRetry {
424
+ Yes ,
425
+ /// We are not allowed to retry macro expansion as a fatal error has been emitted already.
426
+ No ( ErrorGuaranteed ) ,
427
+ }
428
+
329
429
/// Try expanding the macro. Returns the index of the sucessful arm and its named_matches if it was successful,
330
430
/// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors
331
431
/// correctly.
@@ -335,7 +435,7 @@ fn try_match_macro<'matcher, T: Tracker<'matcher>>(
335
435
arg : & TokenStream ,
336
436
lhses : & ' matcher [ Vec < MatcherLoc > ] ,
337
437
track : & mut T ,
338
- ) -> Result < ( usize , NamedMatches ) , ( ) > {
438
+ ) -> Result < ( usize , NamedMatches ) , CanRetry > {
339
439
// We create a base parser that can be used for the "black box" parts.
340
440
// Every iteration needs a fresh copy of that parser. However, the parser
341
441
// is not mutated on many of the iterations, particularly when dealing with
@@ -383,10 +483,10 @@ fn try_match_macro<'matcher, T: Tracker<'matcher>>(
383
483
}
384
484
Error ( _, _) => {
385
485
// We haven't emitted an error yet
386
- return Err ( ( ) ) ;
486
+ return Err ( CanRetry :: Yes ) ;
387
487
}
388
- ErrorReported ( _ ) => {
389
- return Err ( ( ) ) ;
488
+ ErrorReported ( guarantee ) => {
489
+ return Err ( CanRetry :: No ( guarantee ) ) ;
390
490
}
391
491
}
392
492
@@ -395,7 +495,7 @@ fn try_match_macro<'matcher, T: Tracker<'matcher>>(
395
495
mem:: swap ( & mut gated_spans_snapshot, & mut sess. gated_spans . spans . borrow_mut ( ) ) ;
396
496
}
397
497
398
- Err ( ( ) )
498
+ Err ( CanRetry :: Yes )
399
499
}
400
500
401
501
// Note that macro-by-example's input is also matched against a token tree:
@@ -478,7 +578,7 @@ pub fn compile_declarative_macro(
478
578
let mut tt_parser =
479
579
TtParser :: new ( Ident :: with_dummy_span ( if macro_rules { kw:: MacroRules } else { kw:: Macro } ) ) ;
480
580
let argument_map =
481
- match tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & argument_gram, & mut NoopTracker ) {
581
+ match tt_parser. parse_tt ( & mut Cow :: Owned ( parser) , & argument_gram, & mut NoopTracker ) {
482
582
Success ( m) => m,
483
583
Failure ( token, msg) => {
484
584
let s = parse_failure_msg ( & token) ;
0 commit comments