Repository: rust-lang/rust
File: compiler/rustc_parse/src/parser/item.rs (Rust)

Code before:
        }
    }

    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, path);
        }
    }

    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,
            "macros that expand to items must be delimited with braces or followed by a semicolon",
        );
        // FIXME: This will make us not emit the help even for declarative
        // macros within the same crate (that we can fix), which is sad.
        if !span.from_expansion() {
            let DelimSpan { open, close } = args.dspan;
            let is_macro_rules = path.is_some_and(|p| {
                p.segments.first().is_some_and(|seg| seg.ident.name == sym::macro_rules)
            });
            // Check if this looks like `macro_rules!(name) { ... }`
            // a common mistake when trying to define a macro.
            if let Some(path) = path
                && is_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();
    }

Review comment (suggestion):
```suggestion
                && path.segments.first().is_some_and(|seg| seg.ident.name == sym::macro_rules)
```

Code after:

    /// Is this a possibly malformed start of a `macro_rules! foo` item definition?
    fn is_macro_rules_item(&mut self) -> IsMacroRulesItem {
        if self.check_keyword(exp!(MacroRules)) {
            let macro_rules_span = self.token.span;

            if self.look_ahead(1, |t| *t == token::Bang) && self.look_ahead(2, |t| t.is_ident()) {
                return IsMacroRulesItem::Yes { has_bang: true };
            } else if self.look_ahead(1, |t| t.is_ident()) {
                // macro_rules foo
                self.dcx().emit_err(errors::MacroRulesMissingBang {
                    span: macro_rules_span,
                    hi: macro_rules_span.shrink_to_hi(),
                });

                return IsMacroRulesItem::Yes { has_bang: false };
            }
        }

        IsMacroRulesItem::No
    }

    /// Parses a `macro_rules! foo { ... }` declarative macro.
    fn parse_item_macro_rules(
        &mut self,
        vis: &Visibility,
        has_bang: bool,
    ) -> PResult<'a, ItemKind> {
        self.expect_keyword(exp!(MacroRules))?; // `macro_rules`

        if has_bang {
            self.expect(exp!(Bang))?; // `!`
        }
        let ident = self.parse_ident()?;

        if self.eat(exp!(Bang)) {
            // Handle macro_rules! foo!
            let span = self.prev_token.span;
            self.dcx().emit_err(errors::MacroNameRemoveBang { span });
        }

        let body = self.parse_delim_args()?;
        self.eat_semi_for_macro_if_needed(&body, None);
        self.complain_if_pub_macro(vis, true);

        Ok(ItemKind::MacroDef(
            ident,
            ast::MacroDef { body, macro_rules: true, eii_declaration: None },
        ))
    }