{"record":{"id":"29d95021da788e21","repo":"astral-sh/ruff","slug":"expected-to-handle-named-fields","errorCode":null,"errorMessage":"Expected to handle named fields","messagePattern":"Expected to handle named fields","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_macros/src/combine_options.rs","lineNumber":43,"sourceCode":"                            #output\n                        ),*\n                        }\n                    }\n                }\n            })\n        }\n        _ => Err(syn::Error::new(\n            ident.span(),\n            \"Can only derive CombineOptions from structs with named fields.\",\n        )),\n    }\n}\n\nfn handle_field(field: &Field) -> syn::Result<proc_macro2::TokenStream> {\n    let ident = field\n        .ident\n        .as_ref()\n        .expect(\"Expected to handle named fields\");\n\n    match &field.ty {\n        Type::Path(TypePath {\n            path: Path { segments, .. },\n            ..\n        }) => match segments.first() {\n            Some(PathSegment {\n                ident: type_ident, ..\n            }) if type_ident == \"Option\" => Ok(quote_spanned!(\n                ident.span() => #ident: self.#ident.or(other.#ident)\n            )),\n            _ => Err(syn::Error::new(\n                ident.span(),\n                \"Expected `Option<_>` or `Vec<_>` as type.\",\n            )),\n        },\n        _ => Err(syn::Error::new(ident.span(), \"Expected type.\")),\n    }","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/astral-sh/ruff/blob/26f38c119cac42e4d320ba08f09224fdec74af2c/crates/ruff_macros/src/combine_options.rs#L25-L61","documentation":"This panic comes from `handle_field` in the `combine_options` proc-macro, which only knows how to generate code for structs with named fields. When the derive is applied to a struct with tuple or unit fields, `field.ident` is `None` and the `.expect` panics during macro expansion instead of emitting a proper compile error.","triggerScenarios":"Applying the derive handled by `combine_options.rs` to a struct declared with positional fields such as `struct Foo(u32);`, or to a tuple/unit enum variant, instead of a named-field struct like `struct Foo { option: u32 }`.","commonSituations":"A developer adds the derive to a new-type wrapper or tuple struct while refactoring option types in ruff, or generates the struct via a macro that emits positional fields.","solutions":["Change the struct to use named fields: `struct Foo { option: u32 }` instead of `struct Foo(u32)`.","Remove the derive from tuple/unit structs it was not designed for and hand-implement the generated trait.","Extend `handle_field` in crates/ruff_macros/src/combine_options.rs to return a spanned `syn::Error` for `field.ident == None` instead of panicking."],"exampleFix":"// before\n#[derive(CombineOptions)]\nstruct LintOptions(u32);\n\n// after\n#[derive(CombineOptions)]\nstruct LintOptions {\n    select: u32,\n}","handlingStrategy":"validation","validationCode":"// Verify the target struct has named fields before applying the derive:\n// struct MyOptions { select: u32 }  // OK\n// struct MyOptions(u32);            // panics the macro\n// Prefer fixing the macro itself:\nlet Some(ident) = field.ident.as_ref() else {\n    return Err(syn::Error::new_spanned(field, \"expected named fields\"));\n};","typeGuard":"fn has_named_fields(input: &syn::DeriveInput) -> bool {\n    matches!(&input.data, syn::Data::Struct(d)\n        if d.fields.iter().all(|f| f.ident.is_some()))\n}","tryCatchPattern":null,"preventionTips":["Only apply the derive to structs written with `field: Type` syntax.","Review struct declarations after refactors between named and tuple syntax.","Prefer `syn::Error::new_spanned` over `.expect` inside proc-macros so failures become compile errors.","Add a trybuild UI test covering tuple-struct input for the derive."],"tags":["rust","proc-macro","derive-macro","named-fields"],"backgroundTag":"derive-macro-unsupported-field-shape","analyzedSha":"26f38c119cac42e4d320ba08f09224fdec74af2c","analyzedAt":"2026-09-05T10:32:37.492Z","contentChangedAt":"2026-09-05T10:32:37.492Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}