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();
}