Skip to content

Commit 16bc545

Browse files
committed
Improve code suggestion for incorrect macro_rules! usage
1 parent f57eac1 commit 16bc545

File tree

6 files changed

+94
-29
lines changed

6 files changed

+94
-29
lines changed

compiler/rustc_parse/src/parser/item.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ impl<'a> Parser<'a> {
492492
match self.parse_delim_args() {
493493
// `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
494494
Ok(args) => {
495-
self.eat_semi_for_macro_if_needed(&args);
495+
self.eat_semi_for_macro_if_needed(Some(&path), &args);
496496
self.complain_if_pub_macro(vis, false);
497497
Ok(MacCall { path, args })
498498
}
@@ -2328,7 +2328,7 @@ impl<'a> Parser<'a> {
23282328
}
23292329

23302330
let body = self.parse_delim_args()?;
2331-
self.eat_semi_for_macro_if_needed(&body);
2331+
self.eat_semi_for_macro_if_needed(None, &body);
23322332
self.complain_if_pub_macro(vis, true);
23332333

23342334
Ok(ItemKind::MacroDef(
@@ -2353,13 +2353,13 @@ impl<'a> Parser<'a> {
23532353
}
23542354
}
23552355

2356-
fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs) {
2356+
fn eat_semi_for_macro_if_needed(&mut self, path: Option<&ast::Path>, args: &DelimArgs) {
23572357
if args.need_semicolon() && !self.eat(exp!(Semi)) {
2358-
self.report_invalid_macro_expansion_item(args);
2358+
self.report_invalid_macro_expansion_item(path, args);
23592359
}
23602360
}
23612361

2362-
fn report_invalid_macro_expansion_item(&self, args: &DelimArgs) {
2362+
fn report_invalid_macro_expansion_item(&self, path: Option<&ast::Path>, args: &DelimArgs) {
23632363
let span = args.dspan.entire();
23642364
let mut err = self.dcx().struct_span_err(
23652365
span,
@@ -2369,17 +2369,35 @@ impl<'a> Parser<'a> {
23692369
// macros within the same crate (that we can fix), which is sad.
23702370
if !span.from_expansion() {
23712371
let DelimSpan { open, close } = args.dspan;
2372-
err.multipart_suggestion(
2373-
"change the delimiters to curly braces",
2374-
vec![(open, "{".to_string()), (close, '}'.to_string())],
2375-
Applicability::MaybeIncorrect,
2376-
);
2377-
err.span_suggestion(
2378-
span.with_neighbor(self.token.span).shrink_to_hi(),
2379-
"add a semicolon",
2380-
';',
2381-
Applicability::MaybeIncorrect,
2382-
);
2372+
2373+
// Special-case a common mistake when trying to define a macro:
2374+
// `macro_rules! name { .. }` is a macro definition, but users sometimes write
2375+
// `macro_rules!(name) { .. }`, which is parsed as a macro *invocation* followed by a block.
2376+
let is_macro_rules = path
2377+
.and_then(|p| p.segments.first())
2378+
.map(|seg| seg.ident.name == sym::macro_rules)
2379+
.unwrap_or(false);
2380+
let is_parens = args.delim == Delimiter::Parenthesis;
2381+
let followed_by_block = self.token == token::OpenBrace;
2382+
if is_macro_rules && is_parens && followed_by_block {
2383+
err.multipart_suggestion(
2384+
"to define a macro, remove the parentheses around the macro name",
2385+
vec![(open, " ".to_string()), (close, String::new())],
2386+
Applicability::MachineApplicable,
2387+
);
2388+
} else {
2389+
err.multipart_suggestion(
2390+
"change the delimiters to curly braces",
2391+
vec![(open, "{".to_string()), (close, '}'.to_string())],
2392+
Applicability::MaybeIncorrect,
2393+
);
2394+
err.span_suggestion(
2395+
span.with_neighbor(self.token.span).shrink_to_hi(),
2396+
"add a semicolon",
2397+
';',
2398+
Applicability::MaybeIncorrect,
2399+
);
2400+
}
23832401
}
23842402
err.emit();
23852403
}

tests/ui/macros/issue-118786.fixed

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![allow(unused_macros)]
2+
//@ compile-flags: --crate-type lib -O -C debug-assertions=yes
3+
//@ dont-require-annotations: NOTE
4+
//@ run-rustfix
5+
6+
// Regression test for issue 118786
7+
8+
macro_rules! make_macro {
9+
($macro_name:tt) => {
10+
macro_rules! $macro_name {
11+
//~^ ERROR macro expansion ignores `{` and any tokens following
12+
//~| ERROR cannot find macro `macro_rules` in this scope
13+
//~| NOTE put a macro name here
14+
() => {}
15+
}
16+
}
17+
}
18+
19+
make_macro!( meow);
20+
//~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon

tests/ui/macros/issue-118786.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
#![allow(unused_macros)]
12
//@ compile-flags: --crate-type lib -O -C debug-assertions=yes
23
//@ dont-require-annotations: NOTE
4+
//@ run-rustfix
35

46
// Regression test for issue 118786
57

68
macro_rules! make_macro {
79
($macro_name:tt) => {
810
macro_rules! $macro_name {
9-
//~^ ERROR macro expansion ignores `{` and any tokens following
10-
//~| ERROR cannot find macro `macro_rules` in this scope
11-
//~| NOTE put a macro name here
11+
//~^ ERROR macro expansion ignores `{` and any tokens following
12+
//~| ERROR cannot find macro `macro_rules` in this scope
13+
//~| NOTE put a macro name here
1214
() => {}
1315
}
1416
}

tests/ui/macros/issue-118786.stderr

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
error: macros that expand to items must be delimited with braces or followed by a semicolon
2-
--> $DIR/issue-118786.rs:17:13
2+
--> $DIR/issue-118786.rs:19:13
33
|
44
LL | make_macro!((meow));
55
| ^^^^^^
66
|
7-
help: change the delimiters to curly braces
7+
help: to define a macro, remove the parentheses around the macro name
88
|
99
LL - make_macro!((meow));
10-
LL + make_macro!({meow});
10+
LL + make_macro!( meow);
1111
|
12-
help: add a semicolon
13-
|
14-
LL | macro_rules! $macro_name; {
15-
| +
1612

1713
error: macro expansion ignores `{` and any tokens following
18-
--> $DIR/issue-118786.rs:8:34
14+
--> $DIR/issue-118786.rs:10:34
1915
|
2016
LL | macro_rules! $macro_name {
2117
| ^
@@ -26,7 +22,7 @@ LL | make_macro!((meow));
2622
= note: the usage of `make_macro!` is likely invalid in item context
2723

2824
error: cannot find macro `macro_rules` in this scope
29-
--> $DIR/issue-118786.rs:8:9
25+
--> $DIR/issue-118786.rs:10:9
3026
|
3127
LL | macro_rules! $macro_name {
3228
| ^^^^^^^^^^^
@@ -35,7 +31,7 @@ LL | make_macro!((meow));
3531
| ------------------- in this macro invocation
3632
|
3733
note: maybe you have forgotten to define a name for this `macro_rules!`
38-
--> $DIR/issue-118786.rs:8:20
34+
--> $DIR/issue-118786.rs:10:20
3935
|
4036
LL | macro_rules! $macro_name {
4137
| ^ put a macro name here
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
macro_rules!(i_think_the_name_should_go_here) {
2+
//~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon
3+
//~| ERROR expected item, found `{`
4+
() => {}
5+
}
6+
7+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: macros that expand to items must be delimited with braces or followed by a semicolon
2+
--> $DIR/macro-rules-paren-name-issue-150899.rs:1:13
3+
|
4+
LL | macro_rules!(i_think_the_name_should_go_here) {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
help: to define a macro, remove the parentheses around the macro name
8+
|
9+
LL - macro_rules!(i_think_the_name_should_go_here) {
10+
LL + macro_rules! i_think_the_name_should_go_here {
11+
|
12+
13+
error: expected item, found `{`
14+
--> $DIR/macro-rules-paren-name-issue-150899.rs:1:47
15+
|
16+
LL | macro_rules!(i_think_the_name_should_go_here) {
17+
| ^ expected item
18+
|
19+
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
20+
21+
error: aborting due to 2 previous errors
22+

0 commit comments

Comments
 (0)