Skip to content

Commit b7b6722

Browse files
committed
Only do parser recovery on retried macro matching
This prevents issues with eager parser recovery during macro matching.
1 parent 6d651a2 commit b7b6722

File tree

5 files changed

+60
-10
lines changed

5 files changed

+60
-10
lines changed

Diff for: compiler/rustc_expand/src/mbe/macro_rules.rs

+27-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_lint_defs::builtin::{
2222
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
2323
};
2424
use rustc_lint_defs::BuiltinLintDiagnostics;
25-
use rustc_parse::parser::Parser;
25+
use rustc_parse::parser::{Parser, Recovery};
2626
use rustc_session::parse::ParseSess;
2727
use rustc_session::Session;
2828
use rustc_span::edition::Edition;
@@ -219,6 +219,8 @@ pub(super) trait Tracker<'matcher> {
219219

220220
/// For tracing.
221221
fn description() -> &'static str;
222+
223+
fn recovery() -> Recovery;
222224
}
223225

224226
/// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to monomorphization.
@@ -230,6 +232,9 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
230232
fn description() -> &'static str {
231233
"none"
232234
}
235+
fn recovery() -> Recovery {
236+
Recovery::Forbidden
237+
}
233238
}
234239

235240
/// Expands the rules based macro defined by `lhses` and `rhses` for a given
@@ -330,15 +335,20 @@ fn expand_macro<'cx>(
330335
let mut tracker = CollectTrackerAndEmitter::new(cx, sp);
331336

332337
let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker);
333-
assert!(try_success_result.is_err(), "Macro matching returned a success on the second try");
338+
339+
if try_success_result.is_ok() {
340+
// Nonterminal parser recovery might turn failed matches into successful ones,
341+
// but for that it must have emitted an error already
342+
tracker.cx.sess.delay_span_bug(sp, "Macro matching returned a success on the second try");
343+
}
334344

335345
if let Some(result) = tracker.result {
336346
// An irrecoverable error occurred and has been emitted.
337347
return result;
338348
}
339349

340350
let Some((token, label, remaining_matcher)) = tracker.best_failure else {
341-
return tracker.result.expect("must have encountered Error or ErrorReported");
351+
return DummyResult::any(sp);
342352
};
343353

344354
let span = token.span.substitute_dummy(sp);
@@ -360,7 +370,7 @@ fn expand_macro<'cx>(
360370
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
361371
if let Some((arg, comma_span)) = arg.add_comma() {
362372
for lhs in lhses {
363-
let parser = parser_from_cx(sess, arg.clone());
373+
let parser = parser_from_cx(sess, arg.clone(), Recovery::Allowed);
364374
let mut tt_parser = TtParser::new(name);
365375

366376
if let Success(_) =
@@ -406,7 +416,12 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx,
406416
fn after_arm(&mut self, result: &NamedParseResult) {
407417
match result {
408418
Success(_) => {
409-
unreachable!("should not collect detailed info for successful macro match");
419+
// Nonterminal parser recovery might turn failed matches into successful ones,
420+
// but for that it must have emitted an error already
421+
self.cx.sess.delay_span_bug(
422+
self.root_span,
423+
"should not collect detailed info for successful macro match",
424+
);
410425
}
411426
Failure(token, msg) => match self.best_failure {
412427
Some((ref best_token, _, _)) if best_token.span.lo() >= token.span.lo() => {}
@@ -432,6 +447,10 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx,
432447
fn description() -> &'static str {
433448
"detailed"
434449
}
450+
451+
fn recovery() -> Recovery {
452+
Recovery::Allowed
453+
}
435454
}
436455

437456
impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
@@ -477,7 +496,7 @@ fn try_match_macro<'matcher, T: Tracker<'matcher>>(
477496
// 68836 suggests a more comprehensive but more complex change to deal with
478497
// this situation.)
479498
// FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match.
480-
let parser = parser_from_cx(sess, arg.clone());
499+
let parser = parser_from_cx(sess, arg.clone(), T::recovery());
481500
// Try each arm's matchers.
482501
let mut tt_parser = TtParser::new(name);
483502
for (i, lhs) in lhses.iter().enumerate() {
@@ -1559,8 +1578,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
15591578
}
15601579
}
15611580

1562-
fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> {
1563-
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
1581+
fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> {
1582+
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS).recovery(recovery)
15641583
}
15651584

15661585
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For

Diff for: compiler/rustc_parse/src/parser/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -503,8 +503,8 @@ impl<'a> Parser<'a> {
503503
parser
504504
}
505505

506-
pub fn forbid_recovery(mut self) -> Self {
507-
self.recovery = Recovery::Forbidden;
506+
pub fn recovery(mut self, recovery: Recovery) -> Self {
507+
self.recovery = recovery;
508508
self
509509
}
510510

Diff for: src/test/ui/macros/recovery-allowed.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
macro_rules! please_recover {
2+
($a:expr) => {};
3+
}
4+
5+
please_recover! { not 1 }
6+
//~^ ERROR unexpected `1` after identifier
7+
8+
fn main() {}

Diff for: src/test/ui/macros/recovery-allowed.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: unexpected `1` after identifier
2+
--> $DIR/recovery-allowed.rs:5:23
3+
|
4+
LL | please_recover! { not 1 }
5+
| ----^
6+
| |
7+
| help: use `!` to perform bitwise not
8+
9+
error: aborting due to previous error
10+

Diff for: src/test/ui/macros/recovery-forbidden.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// check-pass
2+
3+
macro_rules! dont_recover_here {
4+
($e:expr) => {
5+
compile_error!("Must not recover to single !1 expr");
6+
};
7+
8+
(not $a:literal) => {};
9+
}
10+
11+
dont_recover_here! { not 1 }
12+
13+
fn main() {}

0 commit comments

Comments
 (0)