rust-lang/rust-analyzer · error
unexpected token, expected `{}`
Error message
unexpected token, expected `{}` What it means
The ungrammar parser's `expect` bumps a token and verifies its kind matches what the grammar rule requires. On mismatch it reports the source location plus 'unexpected token, expected `<what>`', where `what` is a human description of the expected token.
Source
Thrown at lib/ungrammar/src/parser.rs:55
impl Parser {
fn new(mut tokens: Vec<lexer::Token>) -> Parser {
tokens.reverse();
Parser { tokens, ..Parser::default() }
}
fn peek(&self) -> Option<&lexer::Token> {
self.peek_n(0)
}
fn peek_n(&self, n: usize) -> Option<&lexer::Token> {
self.tokens.iter().nth_back(n)
}
fn bump(&mut self) -> Result<lexer::Token> {
self.tokens.pop().ok_or_else(|| format_err!("unexpected EOF"))
}
fn expect(&mut self, kind: TokenKind, what: &str) -> Result<()> {
let token = self.bump()?;
if token.kind != kind {
bail!(token.loc, "unexpected token, expected `{}`", what);
}
Ok(())
}
fn is_eof(&self) -> bool {
self.tokens.is_empty()
}
fn finish(self) -> Result<Grammar> {
for node_data in &self.grammar.nodes {
if matches!(node_data.rule, DUMMY_RULE) {
crate::error::bail!("Undefined node: {}", node_data.name)
}
}
Ok(self.grammar)
}
fn intern_node(&mut self, name: String) -> Node {
let len = self.node_table.len();
let grammar = &mut self.grammar;
*self.node_table.entry(name.clone()).or_insert_with(|| {View on GitHub (pinned to e8f7e90aa3)
Solutions
- Read the attached location and check the token right there against the expected one named in the message
- Add the missing `=` after the node name in a rule definition
- Balance parentheses in grouping expressions `(...)`
- Compare against a working .ungrammar (e.g. rust-analyzer's own rust.ungram) for correct syntax
Example fix
// before Expr BinaryExpr // after Expr = BinaryExpr
Defensive patterns
Strategy: try-catch
Validate before calling
// every top-level rule line should contain '=' before any rule body
fn rule_missing_equals(src: &str) -> Vec<&str> {
src.lines().filter(|l| {
let l = l.trim();
!l.is_empty() && !l.starts_with('=') && l.split_once('=')
.map(|(name, _)| name.trim().split_whitespace().count() != 1)
.unwrap_or(!l.starts_with('(') )
}).collect()
} Try / catch
match ungrammar::Grammar::parse(src) {
Err(e) if e.to_string().contains("unexpected token, expected") => {
eprintln!("syntax error in grammar: {e}")
}
r => r?,
} Prevention
- Always write `NodeName = rule` with the equals sign
- Keep parentheses balanced when editing rules
- Diff grammar changes against rust-analyzer's rust.ungram for style
When it happens
Trigger: A `.ungrammar` file violates rule syntax: e.g. missing `=` after a rule name (`Foo bar`), an unbalanced `)` without `(`, or a token appearing where `=`, `|`, `(` etc. is required.
Common situations: Hand-written grammar rules with typos; forgetting `=` between node name and rule; editing rules and deleting a required separator.
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
- unclosed token literal
- unexpected character: `{}`
- The first element in a sequence of productions or alternativ
- unexpected token
- invalid escape in token literal
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/d6b7198ba8299e87.
Report an issue: GitHub.