astral-sh/ruff · error
Expected to handle named fields
Error message
Expected to handle named fields
What it means
`handle_option_group` in the `ConfigurationOptions` derive walks the fields of a configuration group struct and reads each field's name via `field.ident`. Only named-field structs are supported, so a tuple or unit field makes `ident` `None` and the `.expect` panics at macro expansion time. It is invoked from `derive_impl` for each group-level field.
Source
Thrown at crates/ruff_macros/src/config.rs:112
#documentation
}
})
}
_ => Err(syn::Error::new(
ident.span(),
"Can only derive ConfigurationOptions from structs with named fields.",
)),
}
}
/// For a field with type `Option<Foobar>` where `Foobar` itself is a struct
/// deriving `ConfigurationOptions`, create code that calls retrieves options
/// from that group: `Foobar::get_available_options()`
fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
let ident = field
.ident
.as_ref()
.expect("Expected to handle named fields");
match &field.ty {
Type::Path(TypePath {
path: Path { segments, .. },
..
}) => match segments.first() {
Some(PathSegment {
ident: type_ident,
arguments:
PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }),
}) if type_ident == "Option" => {
let path = &args[0];
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
Ok(quote_spanned!(
ident.span() => (visit.record_set(#kebab_name, ruff_options_metadata::OptionSet::of::<#path>()))
))
}View on GitHub (pinned to 26f38c119c)
Solutions
- Give the group struct named fields: `struct GlobalOptions { version: Version }`.
- Drop the `ConfigurationOptions` derive from the offending type and implement `get_available_options` manually.
- Patch `handle_option_group` in crates/ruff_macros/src/config.rs to return a spanned `syn::Error` for non-named fields rather than panicking.
Example fix
// before
#[derive(ConfigurationOptions)]
struct CacheOptions(PathBuf);
// after
#[derive(ConfigurationOptions)]
struct CacheOptions {
dir: PathBuf,
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure the group struct is named-field before deriving:
// struct Group { version: Version } // OK
// struct Group(Version); // panics the macro
// Macro-side guard:
let Some(ident) = field.ident.as_ref() else {
return Err(syn::Error::new_spanned(field, "option groups require named fields"));
}; Type guard
fn all_fields_named(data: &syn::Data) -> bool {
match data {
syn::Data::Struct(s) => s.fields.iter().all(|f| f.ident.is_some()),
_ => false,
}
} Prevention
- Keep every `ConfigurationOptions` group struct named-field only.
- Never use new-type wrappers for configuration groups.
- Replace `.expect` in the derive with spanned syn errors.
- Document the named-fields requirement on the derive macro.
When it happens
Trigger: Deriving `ConfigurationOptions` on a struct containing tuple fields (e.g. `struct GlobalOptions(Version)`) or unit fields, causing `field.ident.as_ref()` to be `None` during expansion.
Common situations: Adding a new group type to ty/ruff configuration as a new-type wrapper, or refactoring a named-field struct into tuple syntax while keeping the derive.
Related errors
- Expected to handle named fields
- project directory `{project_path}` does not contain a base n
- truth source directory `{dir}` does not contain a base name
- could not find any `<CURSOR>` directives in any of the files
- Missing #[prefix = "..."] attribute
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/459096c01660794c.
Report an issue: GitHub.