Problem
Every unresolved-reference diagnostic the lowering emits points at the position that wrote the reference. The ones the resolve phase emits do not — they carry an empty pointer:
error openapi/unresolved-ref ptr="" external reference not allowed
error openapi/unresolved-ref ptr=/components/schemas/A unresolved $ref "c.yaml#/components/schemas/X"
A document with several unresolvable references produces several findings that all name the document and none of them the site, so there is nothing to act on.
It stems from ResolveAllReferences running in compilers/openapi/internal/load/load.go before the walk that would know a position, and from the library returning bare errors.New values with no location attached.
Why it is not cosmetic
Six reference positions have no sited diagnostic at all — the unsited one is their only report. Compiling a spec whose $ref at each position names a missing component, each in turn:
| position |
sited diagnostic |
unsited |
component schema, property, allOf branch, items, oneOf variant, securityScheme, discriminator mapping |
yes |
yes |
| pathItem, parameter, response, requestBody, header, callback |
no |
yes |
So the unsited diagnostic cannot simply be dropped as a duplicate; it has to be replaced by a sited one.
It also forces a compromise in the load phase today: identical resolver failures are collapsed into one diagnostic, because without a site N of them genuinely carry no more information than one. Once each names its site they are distinct findings and should separate.
Direction
The resolve phase needs a position for each failure. Two candidate sources:
soa.Walk yields a WalkItem.Location alongside each resolvable, and ResolveAllReferences is itself a Walk + resolve loop — driving that walk directly keeps the location that the library's own helper discards.
- A raw-YAML scan for
$ref values, in the shape internal/scan already uses for cycle detection, which would also carry line:col.
Acceptance
- Each unresolvable reference is reported once, at the pointer that wrote it, whatever its position.
- The six positions above stop depending on a document-level diagnostic.
- The identical-message collapse in
resolveDiags comes out.
Problem
Every unresolved-reference diagnostic the lowering emits points at the position that wrote the reference. The ones the resolve phase emits do not — they carry an empty pointer:
A document with several unresolvable references produces several findings that all name the document and none of them the site, so there is nothing to act on.
It stems from
ResolveAllReferencesrunning incompilers/openapi/internal/load/load.gobefore the walk that would know a position, and from the library returning bareerrors.Newvalues with no location attached.Why it is not cosmetic
Six reference positions have no sited diagnostic at all — the unsited one is their only report. Compiling a spec whose
$refat each position names a missing component, each in turn:allOfbranch,items,oneOfvariant, securityScheme, discriminator mappingSo the unsited diagnostic cannot simply be dropped as a duplicate; it has to be replaced by a sited one.
It also forces a compromise in the load phase today: identical resolver failures are collapsed into one diagnostic, because without a site N of them genuinely carry no more information than one. Once each names its site they are distinct findings and should separate.
Direction
The resolve phase needs a position for each failure. Two candidate sources:
soa.Walkyields aWalkItem.Locationalongside each resolvable, andResolveAllReferencesis itself aWalk+ resolve loop — driving that walk directly keeps the location that the library's own helper discards.$refvalues, in the shapeinternal/scanalready uses for cycle detection, which would also carry line:col.Acceptance
resolveDiagscomes out.