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
- Fix the syntax of the macro_rules!/macro definition itself; look for parser errors reported at the definition site.
- Check for unbalanced delimiters or stray tokens in the macro body and its match arms.
- 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.
- 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
- Keep macro_rules! bodies syntactically valid: balanced delimiters, `;` between arms.
- Check IDE/parser errors at the definition site before debugging expansion output.
- Compare with rustc to distinguish user error from rust-analyzer parser bugs.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Can't rename local that is defined in a macro declaration
- unmatched closing delimiter from syntax fixup
- Token from lexer must be single char: token = {token:#?}
- Next token must be ident
- Cannot rename builtin type
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/62e7e057146a0caa.
Report an issue: GitHub.