{"id":"6ba0b3971bb7afda","repo":"rust-lang/rust","slug":"cannot-derive-on-union-6ba0b3","errorCode":null,"errorMessage":"cannot derive on union","messagePattern":"cannot derive on union","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_macros/src/visitable.rs","lineNumber":6,"sourceCode":"use quote::quote;\nuse synstructure::BindingInfo;\n\npub(super) fn visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {\n    if let syn::Data::Union(_) = s.ast().data {\n        panic!(\"cannot derive on union\")\n    }\n\n    let has_attr = |bind: &BindingInfo<'_>, name| {\n        let mut found = false;\n        bind.ast().attrs.iter().for_each(|attr| {\n            if !attr.path().is_ident(\"visitable\") {\n                return;\n            }\n            let _ = attr.parse_nested_meta(|nested| {\n                if nested.path.is_ident(name) {\n                    found = true;\n                }\n                Ok(())\n            });\n        });\n        found\n    };\n","sourceCodeStart":1,"sourceCodeEnd":24,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_macros/src/visitable.rs#L1-L24","documentation":"`visitable_derive` (in rustc_macros/src/visitable.rs) generates `Walkable`/`MutWalkable` impls for the rustc_ast IR traversal by visiting each field ref/ref-mut. Unions have no per-field access pattern the generator can express, so the guard at visitable.rs:5 panics on `syn::Data::Union`.","triggerScenarios":"Annotating a `union` with `#[derive(Visitable)]` (the AST `Walkable`/`MutWalkable` derive used by rustc_ast/rustc_parse visitors); macro expansion matches the union arm and panics.","commonSituations":"Adding AST-walking derives to a union-shaped IR node during parser/AST refactoring. Copying a derive block from a struct AST node onto a union. Introducing a union for FFI inside AST code and inheriting nearby derives.","solutions":["Remove `#[derive(Visitable)]` from the union.","Restructure the IR node as a struct or enum so the derive can enumerate fields/variants.","Manually implement `Walkable`/`MutWalkable` to visit only the field indicated by an external discriminant."],"exampleFix":"// before\n#[derive(Visitable)]\nunion Node {\n    expr: Expr,\n    item: Item,\n}\n\n// after\n#[derive(Visitable)]\nenum Node {\n    Expr(Expr),\n    Item(Item),\n}","handlingStrategy":"validation","validationCode":"// `visitable.rs` derive — same union rejection as the other TypeFoldable /\n// TypeVisitable family members.\nfn reject_union_for_derive(item: &syn::Item) -> Result<(), String> {\n    if let syn::Item::Union(u) = item {\n        return Err(format!(\n            \"union `{}` cannot derive visitable; manual impl required\",\n            u.ident));\n    }\n    Ok(())\n}","typeGuard":"fn visitable_derivable(item: &syn::Item) -> bool {\n    matches!(item, syn::Item::Struct(_) | syn::Item::Enum(_))\n}","tryCatchPattern":null,"preventionTips":["Adopt a project-wide rule: derives that need to traverse fields are struct/enum only.","Maintain a short allow-list of unions and their manual trait impls."],"tags":["rustc-macros","derive","ast-visitable","union"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}