Repository navigation
feat!: name what replaces a deprecated entity - #1376
Open
OmarAlJarrah wants to merge 2 commits into
Open
OmarAlJarrah wants to merge 2 commits into
OmarAlJarrah wants to merge 2 commits into
Conversation
A deprecation could carry a message, the version it began in and a scheduled removal, but not what to use instead, so a docs target could not link "use X instead" and an SDK could not name the replacement in the deprecation it emits. Deprecation now carries ReplacedBy, a list of EntityRef values. An EntityRef names one entity of the document by its ID, in the field of its class, one field for each class of identity, so a consumer reads the kind of entity from the field and the reference check holds the ID like any other. irverify holds a ref to exactly one field set and an entity to never naming itself; neither ir.TypeEdges, usage nor GraphQL reachability follows a replacement, since naming a type does not use it. No format states a replacement in a keyword, so the OpenAPI compiler fills the field only through a promotion mapping naming the new deprecation.replacedBy target; no default key names it. A mapped key's string or list names each replacement as the document names an entity of the node's class: an operationId, a parameter of the same operation, a header of the same response, a property of the same model, a components schema or a security scheme. The names are resolved once the document is whole and fill the field all or none, with an info finding for one that names nothing or the node itself. The parameter lookup links use moves to lowering so both read a name one way. BREAKING CHANGE: IRVersion moves to 0.36.0. A consumer pinned to 0.35.0 refuses a document whose deprecation names a replacement. In Go, ir.Deprecation is no longer comparable with ==.
This was referenced Nov 2, 2026
…t/ir-deprecation-replacement
17 tasks done
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 deprecation could carry a message, the version it began in and a scheduled removal, but not what to use instead, so a docs target could not link "use
getOrderV2instead" and an SDK could not name the replacement in the deprecation it emits.DeprecationgainsReplacedBy []EntityRef. AnEntityRefnames one entity of the document by its ID, in the field of its class, one field for each class of identity, so a consumer reads the kind of entity from the field and the reference check holds the ID as it holds any other (ir/dangling-*-ref). A replacement may be of another class than what it replaces, since a source can move a value from a query parameter into the body.docs/ir/annotations.md#replacementsgives the design and the two alternatives it rejects: a bare ID string, which no check could read, and a class tag beside a string, which would untype the ID.irverifyholds a ref to exactly one field set (ir/entity-ref-empty,ir/entity-ref-ambiguous), and every node declaring an ID beside aDeprecationto never naming itself (ir/replaced-by-itself). A test holdsEntityRefto one field per identity class, and the carrier list to everyirtype declaring an ID beside aDeprecation; that pin foundMessage, which a first list left out. A replacement names an entity and does not use it, soir.TypeEdges, usage and GraphQL reachability pass over it as they pass over aDocRef.deprecation.replacedBytarget. No default key names it, as none namesunion.open, since no spelling has become common; openapi: the default promotion mapping covers only deprecation and extensible enums #656 is where defaults are chosen. A mapped key's string or list names each replacement as the document names an entity of the node's class: anoperationId, a parameter of the same operation (read as a link's key is, now one helper inlowering), a header of the same response, a property of the same model, acomponents/schemasname or a security scheme key. Names are resolved once the document is whole, since a replacement may be declared after what it replaces. They fill the field all or none: a name naming nothing, or the node itself, is an infoopenapi/degraded-constructat the key. A replacement is never lent by a$ref's target, whose class is another.docs/compilers/openapi.md#extension-promotionand the promotion table indocs/ir/unmodeled.mdgain the target.Off the golden spec: no key there names a replacement (
jqover every object key matchingreplac|supersed|successor|instead|alternative→ none, api-schemas @319dc756). Of 1,840 OpenAPI 3 specs in a real-world corpus, one convention appears, in 2 test specs, naming anoperationIdon an operation and a parameter's name on a parameter, which is the reading this applies under a mapping.Closes #670
Test plan
go test ./ir/... ./compilers/... ./pass/... ./engine/... ./internal/... ./cmd/...,make vetandgolangci-lint run ./...pass; every touched package is at 100% statement coverage on its own tests.replacement.TestResolve_NamesEachReplacementInItsNodesScoperesolves a name on each kind of node: an operation, a parameter, a header of a success response, an error case and a default, a schema, a property and a security scheme.TestResolve_ANameNamingNothingFillsNothingcovers a partial list, a self-reference and names from the wrong scope. Plants, each red: a self-reference let through; a partial list filled.e2e.TestPromotion_ReplacementsResolveOnceTheDocumentIsWholecompiles a spec throughCompilewith a mapping. An operation names anoperationIddeclared after it, a parameter namesquery.pageSizebeside it, a schema names another, and a list holding a missing name fills nothing and reports at its key.irverify: the shape and self-replacement checks over every carrier, plants red on the class match and the ambiguity bound, and a dangling replacement reported at its field. The walk-check cost tests stay at zero allocations on clean values: the carrier fields are indexed once from the types.testdatafails exactly the 51 specs it fails onmain, all deliberate negative fixtures.Deprecation.replacedByand eachEntityRefmember as unwitnessed: the target needs a mapping, and committed specs compile under the default one.Breaking
IRVersionmoves to 0.36.0. A consumer pinned to 0.35.0 refuses a document whose deprecation names a replacement. In Go,ir.Deprecationis no longer comparable with==.