Skip to content

openapi: an outside $ref weakens the name of an inline position under /paths #372

Description

@OmarAlJarrah

Summary

#353 closed the order-dependent hint at the four inline structural positions —
items, additionalProperties, a patternProperties entry, a prefixItems slot —
by having the pointer walk (structuralPointerHint) replay what the structural
lowering composes. That derivation is confined to /components/schemas/…, and the
remainder is this issue.

Under /paths the enclosing hint comes from an operationId, a response, or a
media-type key, none of which the pointer records. With no reference to it, the
position is named by the structural lowering:

t/anon/paths/~1x/get/responses/200/content/application~1json/schema/items
hint=response_item

Add a component pointing at that same pointer, and the node is named items
instead — the last segment of the pointer, which is all the walk has there.

It is not an order dependence

Worth stating precisely, because #353 was. Components lower before paths, so a
reference from components always interns the node first, in either spelling of
the document. Both orders produce items, and the document stays deterministic.

What is left is that a node's name depends on whether some unrelated schema
elsewhere points at it. A/items in components is A_item whether or not anything
references it; the same position under /paths is response_item or items
depending on that. It is a weaker defect than #353's and a real one: the name an
emitter renders should be a property of the declaration, not of the document's
reference graph.

What closing it would take

  1. Make the reference side never name the node. A node's hint would come only
    from the lowering that reaches it through its enclosing declaration; a lowering
    arriving through an outside $ref would intern the node but not claim its name,
    and the declaration would set it on arrival. That matches the IR's own principle —
    a name comes from a declaration, not from a reference to it — but it means the
    type registry has to distinguish an authoritative namer from a provisional one,
    which is new machinery in compilers/compile rather than a change to one hint
    function.
  2. Decide the weaker spelling names the node at these positions everywhere.
    Cheaper, and worse: the enclosing context is most of what makes an anonymous
    node's hint usable to an emitter.

Direction 1 looks right and is the larger change. It would also subsume
branchPointerHint and structuralPointerHint, which exist only to make two namers
agree — with direction 1 there is one namer, and the agreement is structural rather
than maintained.

Where it is pinned

TestInlinePosition_UnderPathsTakesTheWeakerName
(compilers/openapi/internal/schema/schema_test.go) pins all three values — the
unreferenced response_item and the referenced items in both declaration orders —
and names this issue.

Note that orderInvariantIR() no longer excludes ir.Naming.Hint at all: #353 made
the field comparable, and this remainder is not an order dependence, so nothing here
needs hiding from that comparison.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions