Improve error message: missing ;
in macro_rules
This commit is contained in:
parent
9e7aff7945
commit
c2be1342b7
@ -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");
|
||||
@ -200,9 +200,17 @@ fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self {
|
||||
}
|
||||
|
||||
/// 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> Tracker<'matcher> for FailureForwarder {
|
||||
impl<'matcher> FailureForwarder<'matcher> {
|
||||
pub fn new() -> Self {
|
||||
Self { expected_token: None }
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -212,6 +220,14 @@ fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failur
|
||||
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(
|
||||
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -541,6 +541,8 @@ fn parse_tt_inner<'matcher, T: Tracker<'matcher>>(
|
||||
// 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 } => {
|
||||
@ -632,6 +634,7 @@ pub(super) fn parse_tt<'matcher, T: Tracker<'matcher>>(
|
||||
parser.approx_token_stream_pos(),
|
||||
track,
|
||||
);
|
||||
|
||||
if let Some(res) = res {
|
||||
return res;
|
||||
}
|
||||
|
@ -167,6 +167,11 @@ fn after_arm(&mut self, _result: &NamedParseResult<Self::Failure>) {}
|
||||
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
|
||||
@ -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);
|
||||
|
11
tests/ui/macros/missing-semi.rs
Normal file
11
tests/ui/macros/missing-semi.rs
Normal file
@ -0,0 +1,11 @@
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
|
||||
}
|
||||
() => {
|
||||
//~^ ERROR expected `;`, found `(`
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
8
tests/ui/macros/missing-semi.stderr
Normal file
8
tests/ui/macros/missing-semi.stderr
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user