rust-lang/rust-analyzer · error

dynamic `SyntaxKind` for `AstNode::kind()`

Error message

dynamic `SyntaxKind` for `AstNode::kind()`

What it means

This panic is the default body of `AstNode::kind()` in the syntax crate. The method can only return a `SyntaxKind` statically when the node type has a fixed kind; AST enums (dynamic nodes like `ast::Expr`, `ast::Pat`) do not, so calling `kind()` on them panics. It is an API-contract error, not a data error.

Source

Thrown at crates/syntax/src/ast.rs:50

    token_ext::{AnyComment, AnyString, CommentKind, CommentShape, IsString, QuoteOffsets, Radix},
    traits::{
        AttrsIter, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody,
        HasModuleItem, HasName, HasTypeBounds, HasVisibility, attrs_including_inner,
        attrs_with_doc_including_inner,
    },
};

/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
/// the same representation: a pointer to the tree root and a pointer to the
/// node itself.
pub trait AstNode {
    /// This panics if the `SyntaxKind` is not statically known.
    fn kind() -> SyntaxKind
    where
        Self: Sized,
    {
        panic!("dynamic `SyntaxKind` for `AstNode::kind()`")
    }

    fn can_cast(kind: SyntaxKind) -> bool
    where
        Self: Sized;

    fn cast(syntax: SyntaxNode) -> Option<Self>
    where
        Self: Sized;

    fn syntax(&self) -> &SyntaxNode;
    fn clone_subtree(&self) -> Self
    where
        Self: Sized,
    {
        Self::cast(self.syntax().clone_subtree()).unwrap()
    }
}

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Only call `AstNode::kind()` on concrete node types (e.g. `ast::PathExpr`, `ast::Fn`) whose kind is statically known.
  2. For dynamic nodes, get the kind at runtime from an instance via `node.syntax().kind()`.
  3. Dispatch on the enum's own runtime accessors (e.g. `Expr::is_ref_like`, `can_cast`) or match on `SyntaxKind` yourself.
  4. In generic code, add a bound/documentation that `N` must be a statically-known node type.

Example fix

// before
fn describe<N: AstNode>(n: &N) -> SyntaxKind { N::kind() } // panics for ast::Expr
// after
fn describe(n: &syntax::SyntaxNode) -> syntax::SyntaxKind { n.kind() }
Defensive patterns

Strategy: type-guard

Validate before calling

// Prefer runtime kinds on instances; only concrete generated node types have a static kind.
fn use_runtime_kind(node: &syntax::SyntaxNode) -> syntax::SyntaxKind {
    node.kind()
}

Type guard

fn static_kind_of<N: syntax::AstNode>() -> Option<syntax::SyntaxKind> {
    // Only valid for concrete node types; enums like ast::Expr return None here
    // by construction — avoid N::kind() and use node.syntax().kind() instead.
    None
}

Prevention

When it happens

Trigger: Calling `AstNode::kind()` on a dynamic AST enum type such as `ast::Expr`, `ast::Stmt`, `ast::Pat`, or `ast::Adt` — any generated type without a constant kind — via the blanket default implementation.

Common situations: Generic code like `fn f<N: AstNode>() { let k = N::kind(); }` instantiated with an AST enum; calling `T::kind()` after refactoring a concrete node (e.g. `ast::PathExpr`) into a generic parameter; relying on `ast::Expr::kind()` in tests.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/06eef6425d9ea756. Report an issue: GitHub.