{"record":{"id":"20bd7a0c30675a5a","repo":"GraphiteEditor/Graphite","slug":"extractfield-only-works-on-structs","errorCode":null,"errorMessage":"ExtractField only works on structs","messagePattern":"ExtractField only works on structs","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"proc-macros/src/extract_fields.rs","lineNumber":19,"sourceCode":"use crate::helpers::clean_rust_type_syntax;\nuse proc_macro2::{Span, TokenStream};\nuse quote::{ToTokens, format_ident, quote};\nuse syn::{Data, DeriveInput, Fields, Type, parse2};\n\npub fn derive_extract_field_impl(input: TokenStream) -> syn::Result<TokenStream> {\n\tlet input = parse2::<DeriveInput>(input)?;\n\tlet struct_name = &input.ident;\n\tlet generics = &input.generics;\n\tlet (impl_generics, ty_generics, where_clause) = generics.split_for_impl();\n\n\tlet line_number = struct_name.span().start().line;\n\n\tlet fields = match &input.data {\n\t\tData::Struct(data) => match &data.fields {\n\t\t\tFields::Named(fields) => &fields.named,\n\t\t\t_ => return Err(syn::Error::new(Span::call_site(), \"ExtractField only works on structs with named fields\")),\n\t\t},\n\t\t_ => return Err(syn::Error::new(Span::call_site(), \"ExtractField only works on structs\")),\n\t};\n\n\tlet mut field_line = Vec::new();\n\t// Extract field names and types as strings at compile time\n\tlet field_info = fields\n\t\t.iter()\n\t\t.map(|field| {\n\t\t\tlet ident = field.ident.as_ref().unwrap();\n\t\t\tlet name = ident.to_string();\n\t\t\tlet ty = clean_rust_type_syntax(field.ty.to_token_stream().to_string());\n\t\t\tlet line = ident.span().start().line;\n\t\t\tfield_line.push(line);\n\t\t\t(name, ty)\n\t\t})\n\t\t.collect::<Vec<_>>();\n\n\tlet field_str = field_info.into_iter().map(|(name, ty)| (format!(\"{name}: {ty}\")));\n","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/proc-macros/src/extract_fields.rs#L1-L37","documentation":"The `ExtractField` derive is defined only for structs; applying it to an enum or union fails this early check in the derive implementation. The macro walks `Data::Struct` fields to build compile-time field metadata, which has no meaning for enum variants or union fields.","triggerScenarios":"Applying `#[derive(ExtractField)]` to an `enum` or `union` item. Any `Data` variant other than `Data::Struct` hits the second error arm.","commonSituations":"A blanket derive added to every message type in a crate, or moving a type from struct to enum (e.g. introducing a `None` state) while the old derive list stays in place.","solutions":["Remove `ExtractField` from the enum/union's derive list — the metadata it generates only exists for structs.","If each variant's payload needs field introspection, put `ExtractField` on the payload structs instead.","If the type should be a struct, revert the enum conversion."],"exampleFix":"// before\n#[derive(ExtractField)]\nenum ShapeData {\n\tRect { w: f64, h: f64 },\n}\n\n// after\n#[derive(ExtractField)]\nstruct Rect {\n\tw: f64,\n\th: f64,\n}\nenum ShapeData {\n\tRect(Rect),\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep ExtractField out of enum/union derive lists.","When converting a struct to an enum, prune derives that are struct-only.","Derive ExtractField on variant payload structs instead."],"tags":["rust","proc-macro","derive","structs-only"],"backgroundTag":"derive-macro-input-validation","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}