Only do parser recovery on retried macro matching
This prevents issues with eager parser recovery during macro matching.
This commit is contained in:
parent
6d651a295e
commit
b7b67228f9
5 changed files with 60 additions and 10 deletions
|
@ -22,7 +22,7 @@ use rustc_lint_defs::builtin::{
|
|||
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||
};
|
||||
use rustc_lint_defs::BuiltinLintDiagnostics;
|
||||
use rustc_parse::parser::Parser;
|
||||
use rustc_parse::parser::{Parser, Recovery};
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::edition::Edition;
|
||||
|
@ -219,6 +219,8 @@ pub(super) trait Tracker<'matcher> {
|
|||
|
||||
/// For tracing.
|
||||
fn description() -> &'static str;
|
||||
|
||||
fn recovery() -> Recovery;
|
||||
}
|
||||
|
||||
/// 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 {
|
|||
fn description() -> &'static str {
|
||||
"none"
|
||||
}
|
||||
fn recovery() -> Recovery {
|
||||
Recovery::Forbidden
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands the rules based macro defined by `lhses` and `rhses` for a given
|
||||
|
@ -330,7 +335,12 @@ fn expand_macro<'cx>(
|
|||
let mut tracker = CollectTrackerAndEmitter::new(cx, sp);
|
||||
|
||||
let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker);
|
||||
assert!(try_success_result.is_err(), "Macro matching returned a success on the second try");
|
||||
|
||||
if try_success_result.is_ok() {
|
||||
// Nonterminal parser recovery might turn failed matches into successful ones,
|
||||
// but for that it must have emitted an error already
|
||||
tracker.cx.sess.delay_span_bug(sp, "Macro matching returned a success on the second try");
|
||||
}
|
||||
|
||||
if let Some(result) = tracker.result {
|
||||
// An irrecoverable error occurred and has been emitted.
|
||||
|
@ -338,7 +348,7 @@ fn expand_macro<'cx>(
|
|||
}
|
||||
|
||||
let Some((token, label, remaining_matcher)) = tracker.best_failure else {
|
||||
return tracker.result.expect("must have encountered Error or ErrorReported");
|
||||
return DummyResult::any(sp);
|
||||
};
|
||||
|
||||
let span = token.span.substitute_dummy(sp);
|
||||
|
@ -360,7 +370,7 @@ fn expand_macro<'cx>(
|
|||
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
|
||||
if let Some((arg, comma_span)) = arg.add_comma() {
|
||||
for lhs in lhses {
|
||||
let parser = parser_from_cx(sess, arg.clone());
|
||||
let parser = parser_from_cx(sess, arg.clone(), Recovery::Allowed);
|
||||
let mut tt_parser = TtParser::new(name);
|
||||
|
||||
if let Success(_) =
|
||||
|
@ -406,7 +416,12 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx,
|
|||
fn after_arm(&mut self, result: &NamedParseResult) {
|
||||
match result {
|
||||
Success(_) => {
|
||||
unreachable!("should not collect detailed info for successful macro match");
|
||||
// Nonterminal parser recovery might turn failed matches into successful ones,
|
||||
// but for that it must have emitted an error already
|
||||
self.cx.sess.delay_span_bug(
|
||||
self.root_span,
|
||||
"should not collect detailed info for successful macro match",
|
||||
);
|
||||
}
|
||||
Failure(token, msg) => match self.best_failure {
|
||||
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,
|
|||
fn description() -> &'static str {
|
||||
"detailed"
|
||||
}
|
||||
|
||||
fn recovery() -> Recovery {
|
||||
Recovery::Allowed
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
|
||||
|
@ -477,7 +496,7 @@ fn try_match_macro<'matcher, T: Tracker<'matcher>>(
|
|||
// 68836 suggests a more comprehensive but more complex change to deal with
|
||||
// this situation.)
|
||||
// FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match.
|
||||
let parser = parser_from_cx(sess, arg.clone());
|
||||
let parser = parser_from_cx(sess, arg.clone(), T::recovery());
|
||||
// Try each arm's matchers.
|
||||
let mut tt_parser = TtParser::new(name);
|
||||
for (i, lhs) in lhses.iter().enumerate() {
|
||||
|
@ -1559,8 +1578,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> {
|
||||
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
|
||||
fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> {
|
||||
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS).recovery(recovery)
|
||||
}
|
||||
|
||||
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue