Skip to content

Commit 26f0246

Browse files
committed
Imporve error message: missing ; in macro_rules
1 parent 9e7aff7 commit 26f0246

File tree

5 files changed

+100
-37
lines changed

5 files changed

+100
-37
lines changed

compiler/rustc_expand/src/mbe/diagnostics.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub(super) fn failed_to_match_macro<'cx>(
5555

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

58-
let mut err = cx.dcx().struct_span_err(span, parse_failure_msg(&token));
58+
let mut err = cx.dcx().struct_span_err(span, parse_failure_msg(&token, &None));
5959
err.span_label(span, label);
6060
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
6161
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
@@ -134,7 +134,12 @@ impl BestFailure {
134134
impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
135135
type Failure = (Token, usize, &'static str);
136136

137-
fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure {
137+
fn build_failure(
138+
tok: Token,
139+
_: Option<Token>,
140+
position: usize,
141+
msg: &'static str,
142+
) -> Self::Failure {
138143
(tok, position, msg)
139144
}
140145

@@ -203,10 +208,15 @@ impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
203208
pub struct FailureForwarder;
204209

205210
impl<'matcher> Tracker<'matcher> for FailureForwarder {
206-
type Failure = (Token, usize, &'static str);
207-
208-
fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure {
209-
(tok, position, msg)
211+
type Failure = (Token, Option<Token>, usize, &'static str);
212+
213+
fn build_failure(
214+
tok: Token,
215+
expect_token: Option<Token>,
216+
position: usize,
217+
msg: &'static str,
218+
) -> Self::Failure {
219+
(tok, expect_token, position, msg)
210220
}
211221

212222
fn description() -> &'static str {
@@ -320,9 +330,19 @@ pub(super) fn annotate_doc_comment(dcx: &DiagCtxt, err: &mut Diag<'_>, sm: &Sour
320330

321331
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
322332
/// other tokens, this is "unexpected token...".
323-
pub(super) fn parse_failure_msg(tok: &Token) -> Cow<'static, str> {
324-
match tok.kind {
325-
token::Eof => Cow::from("unexpected end of macro invocation"),
326-
_ => Cow::from(format!("no rules expected the token `{}`", pprust::token_to_string(tok))),
333+
pub(super) fn parse_failure_msg(tok: &Token, expect_tok: &Option<Token>) -> Cow<'static, str> {
334+
if let Some(expect_tok) = expect_tok {
335+
Cow::from(format!(
336+
"expected `{}`, found `{}`",
337+
pprust::token_to_string(expect_tok),
338+
pprust::token_to_string(tok),
339+
))
340+
} else {
341+
match tok.kind {
342+
token::Eof => Cow::from("unexpected end of macro invocation"),
343+
_ => {
344+
Cow::from(format!("no rules expected the token `{}`", pprust::token_to_string(tok)))
345+
}
346+
}
327347
}
328348
}

compiler/rustc_expand/src/mbe/macro_parser.rs

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -454,10 +454,11 @@ impl TtParser {
454454
token: &Token,
455455
approx_position: usize,
456456
track: &mut T,
457-
) -> Option<NamedParseResult<T::Failure>> {
457+
) -> (Option<NamedParseResult<T::Failure>>, Option<Token>) {
458458
// Matcher positions that would be valid if the macro invocation was over now. Only
459459
// modified if `token == Eof`.
460460
let mut eof_mps = EofMatcherPositions::None;
461+
let mut expected_token = None;
461462

462463
while let Some(mut mp) = self.cur_mps.pop() {
463464
let matcher_loc = &matcher[mp.idx];
@@ -541,6 +542,8 @@ impl TtParser {
541542
// The separator matches the current token. Advance past it.
542543
mp.idx += 1;
543544
self.next_mps.push(mp);
545+
} else {
546+
expected_token = Some(separator.clone());
544547
}
545548
}
546549
&MatcherLoc::SequenceKleeneOpAfterSep { idx_first } => {
@@ -560,7 +563,7 @@ impl TtParser {
560563
} else {
561564
// E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
562565
// Both this check and the one in `nameize` are necessary, surprisingly.
563-
return Some(Error(span, "missing fragment specifier".to_string()));
566+
return (Some(Error(span, "missing fragment specifier".to_string())), None);
564567
}
565568
}
566569
MatcherLoc::Eof => {
@@ -581,27 +584,35 @@ impl TtParser {
581584
// If we reached the end of input, check that there is EXACTLY ONE possible matcher.
582585
// Otherwise, either the parse is ambiguous (which is an error) or there is a syntax error.
583586
if *token == token::Eof {
584-
Some(match eof_mps {
585-
EofMatcherPositions::One(mut eof_mp) => {
586-
// Need to take ownership of the matches from within the `Rc`.
587-
Rc::make_mut(&mut eof_mp.matches);
588-
let matches = Rc::try_unwrap(eof_mp.matches).unwrap().into_iter();
589-
self.nameize(matcher, matches)
590-
}
591-
EofMatcherPositions::Multiple => {
592-
Error(token.span, "ambiguity: multiple successful parses".to_string())
593-
}
594-
EofMatcherPositions::None => Failure(T::build_failure(
595-
Token::new(
596-
token::Eof,
597-
if token.span.is_dummy() { token.span } else { token.span.shrink_to_hi() },
598-
),
599-
approx_position,
600-
"missing tokens in macro arguments",
601-
)),
602-
})
587+
(
588+
Some(match eof_mps {
589+
EofMatcherPositions::One(mut eof_mp) => {
590+
// Need to take ownership of the matches from within the `Rc`.
591+
Rc::make_mut(&mut eof_mp.matches);
592+
let matches = Rc::try_unwrap(eof_mp.matches).unwrap().into_iter();
593+
self.nameize(matcher, matches)
594+
}
595+
EofMatcherPositions::Multiple => {
596+
Error(token.span, "ambiguity: multiple successful parses".to_string())
597+
}
598+
EofMatcherPositions::None => Failure(T::build_failure(
599+
Token::new(
600+
token::Eof,
601+
if token.span.is_dummy() {
602+
token.span
603+
} else {
604+
token.span.shrink_to_hi()
605+
},
606+
),
607+
None,
608+
approx_position,
609+
"missing tokens in macro arguments",
610+
)),
611+
}),
612+
None,
613+
)
603614
} else {
604-
None
615+
(None, expected_token)
605616
}
606617
}
607618

@@ -626,12 +637,13 @@ impl TtParser {
626637

627638
// Process `cur_mps` until either we have finished the input or we need to get some
628639
// parsing from the black-box parser done.
629-
let res = self.parse_tt_inner(
640+
let (res, expected_token) = self.parse_tt_inner(
630641
matcher,
631642
&parser.token,
632643
parser.approx_token_stream_pos(),
633644
track,
634645
);
646+
635647
if let Some(res) = res {
636648
return res;
637649
}
@@ -646,6 +658,7 @@ impl TtParser {
646658
// parser: syntax error.
647659
return Failure(T::build_failure(
648660
parser.token.clone(),
661+
expected_token,
649662
parser.approx_token_stream_pos(),
650663
"no rules expected this token in macro call",
651664
));

compiler/rustc_expand/src/mbe/macro_rules.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,12 @@ pub(super) trait Tracker<'matcher> {
152152
/// Arm failed to match. If the token is `token::Eof`, it indicates an unexpected
153153
/// end of macro invocation. Otherwise, it indicates that no rules expected the given token.
154154
/// The usize is the approximate position of the token in the input token stream.
155-
fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure;
155+
fn build_failure(
156+
tok: Token,
157+
expect_tok: Option<Token>,
158+
position: usize,
159+
msg: &'static str,
160+
) -> Self::Failure;
156161

157162
/// This is called before trying to match next MatcherLoc on the current token.
158163
fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {}
@@ -176,7 +181,13 @@ pub(super) struct NoopTracker;
176181
impl<'matcher> Tracker<'matcher> for NoopTracker {
177182
type Failure = ();
178183

179-
fn build_failure(_tok: Token, _position: usize, _msg: &'static str) -> Self::Failure {}
184+
fn build_failure(
185+
_tok: Token,
186+
_expect_tok: Option<Token>,
187+
_position: usize,
188+
_msg: &'static str,
189+
) -> Self::Failure {
190+
}
180191

181192
fn description() -> &'static str {
182193
"none"
@@ -452,11 +463,11 @@ pub fn compile_declarative_macro(
452463
&argument_gram,
453464
&mut diagnostics::FailureForwarder,
454465
);
455-
let Failure((token, _, msg)) = parse_result else {
466+
let Failure((token, expect_tok, _, msg)) = parse_result else {
456467
unreachable!("matcher returned something other than Failure after retry");
457468
};
458469

459-
let s = parse_failure_msg(&token);
470+
let s = parse_failure_msg(&token, &expect_tok);
460471
let sp = token.span.substitute_dummy(def.span);
461472
let mut err = sess.dcx().struct_span_err(sp, s);
462473
err.span_label(sp, msg);

tests/ui/macros/missing-semi.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#[allow(unused_macros)]
2+
macro_rules! foo {
3+
() => {
4+
5+
}
6+
() => { //~ERROR expected `;`, found `(`
7+
8+
}
9+
}
10+
11+
fn main() {}

tests/ui/macros/missing-semi.stderr

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected `;`, found `(`
2+
--> $DIR/missing-semi.rs:6:5
3+
|
4+
LL | () => {
5+
| ^ no rules expected this token in macro call
6+
7+
error: aborting due to 1 previous error
8+

0 commit comments

Comments
 (0)