rust-lang/rust-analyzer · error
expected ident
Error message
expected ident
What it means
The ungrammar parser's `node` function throws `expected ident` when the token that should start a node definition (the left-hand side of `Node = Rule`) is not an identifier token. Node definitions in a `.ungram` grammar must begin with an identifier followed by `=`. Any other token (operators, punctuation, EOF) reaching this position fails the grammar's structural invariant that a node name comes first.
Source
Thrown at lib/ungrammar/src/parser.rs:92
grammar.nodes.push(NodeData { name, rule: DUMMY_RULE });
Node(len)
})
}
fn intern_token(&mut self, name: String) -> Token {
let len = self.token_table.len();
let grammar = &mut self.grammar;
*self.token_table.entry(name.clone()).or_insert_with(|| {
grammar.tokens.push(TokenData { name });
Token(len)
})
}
}
fn node(p: &mut Parser) -> Result<()> {
let token = p.bump()?;
let node = match token.kind {
TokenKind::Node(it) => p.intern_node(it),
_ => bail!(token.loc, "expected ident"),
};
p.expect(TokenKind::Eq, "=")?;
if !matches!(p.grammar[node].rule, DUMMY_RULE) {
bail!(token.loc, "duplicate rule: `{}`", p.grammar[node].name)
}
let rule = rule(p)?;
p.grammar.nodes[node.0].rule = rule;
Ok(())
}
fn rule(p: &mut Parser) -> Result<Rule> {
if let Some(lexer::Token { kind: TokenKind::Pipe, loc }) = p.peek() {
bail!(
*loc,
"The first element in a sequence of productions or alternatives \
must not have a leading pipe (`|`)"
);View on GitHub (pinned to e8f7e90aa3)
Solutions
- Open the .ungram file at the reported token location and make sure the line is of the form `NodeName = rule` with a valid identifier before `=`.
- Remove or quote any stray punctuation/tokens that are not part of a rule body.
- If the file was truncated or merged badly, restore the missing node name.
- Validate the grammar with a quick parse (e.g. rust-analyzer's codegen or ungrammar CLI) before committing.
Example fix
// before (.ungram) = RecordField // after (.ungram) RecordField = 'Name' ':' SyntaxToken
Defensive patterns
Strategy: validation
Validate before calling
// before parsing, sanity-check the grammar text
fn first_node_token_ok(src: &str) -> bool {
src.trim_start().chars().next().map_or(false, |c| c.is_ascii_alphabetic() || c == '_')
} Type guard
fn is_ident(s: &str) -> bool {
let mut cs = s.chars();
cs.next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_') && cs.all(|c| c.is_ascii_alphanumeric() || c == '_')
} Try / catch
match ungrammar::parse(src) {
Ok(g) => g,
Err(e) if e.to_string().contains("expected ident") => {
eprintln!("grammar error: {}", e);
return Err( GrammarError::Syntax(e));
}
Err(e) => return Err(e.into()),
} Prevention
- Keep every top-level definition in the form `Name = rule` with a valid identifier first.
- Run the grammar through the parser in CI (codegen check) before committing .ungram edits.
- Avoid editing grammar files with tools that may mangle or truncate identifiers.
When it happens
Trigger: Calling `parse` on a `.ungram` file where a token at a node-definition position is not an identifier: e.g. a grammar starting with `= Foo`, a stray token like `|` or `(` where a node name is expected, or a truncated file where the lexer emitted a non-ident token.
Common situations: Hand-edited ungrammar files with typos before `=`, copy-pasted snippets losing the node name, accidentally deleted identifier leaving punctuation behind, or a mis-ordered file where content precedes the first node declaration.
Related errors
- unexpected token, expected `{}`
- Undefined node: {}
- duplicate rule: `{}`
- The first element in a sequence of productions or alternativ
- unexpected token
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/bf7f036045d60c63.
Report an issue: GitHub.