rust-lang/rust-analyzer · error

EnumVariants are always nested in Enums

Error message

EnumVariants are always nested in Enums

What it means

`ast::Variant::parent_enum()` unwraps two parent links from a variant node and casts the result to `ast::Enum`. The library asserts as an internal invariant that a `Variant` (a Rust enum variant) is always nested inside an `ENUM_VARIANTS` node inside an `Enum`, so if the cast fails the tree is corrupt or the node is not actually an enum variant. This is an `expect` on a structural invariant, not a recoverable API error.

Source

Thrown at crates/syntax/src/ast/node_ext.rs:778

            Some(ast::Pat::BoxPat(pat)) => match pat.pat() {
                Some(ast::Pat::IdentPat(pat)) => {
                    let name = pat.name()?;
                    Some(NameOrNameRef::Name(name))
                }
                _ => None,
            },
            _ => None,
        }
    }
}

impl ast::Variant {
    pub fn parent_enum(&self) -> ast::Enum {
        self.syntax()
            .parent()
            .and_then(|it| it.parent())
            .and_then(ast::Enum::cast)
            .expect("EnumVariants are always nested in Enums")
    }
    pub fn kind(&self) -> StructKind {
        StructKind::from_node(self)
    }
}

impl ast::Item {
    pub fn generic_param_list(&self) -> Option<ast::GenericParamList> {
        ast::AnyHasGenericParams::cast(self.syntax().clone())?.generic_param_list()
    }
}

impl ast::Type {
    pub fn generic_arg_list(&self) -> Option<ast::GenericArgList> {
        if let ast::Type::PathType(path_type) = self {
            path_type.path()?.segment()?.generic_arg_list()
        } else {
            None

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Ensure the `Variant` node is still attached to a real `ast::Enum` in a live tree before calling `parent_enum()`
  2. Re-obtain the node from the current tree instead of using a cached handle from before a rewrite/mutation
  3. Replace the expect with `and_then(ast::Enum::cast)` returning `Option<ast::Enum>` if the caller's tree may be non-standard
  4. Verify the node kind is actually VARIANT (not a tuple/record field) before casting

Example fix

// before
let enum_node = variant.parent_enum();
// after
let Some(enum_node) = ast::Enum::cast(variant.syntax().parent()?.parent()?) else { return None; };
Defensive patterns

Strategy: validation

Validate before calling

fn is_attached_enum_variant(v: &ast::Variant) -> bool {
    v.syntax().parent()
        .and_then(|p| p.parent())
        .and_then(ast::Enum::cast)
        .is_some()
}

Type guard

fn parent_enum(v: &ast::Variant) -> Option<ast::Enum> {
    v.syntax().parent()?.parent().and_then(ast::Enum::cast)
}

Prevention

When it happens

Trigger: Calling `parent_enum()` on a `Variant` node that was detached from its tree (syntax().parent() is None), a Variant constructed outside a real `Enum` (e.g. a struct/tuple-struct field repurposed or a manually/incorrectly built tree), or on a node whose grandparent is not castable to `ast::Enum`.

Common situations: Transforming or cloning nodes across trees and keeping stale handles to detached nodes; hand-crafting syntax trees in tests or a syntax_editor rewrite; misusing the AST node_ext helpers on nodes obtained from non-enum contexts after refactors of the grammar.

Related errors


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