Repository navigation
fix!: record an OpenAPI document's $self on its source entry - #732
Merged
Merged
Conversation
ir.SourceInfo gains SelfURI, the URI a document declares for itself, and the OpenAPI compiler fills it from doc.GetSelf(). It sits on the source entry rather than on the Document because it is a fact about one input file: several may be loaded, each may declare its own, and a document-level field could not say which file declared it. The URI is recorded at every supported minor, not gated to 3.2. The library parses `$self` at every 3.x minor and reports no finding for it, so a version gate would drop a declared value without reporting it — the defect this closes. An empty `$self` and an unset one are one state and one field value: an empty URI reference is not a base URI (RFC 3986 §5.1), so it identifies nothing when documents are combined. Out of scope, stated in the field's GoDoc: resolving relative references through the URI (GitHub #74 owns that wiring), and reporting a `$self` written by a dialect that does not define it. The field raises no diagnostic of its own. IRVersion stays 0.6.0: a new optional member is none of the shape changes docs/ir-design.md §2.1 moves the version for.
Plans and agent working notes are not part of the repository; what a reviewer needs is in the PR body, the commits and the linked issues. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SourceInfo.SelfURI's doc comment was 144 words and assertSelfURI's 109, so TestDocComments_StayUnderTheCap failed the gate. Both keep their reasoning in fewer words.
The fixture predates the idSpaces declaration main introduced with IR 0.7.0. Only irVersion and idSpaces change.
SourceInfo gained selfURI. ir-design §2.1 moves the version on any change to a Document's JSON shape, and a decoder pinned to 0.7.0 refuses the new member, so a document carrying it is unreadable there. BREAKING CHANGE: documents are stamped irVersion 0.8.0; consumers built against 0.7.0 refuse them.
This was referenced Oct 13, 2026
SelfURI's doc said relative references resolve against where the source was loaded from. With external references allowed, a non-schema $ref resolves against $self when one is set, so the sentence was false; how $self takes part in resolution is GitHub #863, and the field's doc no longer speaks to it. It now says what the field holds: the value as written, a relative one not resolved against Path, and the value after any overlay, on the patched document's entry. The reason an empty $self is recorded as none is restated. An empty reference resolves to the location it was read from (RFC 3986 §5.2.2), which is how a document declaring none is known; it was described as no base URI at all. Format, Path and Hash gain the one-line docs every other ir field has, and the three places that listed SourceInfo's members as "format, path, content hash" stop listing them. ir-design's lowering row says how $self is read at every minor and points at #864 for whether a minor below 3.2 is warned. The merge's resolution had joined SourceInfo's declaration onto TagDef's closing brace in ir-design; it starts on its own line again.
TestParse_RecordsSelfURI claimed $self is recorded whatever minor declares it, but its 3.0 row declared none, so dropping the value at 3.0 passed. It now declares one at 3.2, 3.1 and 3.0, and adds rows for a relative URI that must stay unresolved against the source's path and for a null $self. TestCompile_OverlaySetsTheSpecsSelfURI pins where a $self an overlay writes lands: on the spec's entry, replacing the declared one, and not on the overlay's. The two test docs that explained the wire shape and the missing matrix row say it in fewer words.
SourceInfo's field docs state Format and Hash unconditionally, but the table a refused compile's findings index names files nothing read and sets Path alone (compilers.Compiler.SourceTable). The struct's doc now says so once rather than hedging each field. The test doc and ir-design's lowering row said every 3.2 field the parser models is read at every minor. That was checked for a sample of them, not all, and additionalOperations draws a validation error below 3.2, so both now say "other 3.2 fields". One wire-shape row is renamed to read like its neighbour.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
OpenAPI 3.2 lets a document declare its own URI with
$self. The compiler parsed the value and then dropped it without a diagnostic. Closes #614.ir.SourceInfogainsSelfURI(JSONselfURI), and the OpenAPI compiler fills it from the parsed document.Where it lives.
$selfdescribes one input document, not the API, so it goes on that document'sSourcesentry beside its format, path and hash. Two alternatives lose:Documentcould not say which file declared the URI once several files are loaded (engine: multi-file specs — bounded load-and-bundle and a sources-based entry point #74).Unmodeledis for constructs the IR has no field for, and this one now has a field.No capability-matrix row is added, because the rows list what an API can express.
Recorded as written. A relative
$selfstays relative and is not resolved againstPath. OpenAPI resolves it against the retrieval URI, which the compiler does not know, and the IR records the declaration.Empty means none.
$self: ""and$self: nullrecord noselfURI. An empty reference resolves to the location it was read from (RFC 3986 §5.2.2), and a document that declares no$selfis known by that same location, so the two states mean the same thing.Every minor. The parser reads
$selfat 3.0 and 3.1 as well as 3.2, and the compiler records it at all three. That matches how it reads other 3.2 fields the parser models, such as a server'sname, a tag'sparentandkind, thequeryoperation anditemSchema, none of which draws a warning below 3.2. Whether a 3.0 or 3.1 document that writes such a field should get one is a single question for all of them, filed as openapi: a 3.2 field the parser models lowers silently in a 3.0 or 3.1 document #864.Overlays. The recorded value is the one that was lowered. An overlay that writes
$selftherefore sets it on the spec's entry and replaces the declared value. The overlay's own entry records nothing.Out of scope: how
$selftakes part in reference resolution. With external references allowed, the parser already resolves a non-schema$refagainst$self, while a schema$refresolves against the load path. A reference that names the document by its own$selfis fetched instead of being recognised as the document itself. This behaviour predates the change, is not affected by it, and is filed as #863.Test plan
go test ./ir -run TestSourceInfo_JSONEncodesSelfURIchecks the wire shape byte for byte, with and without a declared URI. Each row also decodes back.go test ./compilers/openapi -run TestParse_RecordsSelfURIcovers:$selfrecorded at 3.2, 3.1 and 3.0;go test ./compilers/openapi -run TestCompile_OverlaySetsTheSpecsSelfURIchecks that an overlay's$selflands on the spec's entry and that the overlay's entry records none.Corpus:
testdata/conformance/openapi/self-uri.yamland its golden. The case witnesses no matrix row, for the reason above, andTestConformance_UnwitnessedIRFieldscountsSelfURIas witnessed through it.Planted defects. Each of these turns at least one of the tests above red:
Path;$selffrom the raw source bytes instead of the overlaid document;selfUri;omitempty.A no-op control stays green.
1,840 real-world OpenAPI 3.x specs were compiled with
mainand with this branch. IR (apart fromirVersion), diagnostics and exit codes are identical. The one spec that declares$selfgainsselfURIand nothing else.The full gate runs in CI. Locally I ran the touched packages,
internal/archtest,engine,internal/harnessandcmd/..., plus golangci-lint v2.14.0 over the whole repo. All are clean.Breaking
ir.IRVersionmoves from 0.11.0 to 0.12.0, and the ir-design §2.1 history gets an entry for it.SourceInfogainedselfURI, which changes aDocument's JSON shape, so a consumer pinned to 0.11.0 refuses a 0.12.0 document. Every golden is regenerated, and each one changes only in itsirVersionline.