@@ -14,7 +14,9 @@ use rustc_ast::{NodeId, DUMMY_NODE_ID};
14
14
use rustc_ast_pretty:: pprust;
15
15
use rustc_attr:: { self as attr, TransparencyError } ;
16
16
use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
17
- use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , DiagnosticMessage } ;
17
+ use rustc_errors:: {
18
+ Applicability , Diagnostic , DiagnosticBuilder , DiagnosticMessage , ErrorGuaranteed ,
19
+ } ;
18
20
use rustc_feature:: Features ;
19
21
use rustc_lint_defs:: builtin:: {
20
22
RUST_2021_INCOMPATIBLE_OR_PATTERNS , SEMICOLON_IN_EXPRESSIONS_FROM_MACROS ,
@@ -33,7 +35,7 @@ use std::borrow::Cow;
33
35
use std:: collections:: hash_map:: Entry ;
34
36
use std:: { mem, slice} ;
35
37
36
- use super :: macro_parser:: NamedParseResult ;
38
+ use super :: macro_parser:: { NamedMatches , NamedParseResult } ;
37
39
38
40
pub ( crate ) struct ParserAnyMacro < ' a > {
39
41
parser : Parser < ' a > ,
@@ -253,9 +255,87 @@ fn expand_macro<'cx>(
253
255
trace_macros_note ( & mut cx. expansions , sp, msg) ;
254
256
}
255
257
256
- // Which arm's failure should we report? (the one furthest along)
257
- let mut best_failure: Option < ( Token , & str ) > = None ;
258
+ // Track nothing for the best performance
259
+ let try_success_result = try_match_macro ( sess, name, & arg, lhses, & mut NoopTracker ) ;
260
+
261
+ match try_success_result {
262
+ Ok ( ( i, named_matches) ) => {
263
+ let ( rhs, rhs_span) : ( & mbe:: Delimited , DelimSpan ) = match & rhses[ i] {
264
+ mbe:: TokenTree :: Delimited ( span, delimited) => ( & delimited, * span) ,
265
+ _ => cx. span_bug ( sp, "malformed macro rhs" ) ,
266
+ } ;
267
+ let arm_span = rhses[ i] . span ( ) ;
268
+
269
+ let rhs_spans = rhs. tts . iter ( ) . map ( |t| t. span ( ) ) . collect :: < Vec < _ > > ( ) ;
270
+ // rhs has holes ( `$id` and `$(...)` that need filled)
271
+ let mut tts = match transcribe ( cx, & named_matches, & rhs, rhs_span, transparency) {
272
+ Ok ( tts) => tts,
273
+ Err ( mut err) => {
274
+ err. emit ( ) ;
275
+ return DummyResult :: any ( arm_span) ;
276
+ }
277
+ } ;
278
+
279
+ // Replace all the tokens for the corresponding positions in the macro, to maintain
280
+ // proper positions in error reporting, while maintaining the macro_backtrace.
281
+ if rhs_spans. len ( ) == tts. len ( ) {
282
+ tts = tts. map_enumerated ( |i, tt| {
283
+ let mut tt = tt. clone ( ) ;
284
+ let mut sp = rhs_spans[ i] ;
285
+ sp = sp. with_ctxt ( tt. span ( ) . ctxt ( ) ) ;
286
+ tt. set_span ( sp) ;
287
+ tt
288
+ } ) ;
289
+ }
290
+
291
+ if cx. trace_macros ( ) {
292
+ let msg = format ! ( "to `{}`" , pprust:: tts_to_string( & tts) ) ;
293
+ trace_macros_note ( & mut cx. expansions , sp, msg) ;
294
+ }
295
+
296
+ let mut p = Parser :: new ( sess, tts, false , None ) ;
297
+ p. last_type_ascription = cx. current_expansion . prior_type_ascription ;
298
+
299
+ if is_local {
300
+ cx. resolver . record_macro_rule_usage ( node_id, i) ;
301
+ }
302
+
303
+ // Let the context choose how to interpret the result.
304
+ // Weird, but useful for X-macros.
305
+ return Box :: new ( ParserAnyMacro {
306
+ parser : p,
307
+
308
+ // Pass along the original expansion site and the name of the macro
309
+ // so we can print a useful error message if the parse of the expanded
310
+ // macro leaves unparsed tokens.
311
+ site_span : sp,
312
+ macro_ident : name,
313
+ lint_node_id : cx. current_expansion . lint_node_id ,
314
+ is_trailing_mac : cx. current_expansion . is_trailing_mac ,
315
+ arm_span,
316
+ is_local,
317
+ } ) ;
318
+ }
319
+ Err ( ( ) ) => {
320
+ todo ! ( "Retry macro invocation while tracking diagnostics info and emit error" ) ;
321
+
322
+ return DummyResult :: any ( sp) ;
323
+ }
324
+ }
325
+
326
+ DummyResult :: any ( sp)
327
+ }
258
328
329
+ /// Try expanding the macro. Returns the index of the sucessful arm and its named_matches if it was successful,
330
+ /// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors
331
+ /// correctly.
332
+ fn try_match_macro < ' matcher , T : Tracker < ' matcher > > (
333
+ sess : & ParseSess ,
334
+ name : Ident ,
335
+ arg : & TokenStream ,
336
+ lhses : & ' matcher [ Vec < MatcherLoc > ] ,
337
+ track : & mut T ,
338
+ ) -> Result < ( usize , NamedMatches ) , ( ) > {
259
339
// We create a base parser that can be used for the "black box" parts.
260
340
// Every iteration needs a fresh copy of that parser. However, the parser
261
341
// is not mutated on many of the iterations, particularly when dealing with
@@ -277,7 +357,6 @@ fn expand_macro<'cx>(
277
357
// this situation.)
278
358
// FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match.
279
359
let parser = parser_from_cx ( sess, arg. clone ( ) ) ;
280
-
281
360
// Try each arm's matchers.
282
361
let mut tt_parser = TtParser :: new ( name) ;
283
362
for ( i, lhs) in lhses. iter ( ) . enumerate ( ) {
@@ -287,115 +366,36 @@ fn expand_macro<'cx>(
287
366
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
288
367
let mut gated_spans_snapshot = mem:: take ( & mut * sess. gated_spans . spans . borrow_mut ( ) ) ;
289
368
290
- match tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, & mut NoopTracker ) {
369
+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, track) ;
370
+
371
+ track. after_arm ( & result) ;
372
+
373
+ match result {
291
374
Success ( named_matches) => {
292
375
// The matcher was `Success(..)`ful.
293
376
// Merge the gated spans from parsing the matcher with the pre-existing ones.
294
377
sess. gated_spans . merge ( gated_spans_snapshot) ;
295
378
296
- let ( rhs, rhs_span) : ( & mbe:: Delimited , DelimSpan ) = match & rhses[ i] {
297
- mbe:: TokenTree :: Delimited ( span, delimited) => ( & delimited, * span) ,
298
- _ => cx. span_bug ( sp, "malformed macro rhs" ) ,
299
- } ;
300
- let arm_span = rhses[ i] . span ( ) ;
301
-
302
- let rhs_spans = rhs. tts . iter ( ) . map ( |t| t. span ( ) ) . collect :: < Vec < _ > > ( ) ;
303
- // rhs has holes ( `$id` and `$(...)` that need filled)
304
- let mut tts = match transcribe ( cx, & named_matches, & rhs, rhs_span, transparency) {
305
- Ok ( tts) => tts,
306
- Err ( mut err) => {
307
- err. emit ( ) ;
308
- return DummyResult :: any ( arm_span) ;
309
- }
310
- } ;
311
-
312
- // Replace all the tokens for the corresponding positions in the macro, to maintain
313
- // proper positions in error reporting, while maintaining the macro_backtrace.
314
- if rhs_spans. len ( ) == tts. len ( ) {
315
- tts = tts. map_enumerated ( |i, tt| {
316
- let mut tt = tt. clone ( ) ;
317
- let mut sp = rhs_spans[ i] ;
318
- sp = sp. with_ctxt ( tt. span ( ) . ctxt ( ) ) ;
319
- tt. set_span ( sp) ;
320
- tt
321
- } ) ;
322
- }
323
-
324
- if cx. trace_macros ( ) {
325
- let msg = format ! ( "to `{}`" , pprust:: tts_to_string( & tts) ) ;
326
- trace_macros_note ( & mut cx. expansions , sp, msg) ;
327
- }
328
-
329
- let mut p = Parser :: new ( sess, tts, false , None ) ;
330
- p. last_type_ascription = cx. current_expansion . prior_type_ascription ;
331
-
332
- if is_local {
333
- cx. resolver . record_macro_rule_usage ( node_id, i) ;
334
- }
335
-
336
- // Let the context choose how to interpret the result.
337
- // Weird, but useful for X-macros.
338
- return Box :: new ( ParserAnyMacro {
339
- parser : p,
340
-
341
- // Pass along the original expansion site and the name of the macro
342
- // so we can print a useful error message if the parse of the expanded
343
- // macro leaves unparsed tokens.
344
- site_span : sp,
345
- macro_ident : name,
346
- lint_node_id : cx. current_expansion . lint_node_id ,
347
- is_trailing_mac : cx. current_expansion . is_trailing_mac ,
348
- arm_span,
349
- is_local,
350
- } ) ;
379
+ return Ok ( ( i, named_matches) ) ;
351
380
}
352
- Failure ( token, msg) => match best_failure {
353
- Some ( ( ref best_token, _) ) if best_token. span . lo ( ) >= token. span . lo ( ) => { }
354
- _ => best_failure = Some ( ( token, msg) ) ,
355
- } ,
356
- Error ( err_sp, ref msg) => {
357
- let span = err_sp. substitute_dummy ( sp) ;
358
- cx. struct_span_err ( span, & msg) . emit ( ) ;
359
- return DummyResult :: any ( span) ;
381
+ Failure ( _, _) => {
382
+ // Try the next arm
383
+ }
384
+ Error ( _, _) => {
385
+ // We haven't emitted an error yet
386
+ return Err ( ( ) ) ;
387
+ }
388
+ ErrorReported ( _) => {
389
+ return Err ( ( ) ) ;
360
390
}
361
- ErrorReported ( _) => return DummyResult :: any ( sp) ,
362
391
}
363
392
364
393
// The matcher was not `Success(..)`ful.
365
394
// Restore to the state before snapshotting and maybe try again.
366
395
mem:: swap ( & mut gated_spans_snapshot, & mut sess. gated_spans . spans . borrow_mut ( ) ) ;
367
396
}
368
- drop ( parser) ;
369
-
370
- let ( token, label) = best_failure. expect ( "ran no matchers" ) ;
371
- let span = token. span . substitute_dummy ( sp) ;
372
- let mut err = cx. struct_span_err ( span, & parse_failure_msg ( & token) ) ;
373
- err. span_label ( span, label) ;
374
- if !def_span. is_dummy ( ) && !cx. source_map ( ) . is_imported ( def_span) {
375
- err. span_label ( cx. source_map ( ) . guess_head_span ( def_span) , "when calling this macro" ) ;
376
- }
377
- annotate_doc_comment ( & mut err, sess. source_map ( ) , span) ;
378
- // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
379
- if let Some ( ( arg, comma_span) ) = arg. add_comma ( ) {
380
- for lhs in lhses {
381
- let parser = parser_from_cx ( sess, arg. clone ( ) ) ;
382
- if let Success ( _) = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, & mut NoopTracker ) {
383
- if comma_span. is_dummy ( ) {
384
- err. note ( "you might be missing a comma" ) ;
385
- } else {
386
- err. span_suggestion_short (
387
- comma_span,
388
- "missing comma here" ,
389
- ", " ,
390
- Applicability :: MachineApplicable ,
391
- ) ;
392
- }
393
- }
394
- }
395
- }
396
- err. emit ( ) ;
397
- cx. trace_macros_diag ( ) ;
398
- DummyResult :: any ( sp)
397
+
398
+ Err ( ( ) )
399
399
}
400
400
401
401
// Note that macro-by-example's input is also matched against a token tree:
@@ -477,28 +477,29 @@ pub fn compile_declarative_macro(
477
477
let parser = Parser :: new ( & sess. parse_sess , body, true , rustc_parse:: MACRO_ARGUMENTS ) ;
478
478
let mut tt_parser =
479
479
TtParser :: new ( Ident :: with_dummy_span ( if macro_rules { kw:: MacroRules } else { kw:: Macro } ) ) ;
480
- let argument_map = match tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & argument_gram, & mut NoopTracker ) {
481
- Success ( m) => m,
482
- Failure ( token, msg) => {
483
- let s = parse_failure_msg ( & token) ;
484
- let sp = token. span . substitute_dummy ( def. span ) ;
485
- let mut err = sess. parse_sess . span_diagnostic . struct_span_err ( sp, & s) ;
486
- err. span_label ( sp, msg) ;
487
- annotate_doc_comment ( & mut err, sess. source_map ( ) , sp) ;
488
- err. emit ( ) ;
489
- return dummy_syn_ext ( ) ;
490
- }
491
- Error ( sp, msg) => {
492
- sess. parse_sess
493
- . span_diagnostic
494
- . struct_span_err ( sp. substitute_dummy ( def. span ) , & msg)
495
- . emit ( ) ;
496
- return dummy_syn_ext ( ) ;
497
- }
498
- ErrorReported ( _) => {
499
- return dummy_syn_ext ( ) ;
500
- }
501
- } ;
480
+ let argument_map =
481
+ match tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & argument_gram, & mut NoopTracker ) {
482
+ Success ( m) => m,
483
+ Failure ( token, msg) => {
484
+ let s = parse_failure_msg ( & token) ;
485
+ let sp = token. span . substitute_dummy ( def. span ) ;
486
+ let mut err = sess. parse_sess . span_diagnostic . struct_span_err ( sp, & s) ;
487
+ err. span_label ( sp, msg) ;
488
+ annotate_doc_comment ( & mut err, sess. source_map ( ) , sp) ;
489
+ err. emit ( ) ;
490
+ return dummy_syn_ext ( ) ;
491
+ }
492
+ Error ( sp, msg) => {
493
+ sess. parse_sess
494
+ . span_diagnostic
495
+ . struct_span_err ( sp. substitute_dummy ( def. span ) , & msg)
496
+ . emit ( ) ;
497
+ return dummy_syn_ext ( ) ;
498
+ }
499
+ ErrorReported ( _) => {
500
+ return dummy_syn_ext ( ) ;
501
+ }
502
+ } ;
502
503
503
504
let mut valid = true ;
504
505
0 commit comments