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;
/// 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.
fn description() -> &'static str {
"none"
}
+ fn recovery() -> Recovery {
+ Recovery::Forbidden
+ }
}
/// Expands the rules based macro defined by `lhses` and `rhses` for a given
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 occured and has been emitted.
+ // An irrecoverable error occurred and has been emitted.
return result;
}
- let Some((token, label)) = tracker.best_failure else {
- return tracker.result.expect("must have encountered Error or ErrorReported");
+ let Some((token, label, remaining_matcher)) = tracker.best_failure else {
+ return DummyResult::any(sp);
};
let span = token.span.substitute_dummy(sp);
annotate_doc_comment(&mut err, sess.source_map(), span);
+ if let Some(span) = remaining_matcher.span() {
+ err.span_note(span, format!("while trying to match {remaining_matcher}"));
+ } else {
+ err.note(format!("while trying to match {remaining_matcher}"));
+ }
+
// 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(_) =
}
/// The tracker used for the slow error path that collects useful info for diagnostics.
-struct CollectTrackerAndEmitter<'a, 'cx> {
+struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
cx: &'a mut ExtCtxt<'cx>,
+ remaining_matcher: Option<&'matcher MatcherLoc>,
/// Which arm's failure should we report? (the one furthest along)
- best_failure: Option<(Token, &'static str)>,
+ best_failure: Option<(Token, &'static str, MatcherLoc)>,
root_span: Span,
result: Option<Box<dyn MacResult + 'cx>>,
}
-impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx> {
- fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {
- // Empty for now.
+impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
+ fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) {
+ if self.remaining_matcher.is_none()
+ || (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof)
+ {
+ self.remaining_matcher = Some(matcher);
+ }
}
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() => {}
- _ => self.best_failure = Some((token.clone(), msg)),
+ Some((ref best_token, _, _)) if best_token.span.lo() >= token.span.lo() => {}
+ _ => {
+ self.best_failure = Some((
+ token.clone(),
+ msg,
+ self.remaining_matcher
+ .expect("must have collected matcher already")
+ .clone(),
+ ))
+ }
},
Error(err_sp, msg) => {
let span = err_sp.substitute_dummy(self.root_span);
fn description() -> &'static str {
"detailed"
}
+
+ fn recovery() -> Recovery {
+ Recovery::Allowed
+ }
}
-impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx> {
+impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self {
- Self { cx, best_failure: None, root_span, result: None }
+ Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None }
}
}
// 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() {
}
}
-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