Improve code suggestion for incorrect macro_rules! usage

This commit is contained in:
yukang 2026-01-31 06:36:07 +00:00
parent 605f49b274
commit caf7cdf558
6 changed files with 89 additions and 27 deletions

View file

@ -534,7 +534,7 @@ impl<'a> Parser<'a> {
match self.parse_delim_args() {
// `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
Ok(args) => {
self.eat_semi_for_macro_if_needed(&args);
self.eat_semi_for_macro_if_needed(&args, Some(&path));
self.complain_if_pub_macro(vis, false);
Ok(MacCall { path, args })
}
@ -2392,7 +2392,7 @@ impl<'a> Parser<'a> {
}
let body = self.parse_delim_args()?;
self.eat_semi_for_macro_if_needed(&body);
self.eat_semi_for_macro_if_needed(&body, None);
self.complain_if_pub_macro(vis, true);
Ok(ItemKind::MacroDef(
@ -2417,13 +2417,13 @@ impl<'a> Parser<'a> {
}
}
fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs) {
fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs, path: Option<&Path>) {
if args.need_semicolon() && !self.eat(exp!(Semi)) {
self.report_invalid_macro_expansion_item(args);
self.report_invalid_macro_expansion_item(args, path);
}
}
fn report_invalid_macro_expansion_item(&self, args: &DelimArgs) {
fn report_invalid_macro_expansion_item(&self, args: &DelimArgs, path: Option<&Path>) {
let span = args.dspan.entire();
let mut err = self.dcx().struct_span_err(
span,
@ -2433,17 +2433,32 @@ impl<'a> Parser<'a> {
// macros within the same crate (that we can fix), which is sad.
if !span.from_expansion() {
let DelimSpan { open, close } = args.dspan;
err.multipart_suggestion(
"change the delimiters to curly braces",
vec![(open, "{".to_string()), (close, '}'.to_string())],
Applicability::MaybeIncorrect,
);
err.span_suggestion(
span.with_neighbor(self.token.span).shrink_to_hi(),
"add a semicolon",
';',
Applicability::MaybeIncorrect,
);
// Check if this looks like `macro_rules!(name) { ... }`
// a common mistake when trying to define a macro.
if let Some(path) = path
&& path.segments.first().is_some_and(|seg| seg.ident.name == sym::macro_rules)
&& args.delim == Delimiter::Parenthesis
{
let replace =
if path.span.hi() + rustc_span::BytePos(1) < open.lo() { "" } else { " " };
err.multipart_suggestion(
"to define a macro, remove the parentheses around the macro name",
vec![(open, replace.to_string()), (close, String::new())],
Applicability::MachineApplicable,
);
} else {
err.multipart_suggestion(
"change the delimiters to curly braces",
vec![(open, "{".to_string()), (close, '}'.to_string())],
Applicability::MaybeIncorrect,
);
err.span_suggestion(
span.with_neighbor(self.token.span).shrink_to_hi(),
"add a semicolon",
';',
Applicability::MaybeIncorrect,
);
}
}
err.emit();
}

View file

@ -0,0 +1,20 @@
#![allow(unused_macros)]
//@ compile-flags: --crate-type lib
//@ dont-require-annotations: NOTE
//@ run-rustfix
// Regression test for issue 118786
macro_rules! make_macro {
($macro_name:tt) => {
macro_rules! $macro_name {
//~^ ERROR macro expansion ignores `{` and any tokens following
//~| ERROR cannot find macro `macro_rules` in this scope
//~| NOTE put a macro name here
() => {}
}
}
}
make_macro!(meow);
//~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon

View file

@ -1,5 +1,7 @@
//@ compile-flags: --crate-type lib -O -C debug-assertions=yes
#![allow(unused_macros)]
//@ compile-flags: --crate-type lib
//@ dont-require-annotations: NOTE
//@ run-rustfix
// Regression test for issue 118786

View file

@ -1,21 +1,17 @@
error: macros that expand to items must be delimited with braces or followed by a semicolon
--> $DIR/issue-118786.rs:17:13
--> $DIR/issue-118786.rs:19:13
|
LL | make_macro!((meow));
| ^^^^^^
|
help: change the delimiters to curly braces
help: to define a macro, remove the parentheses around the macro name
|
LL - make_macro!((meow));
LL + make_macro!({meow});
LL + make_macro!(meow);
|
help: add a semicolon
|
LL | macro_rules! $macro_name; {
| +
error: macro expansion ignores `{` and any tokens following
--> $DIR/issue-118786.rs:8:34
--> $DIR/issue-118786.rs:10:34
|
LL | macro_rules! $macro_name {
| ^
@ -26,7 +22,7 @@ LL | make_macro!((meow));
= note: the usage of `make_macro!` is likely invalid in item context
error: cannot find macro `macro_rules` in this scope
--> $DIR/issue-118786.rs:8:9
--> $DIR/issue-118786.rs:10:9
|
LL | macro_rules! $macro_name {
| ^^^^^^^^^^^
@ -35,7 +31,7 @@ LL | make_macro!((meow));
| ------------------- in this macro invocation
|
note: maybe you have forgotten to define a name for this `macro_rules!`
--> $DIR/issue-118786.rs:8:20
--> $DIR/issue-118786.rs:10:20
|
LL | macro_rules! $macro_name {
| ^ put a macro name here

View file

@ -0,0 +1,7 @@
macro_rules!(i_think_the_name_should_go_here) {
//~^ ERROR macros that expand to items must be delimited with braces or followed by a semicolon
//~| ERROR expected item, found `{`
() => {}
}
fn main() {}

View file

@ -0,0 +1,22 @@
error: macros that expand to items must be delimited with braces or followed by a semicolon
--> $DIR/macro-rules-paren-name-issue-150899.rs:1:13
|
LL | macro_rules!(i_think_the_name_should_go_here) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: to define a macro, remove the parentheses around the macro name
|
LL - macro_rules!(i_think_the_name_should_go_here) {
LL + macro_rules! i_think_the_name_should_go_here {
|
error: expected item, found `{`
--> $DIR/macro-rules-paren-name-issue-150899.rs:1:47
|
LL | macro_rules!(i_think_the_name_should_go_here) {
| ^ expected item
|
= note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
error: aborting due to 2 previous errors