{"record":{"id":"9fc8a3c653211f5c","repo":"GraphiteEditor/Graphite","slug":"tried-to-derive-hierarchicaltree-for-non-enum","errorCode":null,"errorMessage":"Tried to derive HierarchicalTree for non-enum","messagePattern":"Tried to derive HierarchicalTree for non-enum","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"proc-macros/src/hierarchical_tree.rs","lineNumber":14,"sourceCode":"use crate::helpers::clean_rust_type_syntax;\nuse proc_macro2::{Span, TokenStream};\nuse quote::{ToTokens, quote};\nuse syn::{Data, DeriveInput, Fields, Type, parse2};\n\npub fn generate_hierarchical_tree(input: TokenStream) -> syn::Result<TokenStream> {\n\tlet input = parse2::<DeriveInput>(input)?;\n\tlet input_type = &input.ident;\n\n\tlet line_number = input_type.span().start().line;\n\n\tlet data = match &input.data {\n\t\tData::Enum(data) => data,\n\t\t_ => return Err(syn::Error::new(Span::call_site(), \"Tried to derive HierarchicalTree for non-enum\")),\n\t};\n\n\tlet build_message_tree: Result<Vec<_>, syn::Error> = data\n\t\t.variants\n\t\t.iter()\n\t\t.map(|variant| {\n\t\t\tlet variant_type = &variant.ident;\n\n\t\t\tlet has_child = variant\n\t\t\t\t.attrs\n\t\t\t\t.iter()\n\t\t\t\t.any(|attr| attr.path().get_ident().is_some_and(|ident| ident == \"sub_discriminant\" || ident == \"child\"));\n\n\t\t\tmatch &variant.fields {\n\t\t\t\tFields::Unit => Ok(quote! {\n\t\t\t\t\tmessage_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));\n\t\t\t\t}),\n\t\t\t\tFields::Unnamed(fields) => {","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/proc-macros/src/hierarchical_tree.rs#L1-L32","documentation":"The `HierarchicalTree` derive generates DebugMessageTree formatting for editor messages and is implemented exclusively for enums (each variant becomes a tree node). Applying it to a struct or union fails this compile-time check because there are no variants to walk.","triggerScenarios":"Adding `#[derive(HierarchicalTree)]` to a `struct` or `union`. The match on `input.data` only binds `Data::Enum`.","commonSituations":"A message type refactored from enum to struct (or vice versa: derive list copied from an enum onto a new struct payload type), or IDE auto-derive inserting the full standard derive list onto every new type.","solutions":["Remove `HierarchicalTree` from the struct/union's derive list.","If the type is really a message, model it as an enum with variants and keep the derive there.","For structs used as variant payloads, derive it on the wrapping enum and let the generated code stringify the payload fields."],"exampleFix":"// before\n#[derive(HierarchicalTree)]\nstruct DocumentMessage {\n\tname: String,\n}\n\n// after\n#[derive(HierarchicalTree)]\nenum DocumentMessage {\n\tRename { name: String },\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["HierarchicalTree is enum-only; keep it off structs and unions.","When introducing payload structs, start from a minimal derive list and add what compiles.","Watch for IDE actions that apply a whole crate's derive set to new types."],"tags":["rust","proc-macro","derive","enums-only"],"backgroundTag":"derive-macro-input-validation","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}