Skip to content

Commit e033a38

Browse files
authored
Rollup merge of rust-lang#104335 - Nilstrieb:macrowo, r=compiler-errors
Only do parser recovery on retried macro matching Eager parser recovery can break macros, so we don't do it at first. But when we already know that the macro failed, we can retry it with recovery enabled to still emit useful diagnostics. Helps with rust-lang#103534
2 parents 353b915 + b7b6722 commit e033a38

File tree

5 files changed

+60
-10
lines changed

5 files changed

+60
-10
lines changed

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

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

+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() {}
+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+
+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)