{"record":{"id":"f100f8fe8c73cdb3","repo":"rust-lang/rust-analyzer","slug":"matcharms-are-always-nested-in-matchexprs","errorCode":null,"errorMessage":"MatchArms are always nested in MatchExprs","messagePattern":"MatchArms are always nested in MatchExprs","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/syntax/src/ast/node_ext.rs","lineNumber":1203,"sourceCode":"            ast::Adt::Struct(it) => ast::Item::Struct(it),\n            ast::Adt::Union(it) => ast::Item::Union(it),\n        }\n    }\n}\n\nimpl ast::MatchGuard {\n    pub fn condition(&self) -> Option<ast::Expr> {\n        support::child(&self.syntax)\n    }\n}\n\nimpl ast::MatchArm {\n    pub fn parent_match(&self) -> ast::MatchExpr {\n        self.syntax()\n            .parent()\n            .and_then(|it| it.parent())\n            .and_then(ast::MatchExpr::cast)\n            .expect(\"MatchArms are always nested in MatchExprs\")\n    }\n}\n\nimpl From<ast::Item> for ast::AnyHasAttrs {\n    fn from(node: ast::Item) -> Self {\n        Self::new(node)\n    }\n}\n\nimpl From<ast::AssocItem> for ast::AnyHasAttrs {\n    fn from(node: ast::AssocItem) -> Self {\n        Self::new(node)\n    }\n}\n\nimpl ast::OrPat {\n    pub fn leading_pipe(&self) -> Option<SyntaxToken> {\n        self.syntax","sourceCodeStart":1185,"sourceCodeEnd":1221,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/syntax/src/ast/node_ext.rs#L1185-L1221","documentation":"`ast::MatchArm::parent_match()` climbs two parent links from a match arm and casts to `ast::MatchExpr`. The library asserts that a `MatchArm` is always nested inside `MATCH_ARM_LIST` inside a `MatchExpr`, so failure to cast is treated as an impossible corrupt-tree condition and panics via `expect`. It is an internal invariant check, not a designed failure path.","triggerScenarios":"Calling `parent_match()` on a `MatchArm` that has been detached from its tree (e.g. removed or moved during an edit/rewrite), a stale arm handle after the enclosing match was replaced, or any hand-built/invalid tree where the arm's grandparent is not a `MatchExpr`.","commonSituations":"Holding node references across syntax-tree mutations in IDE refactors or rust-analyzer assist code; building synthetic match arms in tests or codegen without a proper match expression wrapper; running old code against trees produced by a changed grammar.","solutions":["Call `parent_match()` only on arms freshly obtained from a live, attached tree","Refresh node references after any tree mutation instead of reusing pre-edit handles","Use `and_then(ast::MatchExpr::cast)` to get an `Option<ast::MatchExpr>` when the tree provenance is uncertain","Check `node.syntax().parent()` chain manually if you only need the enclosing expression loosely"],"exampleFix":"// before\nlet match_expr = arm.parent_match();\n// after\nlet Some(match_expr) = arm.syntax().parent().and_then(|p| p.parent()).and_then(ast::MatchExpr::cast) else { return None; };","handlingStrategy":"validation","validationCode":"fn is_attached_match_arm(arm: &ast::MatchArm) -> bool {\n    arm.syntax().parent()\n        .and_then(|p| p.parent())\n        .and_then(ast::MatchExpr::cast)\n        .is_some()\n}","typeGuard":"fn parent_match(arm: &ast::MatchArm) -> Option<ast::MatchExpr> {\n    arm.syntax().parent()?.parent().and_then(ast::MatchExpr::cast)\n}","tryCatchPattern":null,"preventionTips":["Discard and re-resolve node references after tree edits/rewrites","Never call parent helpers on synthetic nodes not built inside a real MatchExpr","Wrap uncertain traversals in Option-based casts rather than the expect helpers"],"tags":["rust","syntax-tree","invariant-assertion","panic"],"backgroundTag":"detached-syntax-node","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"}