Skip to content

Orphan single-variant enums emitted for filtered discriminator fields #11

@lightsofapollo

Description

@lightsofapollo

Summary

When a struct is a variant of a discriminated oneOf (or anyOf), the discriminator field is correctly filtered out of the generated struct (src/generator.rs:932-947) so it doesn't double-encode against #[serde(tag = "...")]. However, the single-variant enum that was cached during analysis for that field is still emitted at the top level as orphan code — never referenced by any struct field.

Repro

This already happens today for enum: ["X"] discriminator fields. The snapshot src/snapshots/openapi_to_rust__test_helpers__discriminator_no_mapping.snap captures the pattern:

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "species")]
pub enum Animal {
    #[serde(rename = "cat")]
    Cat(Cat),
    #[serde(rename = "dog")]
    Dog(Dog),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Dog {
    pub bark: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
pub enum DogSpecies {              // ← orphan: nothing references this
    #[default]
    #[serde(rename = "dog")]
    Dog,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Cat {
    pub meow: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
pub enum CatSpecies {              // ← orphan
    #[default]
    #[serde(rename = "cat")]
    Cat,
}

After #10 is fixed (treating const-only as a single-value enum), this same orphan pattern shows up for { "type": "string", "const": "X" } discriminator fields too — visible in oneof_in_property.snap and array_union_items.snap after that PR.

Why it matters

  • Cosmetic clutter in generated modules.
  • Names like DogSpecies / URLImageSourceType pollute the public surface of the generated crate, which is awkward for downstream consumers using pub use generated::*.
  • Misleading API surface — these types look usable but have no role in (de)serialization.

Suggested fix

Add a dead-type pruning pass after analysis, before codegen: walk the type graph from root operations / non-filtered struct fields, and drop anything in resolved_cache that isn't reachable. Alternatively, when the generator filters a discriminator field out of a struct, mark its referenced inline-cached enum as a candidate for removal if it has no other dependents.

The first option is more general (also catches any other unreferenced types that might leak in) and probably worth doing once.

Discovered while

Working on #10const-only string property generation. The fix for #10 makes this orphan pattern more visible because more discriminator fields now produce single-variant enums, but it does not introduce the underlying bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions