Summary
ir.Parameter.Unmodeled and the ir.Property.Unmodeled a header lowers to each
carry entries from two different source objects, both unscoped:
A parameter and its schema are two objects at two pointers, so both can spell the
same key. They then spell one entry, and which one survives is decided by
lowering order rather than by the document.
Reproduction
Verified at 6fcfd8a. Two shapes, both losing the parameter's own key:
# the schema's undeclared keyword takes the entry (#356 on both sides)
- name: p
in: query
divisibleBy: FROM_PARAMETER_OBJECT
schema: {type: string, divisibleBy: FROM_SCHEMA}
# the keyword #348 keeps beside a $ref takes it
- name: p
in: query
format: FROM_PARAMETER_OBJECT
schema: {$ref: '#/components/schemas/S', format: FROM_SCHEMA_BESIDE_REF}
Each compiles to a single openapi:divisibleBy / openapi:format on the
parameter, holding the schema's value. The same happens on a response header,
whose object keys and schema keywords share the ir.Property it lowers to.
Current state
#356 no longer drops the losing key in silence — it reports
openapi/unknown-key-entry-taken, naming the pointer of the construct holding
the entry. The key is still represented in the IR in no form at all, so this is a
report of the loss, not a fix for it.
The fix, and why it is not #356's
Applying the rule #345 already established for the objects that ride on a carrier
they do not own — key them by the source path from the carrier down — means the
schema's entries become openapi:schema/<keyword> on a parameter and a header,
while the object's own stay unscoped.
That is a one-line change per site and a wide change in effect: it moves keys
#348 and the validation-only reader already publish, so every golden carrying
an unhomed keyword or an if/not on a parameter or header is rewritten. It is
also a decision about the meaning of a carrier's namespace, which is worth
settling once for all three mechanisms rather than as a side effect of the census
that happened to surface it.
Properties are deliberately not in scope: a property is its schema, so its
carrier has only one namespace and its entries must stay unscoped.
Summary
ir.Parameter.Unmodeledand their.Property.Unmodeleda header lowers to eachcarry entries from two different source objects, both unscoped:
x-*(fix(compilers/openapi): read x-* at every object that carries it #345), itsallowEmptyValue, itsxmlhints,and its undeclared keys (fix(compilers/openapi): keep keys the source model does not name #356);
could not read (fix(compilers/openapi): preserve keywords no lowering reads #348), the validation-only keywords
annotation.Readkeeps,and the schema's undeclared keywords (fix(compilers/openapi): keep keys the source model does not name #356).
A parameter and its schema are two objects at two pointers, so both can spell the
same key. They then spell one entry, and which one survives is decided by
lowering order rather than by the document.
Reproduction
Verified at
6fcfd8a. Two shapes, both losing the parameter's own key:Each compiles to a single
openapi:divisibleBy/openapi:formaton theparameter, holding the schema's value. The same happens on a response header,
whose object keys and schema keywords share the
ir.Propertyit lowers to.Current state
#356 no longer drops the losing key in silence — it reports
openapi/unknown-key-entry-taken, naming the pointer of the construct holdingthe entry. The key is still represented in the IR in no form at all, so this is a
report of the loss, not a fix for it.
The fix, and why it is not #356's
Applying the rule #345 already established for the objects that ride on a carrier
they do not own — key them by the source path from the carrier down — means the
schema's entries become
openapi:schema/<keyword>on a parameter and a header,while the object's own stay unscoped.
That is a one-line change per site and a wide change in effect: it moves keys
#348and the validation-only reader already publish, so every golden carryingan unhomed keyword or an
if/noton a parameter or header is rewritten. It isalso a decision about the meaning of a carrier's namespace, which is worth
settling once for all three mechanisms rather than as a side effect of the census
that happened to surface it.
Properties are deliberately not in scope: a property is its schema, so its
carrier has only one namespace and its entries must stay unscoped.