Skip to content

Commit

Permalink
Auto merge of #125180 - mu001999-contrib:improve/macro-diag, r=fee1-dead
Browse files Browse the repository at this point in the history
Improve error message: missing `;` in macro_rules

Fixes #124968
  • Loading branch information
bors committed May 18, 2024
2 parents bb97203 + c2be134 commit 685a80f
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 13 deletions.
40 changes: 33 additions & 7 deletions compiler/rustc_expand/src/mbe/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub(super) fn failed_to_match_macro<'cx>(

let span = token.span.substitute_dummy(sp);

let mut err = cx.dcx().struct_span_err(span, parse_failure_msg(&token));
let mut err = cx.dcx().struct_span_err(span, parse_failure_msg(&token, None));
err.span_label(span, label);
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
Expand Down Expand Up @@ -200,9 +200,17 @@ impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
}

/// Currently used by macro_rules! compilation to extract a little information from the `Failure` case.
pub struct FailureForwarder;
pub struct FailureForwarder<'matcher> {
expected_token: Option<&'matcher Token>,
}

impl<'matcher> FailureForwarder<'matcher> {
pub fn new() -> Self {
Self { expected_token: None }
}
}

impl<'matcher> Tracker<'matcher> for FailureForwarder {
impl<'matcher> Tracker<'matcher> for FailureForwarder<'matcher> {
type Failure = (Token, usize, &'static str);

fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure {
Expand All @@ -212,6 +220,14 @@ impl<'matcher> Tracker<'matcher> for FailureForwarder {
fn description() -> &'static str {
"failure-forwarder"
}

fn set_expected_token(&mut self, tok: &'matcher Token) {
self.expected_token = Some(tok);
}

fn get_expected_token(&self) -> Option<&'matcher Token> {
self.expected_token
}
}

pub(super) fn emit_frag_parse_err(
Expand Down Expand Up @@ -320,9 +336,19 @@ pub(super) fn annotate_doc_comment(dcx: &DiagCtxt, err: &mut Diag<'_>, sm: &Sour

/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
/// other tokens, this is "unexpected token...".
pub(super) fn parse_failure_msg(tok: &Token) -> Cow<'static, str> {
match tok.kind {
token::Eof => Cow::from("unexpected end of macro invocation"),
_ => Cow::from(format!("no rules expected the token `{}`", pprust::token_to_string(tok))),
pub(super) fn parse_failure_msg(tok: &Token, expected_token: Option<&Token>) -> Cow<'static, str> {
if let Some(expected_token) = expected_token {
Cow::from(format!(
"expected `{}`, found `{}`",
pprust::token_to_string(expected_token),
pprust::token_to_string(tok),
))
} else {
match tok.kind {
token::Eof => Cow::from("unexpected end of macro invocation"),
_ => {
Cow::from(format!("no rules expected the token `{}`", pprust::token_to_string(tok)))
}
}
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_expand/src/mbe/macro_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,8 @@ impl TtParser {
// The separator matches the current token. Advance past it.
mp.idx += 1;
self.next_mps.push(mp);
} else {
track.set_expected_token(separator);
}
}
&MatcherLoc::SequenceKleeneOpAfterSep { idx_first } => {
Expand Down Expand Up @@ -632,6 +634,7 @@ impl TtParser {
parser.approx_token_stream_pos(),
track,
);

if let Some(res) = res {
return res;
}
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ pub(super) trait Tracker<'matcher> {
fn recovery() -> Recovery {
Recovery::Forbidden
}

fn set_expected_token(&mut self, _tok: &'matcher Token) {}
fn get_expected_token(&self) -> Option<&'matcher Token> {
None
}
}

/// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to
Expand Down Expand Up @@ -447,16 +452,14 @@ pub fn compile_declarative_macro(
// For this we need to reclone the macro body as the previous parser consumed it.
let retry_parser = create_parser();

let parse_result = tt_parser.parse_tt(
&mut Cow::Owned(retry_parser),
&argument_gram,
&mut diagnostics::FailureForwarder,
);
let mut track = diagnostics::FailureForwarder::new();
let parse_result =
tt_parser.parse_tt(&mut Cow::Owned(retry_parser), &argument_gram, &mut track);
let Failure((token, _, msg)) = parse_result else {
unreachable!("matcher returned something other than Failure after retry");
};

let s = parse_failure_msg(&token);
let s = parse_failure_msg(&token, track.get_expected_token());
let sp = token.span.substitute_dummy(def.span);
let mut err = sess.dcx().struct_span_err(sp, s);
err.span_label(sp, msg);
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/macros/missing-semi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[allow(unused_macros)]
macro_rules! foo {
() => {

}
() => {
//~^ ERROR expected `;`, found `(`
}
}

fn main() {}
8 changes: 8 additions & 0 deletions tests/ui/macros/missing-semi.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: expected `;`, found `(`
--> $DIR/missing-semi.rs:6:5
|
LL | () => {
| ^ no rules expected this token in macro call

error: aborting due to 1 previous error

0 comments on commit 685a80f

Please sign in to comment.