Skip to content

Commit 661b30a

Browse files
committed
fix: better error cases with bad/missing identifiers in MBEs
1 parent 503e129 commit 661b30a

14 files changed

+120
-55
lines changed

Diff for: compiler/rustc_parse/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ parse_macro_name_remove_bang = macro names aren't followed by a `!`
472472
parse_macro_rules_missing_bang = expected `!` after `macro_rules`
473473
.suggestion = add a `!`
474474
475+
parse_macro_rules_named_macro_rules = user-defined macros may not be named `macro_rules`
476+
475477
parse_macro_rules_visibility = can't qualify macro_rules invocation with `{$vis}`
476478
.suggestion = try exporting the macro
477479
@@ -501,6 +503,8 @@ parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of
501503
502504
parse_maybe_missing_let = you might have meant to continue the let-chain
503505
506+
parse_maybe_missing_macro_rules_name = maybe you have forgotten to define a name for this `macro_rules!`
507+
504508
parse_maybe_recover_from_bad_qpath_stage_2 =
505509
missing angle brackets in associated item path
506510
.suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths

Diff for: compiler/rustc_parse/src/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2671,6 +2671,13 @@ pub(crate) struct MacroRulesMissingBang {
26712671
pub hi: Span,
26722672
}
26732673

2674+
#[derive(Diagnostic)]
2675+
#[diag(parse_macro_rules_named_macro_rules)]
2676+
pub(crate) struct MacroRulesNamedMacroRules {
2677+
#[primary_span]
2678+
pub span: Span,
2679+
}
2680+
26742681
#[derive(Diagnostic)]
26752682
#[diag(parse_macro_name_remove_bang)]
26762683
pub(crate) struct MacroNameRemoveBang {

Diff for: compiler/rustc_parse/src/parser/item.rs

+52-14
Original file line numberDiff line numberDiff line change
@@ -2040,23 +2040,26 @@ impl<'a> Parser<'a> {
20402040

20412041
/// Is this a possibly malformed start of a `macro_rules! foo` item definition?
20422042
fn is_macro_rules_item(&mut self) -> IsMacroRulesItem {
2043-
if self.check_keyword(kw::MacroRules) {
2044-
let macro_rules_span = self.token.span;
2045-
2046-
if self.look_ahead(1, |t| *t == token::Not) && self.look_ahead(2, |t| t.is_ident()) {
2047-
return IsMacroRulesItem::Yes { has_bang: true };
2048-
} else if self.look_ahead(1, |t| (t.is_ident())) {
2049-
// macro_rules foo
2050-
self.sess.emit_err(errors::MacroRulesMissingBang {
2051-
span: macro_rules_span,
2052-
hi: macro_rules_span.shrink_to_hi(),
2053-
});
2043+
if !self.check_keyword(kw::MacroRules) {
2044+
return IsMacroRulesItem::No;
2045+
}
2046+
2047+
let macro_rules_span = self.token.span;
2048+
let has_bang = self.look_ahead(1, |t| *t == token::Not);
20542049

2055-
return IsMacroRulesItem::Yes { has_bang: false };
2050+
// macro_rules foo
2051+
if !has_bang {
2052+
if !self.look_ahead(1, |t| (t.is_ident())) {
2053+
return IsMacroRulesItem::No;
20562054
}
2055+
2056+
self.sess.emit_err(errors::MacroRulesMissingBang {
2057+
span: macro_rules_span,
2058+
hi: macro_rules_span.shrink_to_hi(),
2059+
});
20572060
}
20582061

2059-
IsMacroRulesItem::No
2062+
IsMacroRulesItem::Yes { has_bang }
20602063
}
20612064

20622065
/// Parses a `macro_rules! foo { ... }` declarative macro.
@@ -2070,7 +2073,42 @@ impl<'a> Parser<'a> {
20702073
if has_bang {
20712074
self.expect(&token::Not)?; // `!`
20722075
}
2073-
let ident = self.parse_ident()?;
2076+
2077+
let ident = match self.parse_ident() {
2078+
Ok(ident) => ident,
2079+
Err(mut err) => {
2080+
match (
2081+
&self.token.kind,
2082+
self.look_ahead(1, |token| token.ident()),
2083+
self.look_ahead(2, |token| {
2084+
(token.kind == TokenKind::CloseDelim(Delimiter::Parenthesis))
2085+
.then_some(token.span)
2086+
}),
2087+
) {
2088+
(
2089+
TokenKind::OpenDelim(Delimiter::Parenthesis),
2090+
Some((Ident { name, .. }, _)),
2091+
Some(closing_span),
2092+
) => {
2093+
err.span_suggestion(
2094+
self.token.span.with_hi(closing_span.hi()),
2095+
"try removing the parenthesis around the name for this `macro_rules!`",
2096+
name,
2097+
Applicability::MachineApplicable,
2098+
);
2099+
}
2100+
_ => {
2101+
err.help(fluent::parse_maybe_missing_macro_rules_name);
2102+
}
2103+
}
2104+
2105+
return Err(err);
2106+
}
2107+
};
2108+
2109+
if ident.name == sym::macro_rules {
2110+
self.sess.emit_err(errors::MacroRulesNamedMacroRules { span: ident.span });
2111+
}
20742112

20752113
if self.eat(&token::Not) {
20762114
// Handle macro_rules! foo!

Diff for: compiler/rustc_resolve/messages.ftl

-2
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,6 @@ resolve_method_not_member_of_trait =
181181
method `{$method}` is not a member of trait `{$trait_}`
182182
.label = not a member of trait `{$trait_}`
183183
184-
resolve_missing_macro_rules_name = maybe you have forgotten to define a name for this `macro_rules!`
185-
186184
resolve_module_only =
187185
visibility must resolve to a module
188186

Diff for: compiler/rustc_resolve/src/diagnostics.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use rustc_span::{BytePos, Span, SyntaxContext};
2828
use thin_vec::{thin_vec, ThinVec};
2929

3030
use crate::errors::{AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion};
31-
use crate::errors::{ConsiderAddingADerive, ExplicitUnsafeTraits, MaybeMissingMacroRulesName};
31+
use crate::errors::{ConsiderAddingADerive, ExplicitUnsafeTraits};
3232
use crate::imports::{Import, ImportKind};
3333
use crate::late::{PatternSource, Rib};
3434
use crate::path_names_to_string;
@@ -1419,11 +1419,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
14191419
"",
14201420
);
14211421

1422-
if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {
1423-
err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span });
1424-
return;
1425-
}
1426-
14271422
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
14281423
err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });
14291424
return;

Diff for: compiler/rustc_resolve/src/errors.rs

-7
Original file line numberDiff line numberDiff line change
@@ -665,13 +665,6 @@ pub(crate) struct ExplicitUnsafeTraits {
665665
pub(crate) ident: Ident,
666666
}
667667

668-
#[derive(Subdiagnostic)]
669-
#[note(resolve_missing_macro_rules_name)]
670-
pub(crate) struct MaybeMissingMacroRulesName {
671-
#[primary_span]
672-
pub(crate) span: Span,
673-
}
674-
675668
#[derive(Subdiagnostic)]
676669
#[help(resolve_added_macro_use)]
677670
pub(crate) struct AddedMacroUse;

Diff for: tests/ui/macros/mbe-missing-ident-error.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Ensures MBEs with a missing ident produce a readable error
2+
3+
macro_rules! {
4+
//~^ ERROR: expected identifier, found `{`
5+
//~| HELP: maybe you have forgotten to define a name for this `macro_rules!`
6+
() => {}
7+
}

Diff for: tests/ui/macros/mbe-missing-ident-error.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: expected identifier, found `{`
2+
--> $DIR/mbe-missing-ident-error.rs:3:14
3+
|
4+
LL | macro_rules! {
5+
| ^ expected identifier
6+
|
7+
= help: maybe you have forgotten to define a name for this `macro_rules!`
8+
9+
error: aborting due to 1 previous error
10+

Diff for: tests/ui/macros/mbe-parenthesis-ident-error.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Ensures MBEs with a invalid ident produce a readable error
2+
3+
macro_rules! (meepmeep) {
4+
//~^ ERROR: expected identifier, found `(`
5+
//~| HELP: try removing the parenthesis around the name for this `macro_rules!`
6+
() => {}
7+
}

Diff for: tests/ui/macros/mbe-parenthesis-ident-error.stderr

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: expected identifier, found `(`
2+
--> $DIR/mbe-parenthesis-ident-error.rs:3:14
3+
|
4+
LL | macro_rules! (meepmeep) {
5+
| ^ expected identifier
6+
|
7+
help: try removing the parenthesis around the name for this `macro_rules!`
8+
|
9+
LL | macro_rules! meepmeep {
10+
| ~~~~~~~~
11+
12+
error: aborting due to 1 previous error
13+

Diff for: tests/ui/macros/user-defined-macro-rules.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
// check-pass
1+
// check-fail
22

3-
macro_rules! macro_rules { () => { struct S; } } // OK
3+
macro_rules! macro_rules { () => {} } //~ ERROR: user-defined macros may not be named `macro_rules`
44

5-
macro_rules! {} // OK, calls the macro defined above
6-
7-
fn main() {
8-
let s = S;
9-
}
5+
macro_rules! {} //~ ERROR: expected identifier, found `{`

Diff for: tests/ui/macros/user-defined-macro-rules.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: user-defined macros may not be named `macro_rules`
2+
--> $DIR/user-defined-macro-rules.rs:3:14
3+
|
4+
LL | macro_rules! macro_rules { () => {} }
5+
| ^^^^^^^^^^^
6+
7+
error: expected identifier, found `{`
8+
--> $DIR/user-defined-macro-rules.rs:5:14
9+
|
10+
LL | macro_rules! {}
11+
| ^ expected identifier
12+
|
13+
= note: maybe you have forgotten to define a name for this `macro_rules!`
14+
15+
error: aborting due to 2 previous errors
16+

Diff for: tests/ui/resolve/issue-118295.rs

-5
This file was deleted.

Diff for: tests/ui/resolve/issue-118295.stderr

-14
This file was deleted.

0 commit comments

Comments
 (0)