Skip to content

Commit

Permalink
Show a note where a macro failed to match
Browse files Browse the repository at this point in the history
This shows a small note on what the macro matcher was currently
processing to aid with "no rules expected the token X" errors.
  • Loading branch information
Noratrieb committed Nov 14, 2022
1 parent 8ef2485 commit 7e7c11c
Show file tree
Hide file tree
Showing 25 changed files with 355 additions and 11 deletions.
48 changes: 47 additions & 1 deletion compiler/rustc_expand/src/mbe/macro_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub(crate) use ParseResult::*;
use crate::mbe::{macro_rules::Tracker, KleeneOp, TokenTree};

use rustc_ast::token::{self, DocComment, Nonterminal, NonterminalKind, Token};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_errors::ErrorGuaranteed;
Expand All @@ -86,6 +87,7 @@ use rustc_span::symbol::MacroRulesNormalizedIdent;
use rustc_span::Span;
use std::borrow::Cow;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::fmt::Display;

/// A unit within a matcher that a `MatcherPos` can refer to. Similar to (and derived from)
/// `mbe::TokenTree`, but designed specifically for fast and easy traversal during matching.
Expand All @@ -96,7 +98,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
///
/// This means a matcher can be represented by `&[MatcherLoc]`, and traversal mostly involves
/// simply incrementing the current matcher position index by one.
#[derive(Debug)]
#[derive(Debug, PartialEq, Clone)]
pub(crate) enum MatcherLoc {
Token {
token: Token,
Expand Down Expand Up @@ -129,6 +131,46 @@ pub(crate) enum MatcherLoc {
Eof,
}

impl MatcherLoc {
pub(super) fn span(&self) -> Option<Span> {
match self {
MatcherLoc::Token { token } => Some(token.span),
MatcherLoc::Delimited => None,
MatcherLoc::Sequence { .. } => None,
MatcherLoc::SequenceKleeneOpNoSep { .. } => None,
MatcherLoc::SequenceSep { .. } => None,
MatcherLoc::SequenceKleeneOpAfterSep { .. } => None,
MatcherLoc::MetaVarDecl { span, .. } => Some(*span),
MatcherLoc::Eof => None,
}
}
}

impl Display for MatcherLoc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MatcherLoc::Token { token } | MatcherLoc::SequenceSep { separator: token } => {
write!(f, "`{}`", pprust::token_to_string(token))
}
MatcherLoc::MetaVarDecl { bind, kind, .. } => {
write!(f, "meta-variable `${bind}")?;
if let Some(kind) = kind {
write!(f, ":{}", kind)?;
}
write!(f, "`")?;
Ok(())
}
MatcherLoc::Eof => f.write_str("end of macro"),

// These are not printed in the diagnostic
MatcherLoc::Delimited => f.write_str("delimiter"),
MatcherLoc::Sequence { .. } => f.write_str("sequence start"),
MatcherLoc::SequenceKleeneOpNoSep { .. } => f.write_str("sequence end"),
MatcherLoc::SequenceKleeneOpAfterSep { .. } => f.write_str("sequence end"),
}
}
}

pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
fn inner(
tts: &[TokenTree],
Expand Down Expand Up @@ -398,6 +440,10 @@ impl TtParser {
}
}

pub(super) fn has_no_remaining_items_for_step(&self) -> bool {
self.cur_mps.is_empty()
}

/// Process the matcher positions of `cur_mps` until it is empty. In the process, this will
/// produce more mps in `next_mps` and `bb_mps`.
///
Expand Down
39 changes: 29 additions & 10 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ fn expand_macro<'cx>(
return result;
}

let Some((token, label)) = tracker.best_failure else {
let Some((token, label, remaining_matcher)) = tracker.best_failure else {
return tracker.result.expect("must have encountered Error or ErrorReported");
};

Expand All @@ -351,6 +351,12 @@ fn expand_macro<'cx>(

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 {
Expand Down Expand Up @@ -379,17 +385,22 @@ fn expand_macro<'cx>(
}

/// 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) {
Expand All @@ -398,8 +409,16 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx>
unreachable!("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);
Expand All @@ -415,9 +434,9 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx>
}
}

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 }
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/array-slice-vec/vec-macro-with-comma-only.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ error: no rules expected the token `,`
|
LL | vec![,];
| ^ no rules expected this token in macro call
|
= note: while trying to match end of macro

