{"record":{"id":"5641e9b9af5f34e8","repo":"rust-lang/rust-analyzer","slug":"this-node-should-have-an-ast-id","errorCode":null,"errorMessage":"this node should have an ast id","messagePattern":"this node should have an ast id","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/span/src/ast_id.rs","lineNumber":721,"sourceCode":"                                            }) && anc.next().is_some_and(|it| {\n                                                it.kind() == SyntaxKind::EXPR_STMT\n                                                    || it.kind() == SyntaxKind::STMT_LIST\n                                            })\n                                        }))\n                                {\n                                    let parent = parent_of(parent_idx, &res);\n                                    let block_ast_id =\n                                        block_expr_ast_id(last_block_node, &mut index_map, parent)\n                                            .expect(\"not a BlockExpr\");\n                                    res.arena\n                                        .alloc((SyntaxNodePtr::new(last_block_node), block_ast_id));\n                                    *already_allocated = ContainsItems::Yes;\n                                }\n\n                                let parent = parent_of(parent_idx, &res);\n                                let ast_id =\n                                    ErasedFileAstId::ast_id_for(&node, &mut index_map, parent)\n                                        .expect(\"this node should have an ast id\");\n                                let idx = res.arena.alloc((SyntaxNodePtr::new(&node), ast_id));\n\n                                next_layer.extend(node.children().map(|child| (child, Some(idx))));\n                                preorder.skip_subtree();\n                            }\n                        }\n                        syntax::WalkEvent::Leave(node) => {\n                            if ast::BlockExpr::can_cast(node.kind()) {\n                                let block = blocks.pop();\n                                debug_assert_eq!(\n                                    block.map(|it| it.0),\n                                    Some(node),\n                                    \"left a BlockExpr we never entered\"\n                                );\n                            }\n                        }\n                    }\n                }","sourceCodeStart":703,"sourceCodeEnd":739,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/span/src/ast_id.rs#L703-L739","documentation":"While building the AstIdMap, every remaining node in the traversal is expected to have an AST id computable via ErasedFileAstId::ast_id_for. If a node cannot be assigned an id the expect panics with 'this node should have an ast id'. It guards the invariant that the map is total over the file's syntax tree.","triggerScenarios":"from_source encounters a syntax node kind that ast_id_for cannot map — typically after parser changes, new/unknown node kinds from macro expansion, or a hand-constructed tree missing expected structure while processing top-level/item nodes in the arena traversal.","commonSituations":"rust-analyzer development: adding a new AST node kind without extending ast_id_for; synching the syntax crate versions across crates (span vs parser mismatch); fuzzed inputs producing novel tree shapes.","solutions":["Update all rust-analyzer crates together (parser, syntax, span, hir) so node-kind enums match.","If you added a new syntax node kind, extend ErasedFileAstId::ast_id_for to handle it.","Reduce the failing input to a minimal example and file an upstream issue.","Verify no version skew: cargo update / consistent lockfile across the workspace."],"exampleFix":"// before (new node kind missing from ast_id_for)\nmatch node.kind() { ... } // no arm for the new kind\n// after\nmatch node.kind() {\n    SyntaxKind::MY_NEW_NODE => ...map it...,\n    ...\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"match ErasedFileAstId::ast_id_for(&node, &mut index_map, parent) {\n    Some(id) => ..., // proceed\n    None => { /* skip node or report diagnostic instead of panicking */ }\n}","preventionTips":["Extend ast_id_for whenever adding a new syntax node kind","Keep the parser and span crates in the same lockfile revision","File upstream reproductions when unknown node kinds appear from macros"],"tags":["rust-analyzer","ast","internal-invariant","panic"],"backgroundTag":"ast-invariant-violated","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"}