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,
|
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||||
};
|
};
|
||||||
use rustc_lint_defs::BuiltinLintDiagnostics;
|
use rustc_lint_defs::BuiltinLintDiagnostics;
|
||||||
use rustc_parse::parser::Parser;
|
use rustc_parse::parser::{Parser, Recovery};
|
||||||
use rustc_session::parse::ParseSess;
|
use rustc_session::parse::ParseSess;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
|
@ -219,6 +219,8 @@ pub(super) trait Tracker<'matcher> {
|
||||||
|
|
||||||
/// For tracing.
|
/// For tracing.
|
||||||
fn description() -> &'static str;
|
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.
|
/// 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 {
|
fn description() -> &'static str {
|
||||||
"none"
|
"none"
|
||||||
}
|
}
|
||||||
|
fn recovery() -> Recovery {
|
||||||
|
Recovery::Forbidden
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expands the rules based macro defined by `lhses` and `rhses` for a given
|
/// 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 mut tracker = CollectTrackerAndEmitter::new(cx, sp);
|
||||||
|
|
||||||
let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker);
|
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 {
|
if let Some(result) = tracker.result {
|
||||||
// An irrecoverable error occurred and has been emitted.
|
// 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 {
|
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);
|
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);`
|
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
|
||||||
if let Some((arg, comma_span)) = arg.add_comma() {
|
if let Some((arg, comma_span)) = arg.add_comma() {
|
||||||
for lhs in lhses {
|
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);
|
let mut tt_parser = TtParser::new(name);
|
||||||
|
|
||||||
if let Success(_) =
|
if let Success(_) =
|
||||||
|
@ -406,7 +416,12 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx,
|
||||||
fn after_arm(&mut self, result: &NamedParseResult) {
|
fn after_arm(&mut self, result: &NamedParseResult) {
|
||||||
match result {
|
match result {
|
||||||
Success(_) => {
|
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 {
|
Failure(token, msg) => match self.best_failure {
|
||||||
Some((ref best_token, _, _)) if best_token.span.lo() >= token.span.lo() => {}
|
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 {
|
fn description() -> &'static str {
|
||||||
"detailed"
|
"detailed"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recovery() -> Recovery {
|
||||||
|
Recovery::Allowed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
|
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
|
// 68836 suggests a more comprehensive but more complex change to deal with
|
||||||
// this situation.)
|
// this situation.)
|
||||||
// FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match.
|
// 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.
|
// Try each arm's matchers.
|
||||||
let mut tt_parser = TtParser::new(name);
|
let mut tt_parser = TtParser::new(name);
|
||||||
for (i, lhs) in lhses.iter().enumerate() {
|
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<'_> {
|
fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> {
|
||||||
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
|
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS).recovery(recovery)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
|
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
|
||||||
|
|
|
@ -503,8 +503,8 @@ impl<'a> Parser<'a> {
|
||||||
parser
|
parser
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn forbid_recovery(mut self) -> Self {
|
pub fn recovery(mut self, recovery: Recovery) -> Self {
|
||||||
self.recovery = Recovery::Forbidden;
|
self.recovery = recovery;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
src/test/ui/macros/recovery-allowed.rs
Normal file
8
src/test/ui/macros/recovery-allowed.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
macro_rules! please_recover {
|
||||||
|
($a:expr) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
please_recover! { not 1 }
|
||||||
|
//~^ ERROR unexpected `1` after identifier
|
||||||
|
|
||||||
|
fn main() {}
|
10
src/test/ui/macros/recovery-allowed.stderr
Normal file
10
src/test/ui/macros/recovery-allowed.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
error: unexpected `1` after identifier
|
||||||
|
--> $DIR/recovery-allowed.rs:5:23
|
||||||
|
|
|
||||||
|
LL | please_recover! { not 1 }
|
||||||
|
| ----^
|
||||||
|
| |
|
||||||
|
| help: use `!` to perform bitwise not
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
13
src/test/ui/macros/recovery-forbidden.rs
Normal file
13
src/test/ui/macros/recovery-forbidden.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
macro_rules! dont_recover_here {
|
||||||
|
($e:expr) => {
|
||||||
|
compile_error!("Must not recover to single !1 expr");
|
||||||
|
};
|
||||||
|
|
||||||
|
(not $a:literal) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
dont_recover_here! { not 1 }
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue