SIGN IN SIGN UP

validate: warn on a format that belongs to a different type (#1120)

* validate: warn on a format that belongs to a different type (#1019)

A format is scoped to a type: {type: integer, format: date-time} is not a
parse error, but the format never applies and spec-compliant tooling
ignores it silently. It is almost always an authoring slip where the type
or the format changed without the other.

Add type-format-mismatch (WARN), covering the OpenAPI Data Type pairings
plus the standard JSON Schema string formats. It only fires when the
format is a KNOWN format owned by a different type: an unrecognized or
custom format is allowed (the spec says tools should ignore formats they
do not recognize), and a schema with no declared type is skipped. A 3.1
nullable type array is compared by its non-null type.

Also dedupe the three per-field origin-location helpers the native lints
had grown into one schemaFieldLocation, built on the existing fieldLoc.

* validate: fold paramTypeLocation onto schemaFieldLocation

It repeated the Fields/Key lookup schemaFieldLocation already does; keep
only the extra fallback it adds (the parameter's own location).

* validate: move the source-location helpers into location.go

validate.go carried the location resolution (the kin-error dispatch plus
the schema-field helpers) alongside the validator itself. Move all four
into location.go so one file answers how a finding gets pinned to a line,
and validate.go is just validate + the kin-error mapping. Pure move, no
behavior change.

* validate: resolve a kin error's location once, not twice

lineForKinError and columnForKinError each ran locationForKinError, so the
typed-error dispatch (a chain of ~15 errors.AsType checks) ran twice for
every finding just to read the two fields off the same Location. Replace
both with lineColumnForKinError, which resolves once and returns the pair,
matching schemaFieldLocation's shape. Moved to location.go with the other
location helpers.

* validate: derive the format owner from formatsByType

formatOwner hardcoded the type list, repeating formatsByType's keys, so
adding a type to the table would not have been picked up: its formats
would have been silently treated as custom and never reported. Invert the
table once into a format -> type index, making the table the single source
of truth and the lookup O(1) instead of two scans. A test asserts the
invariant the inversion relies on (each format has exactly one type).
R
Reuven Harrison committed
b09dd8d44cc05f88ad3364434f662e2e2e33db37
Parent: d2d6031
Committed by GitHub <noreply@github.com> on 7/25/2026, 11:16:16 AM