{"record":{"id":"b3751720dc9f32a6","repo":"rust-lang/rust-analyzer","slug":"ast-doccomment-must-have-a-comment-token","errorCode":null,"errorMessage":"`ast::DocComment` must have a comment token","messagePattern":"`ast::DocComment` must have a comment token","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/syntax/src/ast/node_ext.rs","lineNumber":289,"sourceCode":"    }\n}\n\nimpl ast::DocComment {\n    // `///` or `/**` or `//!` or `/*!`, all are 3 chars.\n    pub const PREFIX_LEN: TextSize = TextSize::new(3);\n\n    pub fn kind(&self) -> AttrKind {\n        match self.inner_doc_comment_token() {\n            Some(_) => AttrKind::Inner,\n            None => AttrKind::Outer,\n        }\n    }\n\n    pub fn token(&self) -> AnyComment {\n        self.syntax\n            .first_token()\n            .and_then(ast::AnyComment::cast)\n            .expect(\"`ast::DocComment` must have a comment token\")\n    }\n\n    pub fn shape(&self) -> CommentShape {\n        CommentShape::from_text(self.text_with_markers())\n    }\n\n    /// Returns the text with the `/**...*/` or `/*!...*/` or `///...` or `//!...` markers.\n    pub fn text_with_markers(&self) -> &str {\n        text_of_first_token(&self.syntax)\n    }\n\n    /// Returns the textual content of a doc comment node as a single string with prefix and suffix removed.\n    pub fn text(&self) -> &str {\n        let shape = self.shape();\n        let text = &self.text_with_markers()[Self::PREFIX_LEN.into()..];\n        if shape == CommentShape::Block {\n            // The `*/` may not exist because of recovery.\n            text.strip_suffix(\"*/\").unwrap_or(text)","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/syntax/src/ast/node_ext.rs#L271-L307","documentation":"ast::DocComment::token() asserts the structural invariant that a DocComment node always wraps a comment token: it takes the node's first token and casts it to AnyComment, panicking with this message if the cast fails. Since DocComment is only produced from comment tokens, failure indicates a corrupted or wrongly-constructed node.","triggerScenarios":"Calling .token() on an ast::DocComment whose syntax node's first token is not castable to AnyComment — practically only when the node was constructed from a non-comment token, or the tree was mutated (edits, token grafting) leaving the DocComment without its comment token.","commonSituations":"Custom syntax-tree surgery in tests or tooling that reparents tokens; synthetic DocComment nodes built in fixtures with wrong tokens; IDE incremental reparse bugs (should be reported upstream).","solutions":["Verify the node actually came from a cast of a comment token; only construct DocComment from COMMENT tokens.","If doing tree surgery, re-validate the subtree after mutation instead of assuming invariants.","If hit on ordinary parsed code, reduce to a minimal file and report a rust-analyzer parser bug."],"exampleFix":"// before\nlet comment = doc_comment.token();\n// after\nlet comment = doc_comment.syntax().first_token().and_then(ast::AnyComment::cast);\nif let Some(comment) = comment { /* ... */ }","handlingStrategy":"type-guard","validationCode":"// Only treat nodes as DocComment when their first token is a comment\nfn is_valid_doc_comment(node: &SyntaxNode) -> bool {\n    node.first_token().map_or(false, |t| t.kind().is_comment())\n}","typeGuard":"fn safe_doc_comment(node: &SyntaxNode) -> Option<ast::DocComment> {\n    ast::DocComment::cast(node.clone())\n        .filter(|dc| dc.syntax().first_token().map_or(false, |t| t.kind().is_comment()))\n}","tryCatchPattern":"let comment = std::panic::catch_unwind(AssertUnwindSafe(|| doc_comment.token()))\n    .ok()\n    .or_else(|| doc_comment.syntax().first_token().and_then(ast::AnyComment::cast));","preventionTips":["Only build DocComment nodes from COMMENT tokens.","Re-fetch nodes from the tree after any syntax mutation.","Avoid caching ast nodes across edits; they can be invalidated.","Prefer the Option-returning cast over expect-based accessors in your own tooling."],"tags":["rust","syntax","ast","invariant"],"backgroundTag":"ast-invariant-panic","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"}