rust-lang/rust-analyzer · error
`ast::DocComment` must have a comment token
Error message
`ast::DocComment` must have a comment token
What it means
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.
Source
Thrown at crates/syntax/src/ast/node_ext.rs:289
}
}
impl ast::DocComment {
// `///` or `/**` or `//!` or `/*!`, all are 3 chars.
pub const PREFIX_LEN: TextSize = TextSize::new(3);
pub fn kind(&self) -> AttrKind {
match self.inner_doc_comment_token() {
Some(_) => AttrKind::Inner,
None => AttrKind::Outer,
}
}
pub fn token(&self) -> AnyComment {
self.syntax
.first_token()
.and_then(ast::AnyComment::cast)
.expect("`ast::DocComment` must have a comment token")
}
pub fn shape(&self) -> CommentShape {
CommentShape::from_text(self.text_with_markers())
}
/// Returns the text with the `/**...*/` or `/*!...*/` or `///...` or `//!...` markers.
pub fn text_with_markers(&self) -> &str {
text_of_first_token(&self.syntax)
}
/// Returns the textual content of a doc comment node as a single string with prefix and suffix removed.
pub fn text(&self) -> &str {
let shape = self.shape();
let text = &self.text_with_markers()[Self::PREFIX_LEN.into()..];
if shape == CommentShape::Block {
// The `*/` may not exist because of recovery.
text.strip_suffix("*/").unwrap_or(text)View on GitHub (pinned to e8f7e90aa3)
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.
Example fix
// before
let comment = doc_comment.token();
// after
let comment = doc_comment.syntax().first_token().and_then(ast::AnyComment::cast);
if let Some(comment) = comment { /* ... */ } Defensive patterns
Strategy: type-guard
Validate before calling
// Only treat nodes as DocComment when their first token is a comment
fn is_valid_doc_comment(node: &SyntaxNode) -> bool {
node.first_token().map_or(false, |t| t.kind().is_comment())
} Type guard
fn safe_doc_comment(node: &SyntaxNode) -> Option<ast::DocComment> {
ast::DocComment::cast(node.clone())
.filter(|dc| dc.syntax().first_token().map_or(false, |t| t.kind().is_comment()))
} Try / catch
let comment = std::panic::catch_unwind(AssertUnwindSafe(|| doc_comment.token()))
.ok()
.or_else(|| doc_comment.syntax().first_token().and_then(ast::AnyComment::cast)); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- segments are always nested in paths
- UseTreeLists are always nested in UseTrees
- AstIdMap node mismatch with node `{ptr:?}`
- you have way too many items in the same file!
- Can't find SyntaxNodePtr {:?} in AstIdMap: {:?}
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/b3751720dc9f32a6.
Report an issue: GitHub.