Skip to content

Fix/more quirks - #81

Merged
fredbi merged 5 commits into
go-openapi:masterfrom
fredbi:fix/more-quirks
Aug 3, 2026
Merged

Fix/more quirks#81
fredbi merged 5 commits into
go-openapi:masterfrom
fredbi:fix/more-quirks

Conversation

@fredbi

@fredbi fredbi commented Aug 3, 2026

Copy link
Copy Markdown
Member

Change type

Please select: 🆕 New feature or enhancement|🔧 Bug fix'|📃 Documentation update

Short description

Fixes

Full description

Checklist

  • I have signed all my commits with my name and email (see DCO. This does not require a PGP-signed commit
  • I have rebased and squashed my work, so only one commit remains
  • I have added tests to cover my changes.
  • I have properly enriched go doc comments in code.
  • I have properly documented any breaking change.

fredbi and others added 5 commits August 3, 2026 10:48
An embed carrying an explicit json name is not a promotion: it nests as a
single named property built from the embedded type, and that path does
consult `swagger:strfmt` / `swagger:type`. The ineffective-annotation warning
fired for every annotated embed regardless, so an author naming an embed was
told the annotation had been dropped while it was in fact being applied.

The warning now sits on the two arms that really do discard it — the allOf
member and the promoting plain embed — instead of ahead of the split.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Embedding a named type over a basic, slice, array or map promotes no member —
there is none to promote — so encoding/json keeps the value as an ordinary key
named after the type. The schema dropped it: the embed walk had arms for struct
and interface only and everything else hit a warn-and-skip whose "unsupported
Go type" wording described a type codescan cannot model rather than one it
silently discards.

Such an embed now takes the same path as a json-named one, because it is the
same thing: a single named property built from the embedded type, so the
embedded type's classifiers reach it and the embed's own json tag renames or
drops it as on any field.

A promoted TextMarshaler is deliberately not modelled, though it makes the
whole struct marshal as a bare scalar under the default marshaller. An embed
means composition here, and a composed model round-trips through a hand-written
MarshalJSON — a promoted marshaller in the source says nothing about the wire.
It is also not decidable from a declaration: a pointer-receiver marshaller
squashes for &v and not for v.

The fixture module gains a second wire oracle, recording raw documents rather
than key sets so it can state that divergence rather than hide it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
io.Reader and its relatives had no recognizer, so they fell through to
structural drilling — and an interface is the shape drilling handles worst. A
formData parameter typed io.Reader emitted no `type` at all, which SimpleSchema
does not permit; a model field published io's own interfaces as definitions
carrying io's godoc, and ReadCloser grew a `close` property of type string out
of `Close() error`.

Recognized by identity: the io stream interfaces plus io.LimitedReader,
mime/multipart.File and runtime.NamedReadCloser. A structural rule would
swallow any user interface exposing a Read method, so the table is closed.
io.Writer stays out — a sink the caller writes into does not travel on the
wire, and recognizing it would invent an intent.

Two answers, chosen by position: `type: file` on a formData parameter, the only
place OAS 2.0 allows it and the canonical upload shape; `{string, byte}`
everywhere else, since a JSON body cannot carry raw octets and base64 is how
OAS 2.0 spells opaque bytes. This states less than the drilled structure it
replaces, not more. An explicit swagger:file / swagger:type / swagger:strfmt
still wins.

Because the recognizers answer from the object alone, these types also stop
requiring a resolvable declaration, so a package graph without `io` degrades
instead of failing the scan.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Two guides for shapes authors currently discover by surprise.

File uploads and byte streams: which named types codescan reads as opaque
bytes, and the position-dependent answer — `type: file` on a formData
parameter, `{string, byte}` everywhere else — with the annotations that
override it and a note on why io.Writer is left alone.

Composing embeds with allOf: an allOf describes one flat document, and Go's
default marshaller only produces that by coincidence — for a plain struct embed
with no marshaller of its own. A non-struct member promotes nothing and a
member carrying its own MarshalJSON captures the whole type, so a composed
model needs a hand-written pair. The section shows the shape go-swagger
generates and says plainly that codescan cannot warn when it is missing, since
it reads declarations rather than marshalling.

The uploads guide is backed by a new shaping/streams example package whose test
emits both rendered goldens.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
Neither rendering can carry it. `format: byte` says base64 bytes and `type:
file` says an upload, so all twelve recognized types collapse onto the same
schema and an io.Reader field becomes indistinguishable from a multipart.File
one. That is the criterion the error recognizer already follows — stamp
x-go-type when the rendering erases the type — as opposed to time.Time and
uuid.UUID, where the format IS the type and the recognizers rightly stay
silent. The stream recognizer was missing from that first group.

The stamp goes through the same skipExt gate as every other vendor extension.

A format override leaves it in place, since the Go type is still what it was; a
type override replaces the schema and takes it along. Both are now pinned by
the witness rather than left to be rediscovered.

The witness fixture also stops naming its fields after their types, which made
x-go-name and x-go-type indistinguishable in the golden and would have hidden a
regression in either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
@fredbi
fredbi merged commit c611667 into go-openapi:master Aug 3, 2026
24 checks passed
@fredbi
fredbi deleted the fix/more-quirks branch August 3, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant