fix(compilers/openapi): name a $ref'd body or header from source - #452
Open
fuad-daoud wants to merge 1 commit into
Open
fix(compilers/openapi): name a $ref'd body or header from source#452fuad-daoud wants to merge 1 commit into
fuad-daoud wants to merge 1 commit into
Conversation
A request body or response header reached through a $ref lowers its schema under a hint derived from the use site — the referencing operation's ID, the headers-map key — while the node interns at the declaration's pointer. Intern is first-write-wins, so two use sites reaching one declaration handed the shared node to whichever lowered first, and the same document compiled to two different IR documents depending on declaration order. ids.DeclarationHint already guarded the case where the declaration is a top-level component entry, named alike from every use site. It matched on the /components/<kind>/<name> shape, so a $ref naming a construct declared inline elsewhere — just as shared — fell through it. Ctx.NamingByReferenceAt marks a lowering as naming by reference when the declaration is not at the position that reached it, which is the condition NamingByReference already describes; the owning declaration then replaces the placeholder through NameFromDeclaration in whichever order the two run. The marking had no counterpart at these positions because $ref is resolved before the lowering starts, leaving nothing downstream able to tell that a reference was what reached it. Two corpus fixtures cover the pair. Neither case was reachable by any committed spec, so the order-invariance oracle ran green over a corpus that never produced the input it exists to catch. Refs #433 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uvz7tyMXhnkKroffBP7Ptu
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
A request body or response header reached through a
$reflowers its schema under a hint derived from the use site — the referencing operation's ID, the headers-map key — while the node interns at the declaration's pointer.Internis first-write-wins, so two use sites reaching one declaration handed the shared node to whichever lowered first: the same document compiled to two different IR documents depending on declaration order, which is the property invariant 3 and the order-invariance oracle exist to deny.ids.DeclarationHintalready guarded the case where the declaration is a top-level component entry, which is named alike from every use site. It matches on the/components/<kind>/<name>shape, so a$refnaming a construct declared inline elsewhere — just as shared — fell through it.Ctx.NamingByReferenceAtmarks a lowering as naming by reference when the declaration is not at the position that reached it. That is the conditionNamingByReferencealready describes, and the owning declaration then replaces the placeholder throughNameFromDeclarationin whichever order the two run. The marking had no counterpart at these positions because$refis resolved before the lowering starts, leaving nothing downstream able to tell that a reference was what reached it —hoistSubSchemamarks only schema-level$refs.Not a regression: both cases reproduce on
mainas it stands.Scope
This is half of #433. The issue reports the defect at a response mounted at two status classes; that half touches code #438 rewrites, so it lands separately once that PR merges rather than forcing a conflict onto an already-green branch. The two halves are independent — this one touches lines that branch never edits.
Parameters were checked and are safe by construction: they take their hint from
p.GetName(), the declaration's own name, not a use-site key.Test plan
testdata/openapi/, one per site. Both reproduce the order dependence before the change and pass after;go run ./cmd/morphic-harness testdatais clean.TestCtx_NamingByReferenceAtMarksOnlyAForeignDeclarationcovers both branches of the new predicate.make gatecould not be run end-to-end locally: under Go 1.27 the pinnedgolangci-lintv2.12.2 crashes inside staticcheck's IR builder on the stdlibpollpackage, andcompilers/openapi/internal/annotation'sTestRawFromNode_DiffersFromTheOldDecodeOnlyWhereRecordedfails on a�escaping difference. Both reproduce on unmodifiedmainand are toolchain skew, not this branch. Every other gate step passes locally; CI pins its own Go.Refs #433
🤖 Generated with Claude Code
https://claude.ai/code/session_01Uvz7tyMXhnkKroffBP7Ptu