error: aborting due to previous error

Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ LL | macro_rules! gimme_a_const {
...
LL | let _fail = Example::<gimme_a_const!()>;
| ^^^^^^^^^^^^^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$rusty:ident`
--> $DIR/macro-fail.rs:28:8
|
LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }}
| ^^^^^^^^^^^^^

error[E0747]: type provided when a constant was expected
--> $DIR/macro-fail.rs:14:33
Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/editions/edition-keywords-2015-2015-parsing.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@ error: no rules expected the token `r#async`
|
LL | r#async = consumes_async!(r#async);
| ^^^^^^^ no rules expected this token in macro call
|
note: while trying to match `async`
--> $DIR/auxiliary/edition-kw-macro-2015.rs:17:6
|
LL | (async) => (1)
| ^^^^^

error: no rules expected the token `async`
--> $DIR/edition-keywords-2015-2015-parsing.rs:17:35
|
LL | r#async = consumes_async_raw!(async);
| ^^^^^ no rules expected this token in macro call
|
note: while trying to match `r#async`
--> $DIR/auxiliary/edition-kw-macro-2015.rs:22:6
|
LL | (r#async) => (1)
| ^^^^^^^

error: aborting due to 2 previous errors

12 changes: 12 additions & 0 deletions src/test/ui/editions/edition-keywords-2015-2018-parsing.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@ error: no rules expected the token `r#async`
|
LL | r#async = consumes_async!(r#async);
| ^^^^^^^ no rules expected this token in macro call
|
note: while trying to match `async`
--> $DIR/auxiliary/edition-kw-macro-2018.rs:17:6
|
LL | (async) => (1)
| ^^^^^

error: no rules expected the token `async`
--> $DIR/edition-keywords-2015-2018-parsing.rs:17:35
|
LL | r#async = consumes_async_raw!(async);
| ^^^^^ no rules expected this token in macro call
|
note: while trying to match `r#async`
--> $DIR/auxiliary/edition-kw-macro-2018.rs:22:6
|
LL | (r#async) => (1)
| ^^^^^^^

error: aborting due to 2 previous errors

12 changes: 12 additions & 0 deletions src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,24 @@ error: no rules expected the token `r#async`
|
LL | r#async = consumes_async!(r#async);
| ^^^^^^^ no rules expected this token in macro call
|
note: while trying to match `async`
--> $DIR/auxiliary/edition-kw-macro-2015.rs:17:6
|
LL | (async) => (1)
| ^^^^^

error: no rules expected the token `async`
--> $DIR/edition-keywords-2018-2015-parsing.rs:21:35
|
LL | r#async = consumes_async_raw!(async);
| ^^^^^ no rules expected this token in macro call
|
note: while trying to match `r#async`
--> $DIR/auxiliary/edition-kw-macro-2015.rs:22:6
|
LL | (r#async) => (1)
| ^^^^^^^

error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
--> $DIR/auxiliary/edition-kw-macro-2015.rs:27:23
Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,24 @@ error: no rules expected the token `r#async`
|
LL | r#async = consumes_async!(r#async);
| ^^^^^^^ no rules expected this token in macro call
|
note: while trying to match `async`
--> $DIR/auxiliary/edition-kw-macro-2018.rs:17:6
|
LL | (async) => (1)
| ^^^^^

error: no rules expected the token `async`
--> $DIR/edition-keywords-2018-2018-parsing.rs:21:35
|
LL | r#async = consumes_async_raw!(async);
| ^^^^^ no rules expected this token in macro call
|
note: while trying to match `r#async`
--> $DIR/auxiliary/edition-kw-macro-2018.rs:22:6
|
LL | (r#async) => (1)
| ^^^^^^^

error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
--> $DIR/auxiliary/edition-kw-macro-2018.rs:27:23
Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/empty/empty-comment.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ LL | macro_rules! one_arg_macro {
...
LL | one_arg_macro!(/**/);
| ^^^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$fmt:expr`
--> $DIR/empty-comment.rs:6:6
|
LL | ($fmt:expr) => (print!(concat!($fmt, "\n")));
| ^^^^^^^^^

error: aborting due to previous error

2 changes: 2 additions & 0 deletions src/test/ui/fail-simple.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ error: no rules expected the token `@`
|
LL | panic!(@);
| ^ no rules expected this token in macro call
|
= note: while trying to match end of macro

error: aborting due to previous error

6 changes: 6 additions & 0 deletions src/test/ui/issues/issue-7970a.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ LL | macro_rules! one_arg_macro {
...
LL | one_arg_macro!();
| ^^^^^^^^^^^^^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$fmt:expr`
--> $DIR/issue-7970a.rs:2:6
|
LL | ($fmt:expr) => (print!(concat!($fmt, "\n")));
| ^^^^^^^^^

error: aborting due to previous error

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ LL | assert!(true, "whatever" blah);
| -^^^^ no rules expected this token in macro call
| |
| help: missing comma here
|
= note: while trying to match sequence start

error: unexpected string literal
--> $DIR/assert-trailing-junk.rs:18:18
Expand All @@ -33,6 +35,8 @@ LL | assert!(true "whatever" blah);
| -^^^^ no rules expected this token in macro call
| |
| help: missing comma here
|
= note: while trying to match sequence start

error: macro requires an expression as an argument
--> $DIR/assert-trailing-junk.rs:22:5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ LL | assert!(true, "whatever" blah);
| -^^^^ no rules expected this token in macro call
| |
| help: missing comma here
|
= note: while trying to match sequence start

error: unexpected string literal
--> $DIR/assert-trailing-junk.rs:18:18
Expand All @@ -33,6 +35,8 @@ LL | assert!(true "whatever" blah);
| -^^^^ no rules expected this token in macro call
| |
| help: missing comma here
|
= note: while trying to match sequence start

error: macro requires an expression as an argument
--> $DIR/assert-trailing-junk.rs:22:5
Expand Down
Loading

0 comments on commit 7e7c11c

Please sign in to comment.