FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

MergeSchemas silently drops a member's type when an earlier allOf member has none (order-dependent output) · Issue #2524 · oapi-codegen/oapi-codegen · GitHub

MergeSchemas silently drops a member's type when an earlier allOf member has none (order-dependent output) #2524

Description

Found during the review of #2522; pre-existing on main and independent of that PR.

Problem

MergeSchemas (pkg/codegen/merge_schemas.go) only errors on a type conflict when both members declare a type:

if s1.Type.Slice() != nil && s2.Type.Slice() != nil && !equalTypes(s1.Type, s2.Type) {
    return openapi3.Schema{}, fmt.Errorf("can not merge incompatible types: ...")
}
result.Type = s1.Type

When s1 has no type and s2 does, result.Type = s1.Type silently discards s2's type. Because allOf members are merged pairwise in order, the generated Go shape depends on member ordering.

Reproduction

AllOfUnionMember:
  allOf:
    - properties:
        name:
          type: string
    - type: [string, number]

generates a plain struct — the second member's type constraint vanishes entirely:

type AllOfUnionMember struct {
    Name *string `json:"name,omitempty"`
}

Reversing the member order flips which constraint silently wins: result.Type then carries the union, the schema lowers to any, and the properties are dropped instead. The same mechanism applies to single types (- type: string as the second member is discarded the same way); the multi-type union case from #2521/#2522 just made it easier to notice, and converted some of these from hard generation errors into silent, order-dependent output.

Expected

Order should not change the result. Either:

  • propagate the type when exactly one member declares one (result.Type = firstNonNil(s1.Type, s2.Type)), keeping the existing incompatibility error when both declare different types, or
  • error loudly on the typeless+typed combination if propagation has unwanted interactions (e.g. a propagated primitive type alongside merged properties still silently drops the properties at the dispatch stage — propagation alone doesn't make that combination coherent).

The first option fixes the common case (a typeless member contributing only properties/required alongside a typed member); the second at least removes the silent order dependency. Behavior change to generated code either way, so this likely wants a look at what existing specs in the wild rely on before choosing.

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


    Back | FazBrowse Home | New Git URL