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 {
NoneView on GitHub (pinned to e8f7e90aa3)
Solutions
- Ensure the `Variant` node is still attached to a real `ast::Enum` in a live tree before calling `parent_enum()`
- Re-obtain the node from the current tree instead of using a cached handle from before a rewrite/mutation
- Replace the expect with `and_then(ast::Enum::cast)` returning `Option<ast::Enum>` if the caller's tree may be non-standard
- 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
- Re-fetch nodes from the current tree after any mutation instead of caching handles
- Check node.kind() is VARIANT before using Variant-specific helpers
- Prefer Option-returning navigation (syntax().parent() chains) in your own code over expect-based helpers
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
- MatchArms are always nested in MatchExprs
- equivalent ancestor node should be present in target tree
- the nearest mapped ancestor must map its descendants
- We explicitly do not provide canonicalization API, as that i
- bad kind {other}
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/528be1d47d8cba09.
Report an issue: GitHub.