{"record":{"id":"eeb30ca981a75f28","repo":"GraphiteEditor/Graphite","slug":"tried-to-derive-transitivechild-without-a-parent","errorCode":null,"errorMessage":"tried to derive TransitiveChild without a #[parent] attribute (on {})","messagePattern":"tried to derive TransitiveChild without a #\\[parent\\] attribute \\(on (.+?)\\)","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"proc-macros/src/transitive_child.rs","lineNumber":12,"sourceCode":"use crate::helper_structs::Pair;\nuse proc_macro2::{Span, TokenStream};\nuse syn::{DeriveInput, Expr, Type};\n\npub fn derive_transitive_child_impl(input_item: TokenStream) -> syn::Result<TokenStream> {\n\tlet input = syn::parse2::<DeriveInput>(input_item).unwrap();\n\n\tlet attribute = input\n\t\t.attrs\n\t\t.iter()\n\t\t.find(|a| a.path().is_ident(\"parent\"))\n\t\t.ok_or_else(|| syn::Error::new(Span::call_site(), format!(\"tried to derive TransitiveChild without a #[parent] attribute (on {})\", input.ident)))?;\n\n\tlet parent_is_top = input.attrs.iter().any(|a| a.path().is_ident(\"parent_is_top\"));\n\n\tlet Pair {\n\t\tfirst: parent_type,\n\t\tsecond: to_parent,\n\t\t..\n\t} = attribute.parse_args::<Pair<Type, Expr>>()?;\n\n\tlet top_parent_type: Type = syn::parse_quote! { <#parent_type as TransitiveChild>::TopParent };\n\n\tlet input_type = &input.ident;\n\n\tlet trait_impl = quote::quote! {\n\t\timpl TransitiveChild for #input_type {\n\t\t\ttype Parent = #parent_type;\n\t\t\ttype TopParent = #top_parent_type;\n\t\t}","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/proc-macros/src/transitive_child.rs#L1-L30","documentation":"The `TransitiveChild` derive requires a companion `#[parent(ParentType, expr)]` helper attribute that tells it the parent type and how to navigate from child to parent (used to climb message hierarchies up to a `TopParent`). Deriving it without that attribute is a compile error naming the offending type.","triggerScenarios":"Applying `#[derive(TransitiveChild)]` to a struct or enum with no `#[parent(...)]` attribute present. The `.find(|a| a.path().is_ident(\"parent\"))` returns None. Note `#[parent_is_top]` alone is not enough — it is only a modifier.","commonSituations":"Adding the derive from an IDE completion that lists it with the standard derives, refactoring that deletes the helper attribute, or writing `#[parent_is_top]` on the root type and forgetting `#[parent]` on an intermediate child. Also: `#[parent]` must be resolvable — if it is stripped by cfg or misspelled, the same error appears.","solutions":["Add the helper attribute above the derive: `#[parent(ParentType, |c| c.parent)]` — first the parent type, then an expression/closure navigating child→parent.","For the root of the hierarchy, combine `#[parent(ParentType, expr)] #[parent_is_top]` if that parent is the top.","Ensure the attribute is spelled exactly `parent` and sits on the same item as the derive."],"exampleFix":"// before\n#[derive(TransitiveChild)]\nstruct LayerNode {\n\tparent: Option<Box<DocumentNode>>,\n}\n\n// after\n#[parent(DocumentNode, |n| n.parent_as_document())]\n#[derive(TransitiveChild)]\nstruct LayerNode {\n\tparent: Option<Box<DocumentNode>>,\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair #[derive(TransitiveChild)] with a #[parent(Type, expr)] helper attribute.","Spell the helper exactly `parent`; `parent_is_top` is only an extra modifier.","When refactoring hierarchies, grep for `derive(.*TransitiveChild` and check each item still has its #[parent]."],"tags":["rust","proc-macro","transitive-child","derive-helper-attribute"],"backgroundTag":"missing-derive-helper-attribute","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}