{"record":{"id":"80d0ee4d38c78868","repo":"rust-lang/rust-analyzer","slug":"undefined-node","errorCode":null,"errorMessage":"Undefined node: {}","messagePattern":"Undefined node: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/ungrammar/src/parser.rs","lineNumber":65,"sourceCode":"        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(|| {\n            grammar.nodes.push(NodeData { name, rule: DUMMY_RULE });\n            Node(len)\n        })\n    }\n    fn intern_token(&mut self, name: String) -> Token {\n        let len = self.token_table.len();\n        let grammar = &mut self.grammar;\n        *self.token_table.entry(name.clone()).or_insert_with(|| {\n            grammar.tokens.push(TokenData { name });\n            Token(len)","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/lib/ungrammar/src/parser.rs#L47-L83","documentation":"After parsing, `finish` walks all declared nodes and bails 'Undefined node: <name>' if any node's rule is still the DUMMY_RULE sentinel — i.e. the node was referenced (or declared) but never given a rule definition. It guarantees every node in the grammar is fully defined.","triggerScenarios":"Referencing a node name in a rule without ever defining `ThatNode = ...`; forward references that were never resolved because the defining rule is missing or misspelled (case-sensitive).","commonSituations":"Refactoring grammar names and forgetting to update one reference; typos in node names; deleting a rule that is still referenced elsewhere.","solutions":["Add the missing rule definition `NodeName = ...` for the reported node","Check spelling/case of the referenced name against its definition","Search the grammar for all usages of the name and confirm exactly one `Name =` definition exists","If the node should be a token instead, quote it ('name') rather than referencing it as a node"],"exampleFix":"// before\nExpr = BinExpr '+' BinExpr  // BinExpr never defined\n// after\nExpr = BinExpr '+' BinExpr\nBinExpr = Name","handlingStrategy":"validation","validationCode":"// check every referenced node identifier has a `Name =` definition\nfn undefined_nodes(src: &str) -> Vec<&str> {\n    let defined: std::collections::HashSet<&str> = src.lines()\n        .filter_map(|l| l.split_once('='))\n        .map(|(n, _)| n.trim())\n        .collect();\n    src.split(|c: char| !c.is_alphanumeric() && c != '_')\n        .filter(|w| !w.is_empty() && w.chars().next().map(|c| c.is_uppercase()).unwrap_or(false))\n        .filter(|w| !defined.contains(*w) && !\"Node\".eq(*w))\n        .collect()\n}","typeGuard":null,"tryCatchPattern":"match ungrammar::Grammar::parse(src) {\n    Err(e) if e.to_string().starts_with(\"Undefined node\") => {\n        eprintln!(\"define the missing node rule: {e}\")\n    }\n    r => r?,\n}","preventionTips":["Grep the grammar for each node name and confirm one `Name =` definition exists","Rename references and definitions together (single commit)","Parse the grammar in CI so undefined references fail builds"],"tags":["parser","ungrammar","grammar","undefined-reference"],"backgroundTag":"undefined-reference","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"}