openapi: preserve numeric values losslessly as BigVal - #23
Merged
Conversation
Numeric constraints and defaults were routed through the third-party parser's
float64 model, which silently dropped bounds beyond float64 range (e.g.
minimum: 1.8e308 produced a schema byte-identical to one with no bound),
rounded high-precision decimals, and rejected valid non-JSON spellings like a
leading-dot .5 as a type mismatch.
Read numeric bounds, multipleOf, exclusive bounds, defaults, consts, enum
members, and examples from the raw YAML/JSON nodes and store them as ir.BigVal,
so every literal — out-of-float64-range magnitudes, high precision, and
exponential forms — survives verbatim; NewBigVal canonicalizes only the
JSON-invalid affixes (a leading or trailing dot, a leading +) without touching a
significant digit, and rejects non-finite literals. Suppress the library's
float64-derived type-mismatch findings for literals that are valid numbers.
Also attach a top-level scalar component's constraints to its alias Scalar: a
component like {minimum: 5} reduces to a shared primitive and, unlike a
property, has no other node to hold the constraint, so it was silently dropped.
Closes #16
…int branches Add focused tests for the numeric-BigVal handling that had no coverage: the recursion and nil guards in the numeric-literal artifact classifier (keywordScalars, walkNumericScalars, invalidSyntaxOnValidNumbers), the type-mismatch arms (nil node, no matching keyword literal, non-mismatch underlying error), and the early returns plus diagnostic-stamping loop of componentConstraints (nil/bool/reference input, empty-$ref schema, and a non-numeric scalar bound that must warn rather than be dropped). This brings compilers/openapi back to 100% statement coverage without touching production code.
…igval # Conflicts: # compilers/openapi/load.go
…d scalar constraints
Numeric bounds are read losslessly from the raw YAML nodes, so the library's
float64 view of minimum/maximum/multipleOf/exclusive* is never authoritative.
Treat it that way end to end:
- Suppress the library's redundant float64 type-mismatch findings on numeric-
bound keywords unconditionally, not only for in-range values. Morphic re-reads
and re-validates every such keyword, so a genuinely bad bound now surfaces as a
single error with the schema's own provenance instead of two library errors at
line:col plus a duplicate warning.
- Emit the numeric-bound diagnostic at error severity: it is now the sole report
of a non-numeric bound, and a non-numeric bound is an invalid schema.
- Make exclusiveMinimum/exclusiveMaximum handling dialect-aware. 3.0 spells them
as a boolean modifier and the 2020-12 dialect (3.1, 3.2) as a numeric bound;
the wrong form for the dialect (a boolean under 2020-12, a number under 3.0) is
now reported and dropped rather than silently accepted as a degenerate
{ExclusiveMin:true, Min:nil} constraint. A valid 3.0 boolean exclusive bound,
which the library wrongly flags as a numeric type-mismatch, no longer errors.
Also close a constraint-loss gap adjacent to the component fix: a $ref to an
internal scalar sub-schema that reduces to a shared primitive now carries its
value constraints onto the hoisted alias, exactly as a named scalar component
does, via a shared schemaConstraints helper. A sub-schema read from two positions
(its owning property and the $ref hoist) computes its constraints twice, so a
malformed bound is de-duplicated per source pointer to report exactly once.
Document that plain-scalar numeric capture in value lowering is intentionally
independent of the surrounding schema type (the Type-vs-Value split).
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
Numeric constraints and defaults were routed through the parser's
float64model, which defeatedthe arbitrary-precision (
BigVal) design:float64range (e.g.minimum: 1.8e308) was silently dropped — the schema cameout byte-identical to one with no bound.
.5) was rejected as a type mismatch.Because the parser cannot represent these values, its validation findings on the numeric-bound
keywords are unreliable. This change reads every numeric bound from the raw nodes and makes the
compiler authoritative for those keywords, so a valid magnitude/precision/spelling survives verbatim
and a genuinely bad bound is reported once, on the compiler's own terms.
Change
multipleOf, exclusive bounds, defaults,const, enum members, and examples areread from the raw YAML/JSON nodes and stored as
ir.BigVal, so every literal — magnitudesbeyond
float64range, high precision, and exponential forms — survives verbatim.NewBigValcanonicalizes only the JSON-invalid affixes (a leading or trailing dot, a leading
+) withouttouching a significant digit, and rejects non-finite literals (
inf/nan).minimum,maximum,multipleOf,exclusiveMinimum,exclusiveMaximum): the parser'sfloat64type-mismatch findings on them aresuppressed, and a bound that is genuinely not a finite number is reported as a single error with
the schema's own provenance instead of two parser findings plus a duplicate. A spec whose numbers
exceed
float64range or use a valid non-JSON spelling is no longer failed.exclusiveMinimum/exclusiveMaximumhandling is dialect-aware. 3.0 spells them as a booleanmodifier and the 2020-12 dialect (3.1, 3.2) as a numeric bound; a value in the wrong form for the
dialect (a boolean under 2020-12, a number under 3.0) is reported and dropped instead of silently
accepted as a degenerate constraint. A valid 3.0 boolean exclusive bound, which the parser models as
a number and flags, no longer errors.
scalar component (
{minimum: 5}or{type: number, minimum: 5}) and a$refto an internal scalarsub-schema both keep their constraints on the aliasing node — unlike a property (whose constraints
live on the
Property), the alias is otherwise the only node that could hold them. A sub-schema readfrom both its owning property and a
$refthat hoists it de-duplicates a malformed-bound diagnosticper source pointer, so it is reported once.
Test plan
numeric-precisionconformance corpus entry exercises bounds/defaults/consts/enums/examples beyondfloat64range, at high precision, and in leading-dot form, with a byte-exact golden.$ref-hoisted scalar sub-schemas (typed andtypeless) keep their exact
BigValconstraints, that a type-wrong bound yields a single error, andthat a dialect-wrong
exclusiveMinimum/exclusiveMaximum(boolean under 3.1/3.2, number under 3.0)is reported rather than silently accepted.
ir/bigvaltests cover the canonicalization and non-finite rejection.go test ./...,gofmt -l .,go vet ./...,golangci-lint runall clean; existing goldensunchanged; per-package coverage at 100%.
Closes #16.