{"record":{"id":"d6b7198ba8299e87","repo":"rust-lang/rust-analyzer","slug":"unexpected-token-expected","errorCode":null,"errorMessage":"unexpected token, expected `{}`","messagePattern":"unexpected token, expected `(.+?)`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/ungrammar/src/parser.rs","lineNumber":55,"sourceCode":"impl Parser {\n    fn new(mut tokens: Vec<lexer::Token>) -> Parser {\n        tokens.reverse();\n        Parser { tokens, ..Parser::default() }\n    }\n\n    fn peek(&self) -> Option<&lexer::Token> {\n        self.peek_n(0)\n    }\n    fn peek_n(&self, n: usize) -> Option<&lexer::Token> {\n        self.tokens.iter().nth_back(n)\n    }\n    fn bump(&mut self) -> Result<lexer::Token> {\n        self.tokens.pop().ok_or_else(|| format_err!(\"unexpected EOF\"))\n    }\n    fn expect(&mut self, kind: TokenKind, what: &str) -> Result<()> {\n        let token = self.bump()?;\n        if token.kind != kind {\n            bail!(token.loc, \"unexpected token, expected `{}`\", what);\n        }\n        Ok(())\n    }\n    fn is_eof(&self) -> bool {\n        self.tokens.is_empty()\n    }\n    fn finish(self) -> Result<Grammar> {\n        for node_data in &self.grammar.nodes {\n            if matches!(node_data.rule, DUMMY_RULE) {\n                crate::error::bail!(\"Undefined node: {}\", node_data.name)\n            }\n        }\n        Ok(self.grammar)\n    }\n    fn intern_node(&mut self, name: String) -> Node {\n        let len = self.node_table.len();\n        let grammar = &mut self.grammar;\n        *self.node_table.entry(name.clone()).or_insert_with(|| {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/lib/ungrammar/src/parser.rs#L37-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-written grammar rules with typos; forgetting `=` between node name and rule; editing rules and deleting a required separator.","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"],"exampleFix":"// before\nExpr BinaryExpr\n// after\nExpr = BinaryExpr","handlingStrategy":"try-catch","validationCode":"// every top-level rule line should contain '=' before any rule body\nfn rule_missing_equals(src: &str) -> Vec<&str> {\n    src.lines().filter(|l| {\n        let l = l.trim();\n        !l.is_empty() && !l.starts_with('=') && l.split_once('=')\n            .map(|(name, _)| name.trim().split_whitespace().count() != 1)\n            .unwrap_or(!l.starts_with('(') )\n    }).collect()\n}","typeGuard":null,"tryCatchPattern":"match ungrammar::Grammar::parse(src) {\n    Err(e) if e.to_string().contains(\"unexpected token, expected\") => {\n        eprintln!(\"syntax error in grammar: {e}\")\n    }\n    r => r?,\n}","preventionTips":["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"],"tags":["parser","ungrammar","syntax","parse-error"],"backgroundTag":"unexpected-token","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}