rust-lang/rust-analyzer · error · ExpandError

macro definition has parse errors

Error message

macro definition has parse errors

What it means

Declarative macro expansion (macro_rules!/macro) in hir-expand checks whether the stored macro definition body (self.mac) contains a parse error before expanding. If so, it skips expansion and returns an empty subtree with ExpandErrorKind::MacroDefinition. The caller sees 'macro definition has parse errors' at the call site span.

Source

Thrown at crates/hir-expand/src/declarative.rs:42

pub struct DeclarativeMacroExpander {
    pub mac: mbe::DeclarativeMacro,
    pub transparency: Transparency,
    edition: Edition,
}

impl DeclarativeMacroExpander {
    pub fn expand(
        &self,
        db: &dyn SourceDatabase,
        tt: &tt::TopSubtree,
        call_id: MacroCallId,
        span: Span,
    ) -> ExpandResult<(tt::TopSubtree, Option<u32>)> {
        let loc = call_id.loc(db);
        match self.mac.err() {
            Some(_) => ExpandResult::new(
                (tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), None),
                ExpandError::new(span, ExpandErrorKind::MacroDefinition),
            ),
            None => self
                .mac
                .expand(
                    db,
                    tt,
                    |s| {
                        s.ctx =
                            apply_mark(db, s.ctx, call_id.into(), self.transparency, self.edition)
                    },
                    loc.kind.call_style(),
                    span,
                )
                .map_err(Into::into),
        }
    }

    pub fn expand_unhygienic(

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Fix the syntax of the macro_rules!/macro definition itself; look for parser errors reported at the definition site.
  2. Check for unbalanced delimiters or stray tokens in the macro body and its match arms.
  3. If the definition looks valid but still errors, check for an unstable-token/edition mismatch and report a rust-analyzer parser bug with a minimal fixture.
  4. In IDE use, look at the definition location (the error is raised at the call site but the cause is at the def).

Example fix

// before
macro_rules! m {
    () => { 42 }
    (x) => { x }  // missing semicolon after first arm
}
// after
macro_rules! m {
    () => { 42 };
    (x) => { x };
}
Defensive patterns

Strategy: try-catch

Validate before calling

fn macro_def_ok(def: &MacroDef) -> bool { def.err().is_none() } // check before expanding

Try / catch

let res: ExpandResult<_> = def.expand(db, call_id, tt, span);
if let Err(err) = &res.value.1 {
    if matches!(err.kind, ExpandErrorKind::MacroDefinition) {
        // definition has parse errors: surface diagnostics at the def site, skip expansion
    }
}

Prevention

When it happens

Trigger: Invoking a declarative macro whose definition body failed to parse — e.g. `macro_rules!` with unbalanced delimiters, invalid tokens in the definition, or a definition recovered badly by the parser — then calling MacroDef::expand on it.

Common situations: Typing a macro_rules! definition with a syntax mistake (missing semicolon, unbalanced braces/parens); user code that doesn't compile but is open in the IDE, so diagnostics show macro-definition errors alongside parser errors; edition/token issues where the parser misreads the definition.

Understand the failure class

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/62e7e057146a0caa. Report an issue: GitHub.