Repository navigation
fix(compilers/openapi): read what a draft spells otherwise - #1467
Merged
Merged
Conversation
OpenAPI 3.0 ignores the keywords written beside a schema's $ref. The compiler applies them as 3.1 does and warns of each, but then read each keyword in a different dialect: `nullable` as JSON Schema 2020-12 reads it, which defines no such keyword, `exclusiveMinimum` as 3.0 reads it, and 3.0's requirement of `items` beside `type: array` not at all. The warning claimed a 3.1 reading, which was false for `exclusiveMinimum`. Keywords beside a 3.0 $ref are now read as 3.0 defines them, the only reading under which what the author wrote means what it says: a `nullable` there adds null to a `type` written beside it, in the null reading and in what admission reads a schema to admit; with the policy off it is no undefined keyword, so it is not kept verbatim; and a `type: array` there with no `items` draws openapi/missing-array-items. The warning now says the keyword is applied as in 3.1 and read as in 3.0. The reason openapi/inferred-nullable gives for a nullable beside a $ref no longer says no version gives it an effect. A 3.1 one names the dialect, a 3.0 one with no type beside it says it writes none, and one whose null the conjunction removes names what removed it: an allOf beside it, or what the $ref lands on.
The rule table in internal/oas states, per minor, each fact the schema lowering branches on: that `nullable` is a Schema Object field only in 3.0, that 3.0's exclusive bounds are booleans and 2020-12's numbers, and that 3.0 requires `items` beside `type: array`. The lowering read none of those rows. It asked lowering.Ctx.Is30, which parsed the document's version, so each fact was stated twice, in two packages, with nothing holding the two to each other. oas.SchemaRules is the Schema Object's rows at one minor, built once when a context is made, and Ctx.SchemaRules replaces Is30. The lowering now asks whether the dialect defines `nullable`, what shape `exclusiveMinimum` takes, and whether a schema whose `type` is `array` must write `items`: the rule's condition is read from its row too, so the lowering's test of `type` is the table's. SchemaRules has no accessor for its minor, so a holder of one cannot branch on it, and a version the table does not cover answers with rows defining nothing, as Is30 answered false. The one output change is the reason openapi/inferred-nullable gives in a 2020-12 schema, which now says the dialect it is read in defines no nullable keyword rather than quoting the declared version.
A document a reference reaches beside the source was read at the source's minor, whatever it declared. A 3.0 root reaching a 3.1 response refused the response's numeric exclusiveMinimum, a 3.1 root reaching a 3.2 response required the description 3.2 dropped, a 3.0 schema reached from 3.1 lost its nullable and its required items, and Document.Sources recorded every such document as read at the source's minor. Each document is now read at the minor it declares: its schemas in that minor's dialect, its reached objects held to that minor's table, and its Document.Sources entry naming it. A document declaring no version, such as a bare schema file, or one the table does not cover, such as 3.3.0 or a Swagger 2.0 file, is read at the source's minor as before, and the compile still stands. load.Other carries the Schema Object's rows at its minor, and lowering.Ctx.SchemaRules answers with those of the document its scope reads. The null reading crosses documents only at a $ref, so each alias and the end of a chain are read in their own document's dialect, and the walk returns to its own once it has read them. What admission reads crosses documents through declaration sets as well, so it reads each schema in the dialect of the document the bindings place it in. The load phase now builds the rule table once and shares it between judging the source, judging what references reach, and reading each document's rows, where it built the table twice before.
That no lowering asks the OpenAPI version a document declares, and reads what a construct means at that minor from the rule table instead, was a sentence in the lowering specification that nothing enforced. A lowering that read the version again would state a fact the table already states, and nothing would hold the two to each other. The versionreadings analyzer, which make vet runs, refuses every reading of the version outside compilers/openapi/internal/load and compilers/openapi/internal/oas, in production files: the parser library's version field and GetOpenAPI method of its document type, promoted or not, oas.ParseMinor and load.SupportedMinor. The library names other fields OpenAPI too, a walk's among them, so the field is the one its document type declares, compared as an object rather than by name. Detection's own probe of a source's version is no use of the library's type, so routing a source by its version stays allowed. The plants cover each form a use takes, the two exempt packages, a look-alike type, a test file and a package outside the module.
Two sites the change reads its dialect at had no test that told a wrong reading from the right one. The agreement of an allOf's value bounds reads each conjunct in its document's dialect. A numeric exclusiveMinimum is a bound from 3.1 on, so two writers of one meet and fold; in 3.0 the lowering reports and drops it, so its writers state nothing and stay apart. The schema readers ask the rows of a schema's document for every schema they read across the bindings, most often the source's. Those come without a lookup by name, which would parse the name as a URL on each miss; an allocation count holds them to it.
Reading each schema in its own document's dialect gave admission two methods, past the 20 internal/archtest allows a type under compilers/. Neither needed the walker's state. Which dialect a schema is read in follows from the context and where the bindings place the schema, so it is a function of those, definesNullableAt. What a schema writes takes that dialect as an argument, so each reader says which one it reads in: the schema's own document's across the bindings, c's for a schema of c's document.
The document-minor table carried a check function per case, which rule 6.2 of the styleguide reads as a red flag; each case now states the bounds and the missing-items warnings it expects. The helper takes the nullable-widening policy rather than a bare bool, the two-minor test preallocates what it collects, an allocation count reads its answer outside the counted function, and a comparison of rows holding an unexported field compares their identity.
testifylint refuses assert.True over a comparison and asks for assert.Equal, but assert.Equal compares deeply, so rows built twice would pass it. The claim is that a minor's rows are built once, so the test checks identity itself and reports a miss with t.Error.
Three places still said the keywords written beside a 3.0 schema's $ref are read as 3.1 reads them, which stopped being true when they began to be read as 3.0 defines them: the IR's unmodeled-content chapter, the comment on the Schema Object's row in the rule table, and the reason the validation canary gives for its ref-siblings class. Each now says they are applied as 3.1 applies them and read as 3.0 defines them. The nullable rule's paragraph is rewrapped where an earlier edit left a long line.
OpenAPI 3.1 has a root Schema Object's `$schema` choose the JSON Schema dialect it is read in, and a document's jsonSchemaDialect stand in for it where none is written. Both were kept verbatim and ignored, so a draft 4 schema's `minimum: 5, exclusiveMinimum: true`, which means x > 5, was refused as a malformed 3.1 bound. The rule table now has rows by dialect as well as by minor: the OAS dialect and JSON Schema 2020-12 read as the minor's own keywords, and draft 4's keywords stand in for them where a schema names it. A dialect changes the keywords alone; the fields and rules OpenAPI adds to a Schema Object stay the minor's. The binder notes the `$schema` each schema no other holds writes, a document of schemas naming its dialect at its root, and the load phase hands the lowering the rows each such root governs by the nodes of its tree, with each document's jsonSchemaDialect as the default. The exclusive-bound reading asks the rows of the schema it reads by its node, so a schema a `$ref` names is read in its own root's dialect whichever declaration lowers first. A dialect the table has no rows for, a later draft or one of the author's, is read as the minor's own, with an openapi/unread-dialect warning at the `$schema` or jsonSchemaDialect naming it. A 3.0 document defines neither keyword, so nothing changes there.
A draft before 2020-12 still has the keywords it spells otherwise read as 2020-12 reads them, the table has no rows for drafts 6, 7 and 2019-09, and a $schema at the root of a resource an $id embeds chooses nothing. The dialect rule's Status and the code that draws each line now name the issue tracking it.
A `$schema` or jsonSchemaDialect that is no absolute URI, such as a number or a relative path, is already an openapi/validation finding of the rule table, which formats both rows as absolute URIs. It also drew an openapi/unread-dialect warning at the same place, two findings for one defect. SchemaRules now reports a dialect as unread only where an absolute URI names one the table has no rows for; a malformed value reads as the minor's own, as before, and is reported once. The dialect rule also states what a node an alias places at several positions reads: the dialect of the first root whose tree holds it, at every position. No reading today tells one valid node's two dialects apart, since a valid exclusive bound in one is malformed in the other.
The parser library holds a 3.1 schema to its version's 2020-12 meta-schema whatever dialect the schema names, so it refuses the new conformance case's draft 4 bounds; the validation oracle now gives that disagreement a reason of its own. The annotation package's own tests read the info of each dialect keyword, `$vocabulary`'s included, which only the end-to-end case reached, and the new tests are written as the linters ask.
openapi/unread-dialect joins the diag package's list of every code it declares, and the load phase spells the jsonSchemaDialect pointer itself rather than reaching the ids package, which its layer may not import.
OpenAPI 3.1.1 has a `$schema` choose the dialect of any Schema Object that is a schema resource root, and JSON Schema roots a resource of its own at a subschema whose $id names another URI. Only a Schema Object no other holds, or a schema file's root, chose one, so a draft 4 property with an $id beside its `$schema` was refused for its boolean bound. The binder now counts a schema whose $id roots a resource as a dialect root too, and the load phase governs roots innermost first, so a nested root keeps its own dialect for what it holds while the rest of the outer root reads the outer's. An $id naming its own document roots no resource and still chooses nothing.
A 3.1 or 3.2 schema naming draft 6, draft 7 or 2019-09 drew an openapi/unread-dialect warning and was read as 2020-12, as the rule table had rows for draft 4 alone among the earlier drafts. Each now has its keyword rows, every one citing its sentence in that draft's core or validation text, and each dialect states whether it ignores the keywords beside a `$ref`, as drafts 4, 6 and 7 do and 2019-09 does not (SchemaRules.IgnoresRefFields).
The rule table leaves the shape of minimum, maximum, multipleOf and the
exclusive bounds to the schema lowering, which reads them off the raw
node. The lowering reported a bound no decimal spells, and an exclusive
bound in the other form than its dialect gives it, only where it read
the schema's constraints. A schema it never reads that way went
unreported: one under not, if, propertyNames or contains, one beside an
enum, or an array's value bounds. `{not: {exclusiveMinimum: true}}` in a
3.1 document compiled with no diagnostic.
The check now hands each such value to its caller (oas.Options.Owned),
already placed as its own findings are. The load phase judges each once
the bindings say which dialect the schema is read in, by where the
schema is, for the source and for every object a reference reaches, and
reports it at the keyword. The lowering still reads and drops what it
cannot use, and reports neither.
A 3.1 or 3.2 schema naming a JSON Schema draft before 2020-12 was read as 2020-12 reads it wherever a keyword is spelled otherwise there. Each is now read in the schema's dialect: - items written as a list is a tuple in drafts 4, 6, 7 and 2019-09, its rest the additionalItems beside it. Elsewhere a list is reported as openapi/invalid-items-list by the load phase at every schema the check reads, and items reads as absent. - dependencies is kept as a validation-only keyword in drafts 4 to 7. - Draft 4's id names a resource and gives a $ref its base, a fragment alone declares a plain name in drafts 4 to 7, and definitions holds schemas. An $id's format is judged in the schema's dialect. - A keyword beside a $ref, which drafts 4 to 7 ignore, is applied as 2020-12 applies it and warned of as a later version's construct. The binder and the load phase now share one rule for which $schema chooses a dialect: a schema no other holds, or one rooting a resource by an identifier spelled as its embedder or its own dialect spells one. A schema is read in the dialect of where it is, so a node an alias places under two roots reads each one's.
The JSON Schema drafts before 2020-12 spell a plain name as a letter, then letters, digits, '-', '_', ':' and '.', where 2020-12 admits a leading '_' and no ':'. A 2019-09 `$anchor: a:b`, or draft 7's `$id: '#a:b'`, was refused or never declared, and a $ref naming it went unresolved, while a 2019-09 `$anchor: _x` was accepted. The check now hands `$anchor` and `$dynamicAnchor` to the load phase, which judges each name in its schema's dialect as it does an `$id`, and the binder declares an anchor by its schema's dialect's grammar. A $ref's plain-name fragment is looked up under either grammar, since only a declared name resolves, and the message for one no schema declares names a draft's fragment identifiers too.
…rom-table Two conflicts. internal/archtest's allowlist for lowering keeps main's nodeview and yaml with the branch's oas, and its comment says both. lowering's version test keeps the branch's assertions under main's new BinaryMedia parameter, which the branch's other New calls now pass too. main's multipart part reading (#1435) asks Ctx.Is30, which this branch removes: a part writing no type is a file from 3.1, whose Encoding Object defaults its contentType to application/octet-stream. Until the table states that default, it asks whether the dialect defines nullable, which answers the same at every minor.
lowering.New built the whole rule table a second time per compile to read the source's Schema Object rows, while the load phase already held one. The load phase now reads the source's rows off the table it judged the source against, through the dialects that reads each reached document, so a document read at the source's minor shares them, and hands them over as load.Document.Rules. A context reads them from what WithLoaded carries; one given no load phase still reads the version its document declares. A compile now builds the table once. BenchmarkCompile_Petstore, three runs each at GOMAXPROCS=1: 1,442,131 to 1,319,664 B/op and 12,664 to 12,113 allocs/op.
A multipart part writing no type is a file from OpenAPI 3.1, whose Encoding Object defaults its contentType to application/octet-stream (3.1.1 and 3.2.0, "The default values for `contentType` are as follows": a schema with type absent takes application/octet-stream; 3.0.4's table has no such row). That reading asked the version, and the merge of main kept it by asking whether the dialect defines nullable. The table now states it: a Field carries Defaults, each a value for one case at the minors its span holds, and the Encoding Object's contentType defaults a schema writing no type to application/octet-stream from 3.1. oas.EncodingRules is the Encoding Object's rows at one minor, as SchemaRules is the Schema Object's, and load.Rows pairs the two, so a document's rows, the source's included, come from one table per compile. lowering.EncodingRules reads them in the document whose content the context reads, and the part is then judged as naming that media type. The table's own tests hold each default to its citation, to minors its field is defined at, and to one value per case at each minor.
The rule that no lowering reads the version a document declares was a go/analysis analyzer of its own in internal/lintrules. forbidigo expresses it: two patterns refuse the parser library's version field and accessor, and oas.ParseMinor, outside the OpenAPI compiler's load phase and rule table, which an exclusion excuses, and outside tests, as the analyzer did. A member promoted through an embedding of the library's document is the one spelling forbidigo does not see, and nothing embeds it. A struct literal writing the version, which the analyzer refused as a read, is no use forbidigo matches. load.SupportedMinor, read nowhere outside load, is unexported, so the compiler confines it and no pattern is needed. The plants move to scripts/lint-planted.sh's tree: look-alikes of load and oas under internal/lintrules/testdata/planted read the version and draw nothing, and one of a lowering draws each refusal, a context's c.Doc.GetOpenAPI() among them. The analyzer, its tests and its plants go, and internal/lintrules and cmd/morphic-vet return to main's.
A 3.0 $ref site writing type: array and no items drew
openapi/missing-array-items, which says only the schema's other keywords
constrain its elements, even where the $ref lands on a schema writing
items: {$ref: X, type: array} over X: {type: array, items: {type:
string}} lowers to an alias over X, a list of strings, and the warning
was false. The same conjunction spelled {allOf: [{$ref: X}, {type:
array}]} drew none, as an inline allOf branch restating what a
refinement names is not reported.
The keywords beside a $ref are now held to the rule with what they
conjoin with: the site is reported only where admission's reading of
the whole site, its aliases and allOfs included, finds no items. An
items it finds holds however much of the rest it read, and a reading cut
short before reaching one leaves the site reported as written.
Three statements had drifted from what the compiler does. diag.LaterVersionConstruct said a keyword beside a 3.0 $ref is lowered as in a 3.1 document, a line after saying it is read as 3.0 defines it, and later-version-construct opened the same way. openapi-versions said a version that is not major.minor.patch is reported, which holds for the source alone: a document a reference reaches is read at the minor such a version names, with no finding, since only what a reference reaches is judged. TestDocumentMinor_EachDocumentIsReadAtTheMinorItDeclares now pins that case. TestSupportedMinor_AllocatesNothing pinned a cost by the lowering asking the version of every schema, which it no longer does: the version gate runs once per document, so the test goes. TestParseMinor_AllocatesNothing keeps its pin under the reason that remains, Check and CheckFormat asking it of every object the load phase judges.
nulls.lands reads each alias of a $ref chain, and its end, in the dialect of the document holding it, and set the read's own back by hand after the last. A return added between the two would have left the rest of the read in another document's dialect. The restore is now deferred, so it holds on every way out; the closure does not escape, and the deferred call costs no allocation.
A Default held on every Field grew each of the table's Field rows by a slice header, and the table is built once a compile: the Petstore compile allocated about 11 KB more for one default the Encoding Object states. Defaults now sit on their Object beside its Rules, each naming its field, as a rule does, and the table's tests hold each to a field its object defines at every minor the default holds at. BenchmarkCompile_Petstore, three runs each at GOMAXPROCS=1: 1,332,877 to 1,322,027 B/op, 12,118 allocs/op either way.
missing-array-items' rule said a $ref site is reported where nothing it conjoins with writes an items, while the reading behind it, admission's, holds the elements to no items for items: true, and none for an items only a union's branches write. Both still draw the warning, and the rule and missingArrayItemsBesideRef now say so; TestLower_MissingArrayItems pins each. The table's descriptions name the defaults it now states: the oas package doc, the validation rule, and openapi-versions, whose reached document's Encoding Objects take its minor's defaults. Three comments are made exact: the load phase's dialects reads the source and each reached document, not the source first; the forbidigo rule names tests among what its exclusions excuse; and the test exclusion says a test may read a fixture's version.
A reading of a schema's dialect by its node, which folding an inline allOf branch and reading a body as a file both make, answered for a node no `$schema` root holds with the rows of the document whose content the lowering was reading, not those of the document holding the node. A draft 7 document's `dependencies` in an allOf branch the source reached through a $ref kept its branch verbatim, as though the conjuncts disagreed, and a draft 7 source's `definitions` in a body schema another document reached was an unknown keyword, so the body was no file. The load phase now notes, for each node of another document that no root governs, that document's own rows where they are not the source's, and the reading by node falls back to the source's rows rather than the context's, so it answers alike from every document.
Every reading of a schema's dialect by its node asks it of a schema's mapping, and the only schema a scalar can be is a boolean, which writes no keyword, yet the walk noting each node's rows noted every key and value too. Now that it covers the whole of another document whose rows are not the source's, that was most of its cost: a 3.1 source reaching 20 schemas of a 3.0 document of 20,000 compiled with 6% more instructions retired and 9% more peak memory than before the walk covered it, and with about 2% more of each once scalars are left out. Lists are still noted, so a list that aliases place many times is walked once.
Drafts 6 and 7 name a subschema by an identifier that is a fragment alone, and require it to open with a letter, then letters, digits, '-', '_', ':' and '.'. Draft 4 states no grammar for one: its `id` is any URI, and `#foo` names what the URI it makes names. The binder held all three to the drafts' plain-name grammar, and nothing judged it, so a draft 4 `id: '#_inner'` or `'#1x'` declared nothing and a `$ref` to it went unresolved, while a draft 7 `$id: '#_x'` compiled with no finding where main reported it. The draft 6 and 7 `$id` rows now cite that sentence and give the identifier a format, PlainNameURI, that the load phase judges in the schema's dialect. The binder declares a fragment alone by the grammar its dialect's rows give (bind.DialectRule.PlainFragments): the plain-name grammar there, and in draft 4 any fragment no JSON pointer spells, read as a $ref's fragment is. A $ref fragment no JSON pointer spells is looked up as a plain name, as every dialect reads one, and keeps its "no JSON pointer" reason where no schema declares the name and no grammar admits it.
The row cut "a URI-Reference" to "a URI" at a line break of the draft's text, which then said something the draft does not.
Folding an inline allOf branch, reading a body as a file and reading an exclusive bound asked a schema's dialect of its YAML node, which an alias shares between places. A node aliased under a draft 4 root and a 2020-12 schema then read one dialect at both, chosen by the roots' names: a branch's `dependencies` folded at the 2020-12 place, where no reader kept it, so it was lost with no finding; a body writing `id` was a file at the 2020-12 place; and a valid draft 4 bound was dropped while a malformed 2020-12 one was kept beside its error. The typed bindings now place each schema by the model it holds (typed.Bindings.SiteOf), which decoding builds afresh for every path to a node, and those readers ask the rows of that place, in whichever document. The index is kept only where a schema names a dialect by `$schema`: otherwise each node reads its own document's, which its node tells. This also lifts the limit that the allOf fold read a conjunct's exclusive bounds by its node.
Where a draft's list of `items` lowers to no tuple, as with no `type: array`, the list was kept as a degraded lowering and the `additionalItems` beside it as an unknown keyword, under out_of_scope, the reason the IR keeps for runtime and SDK policy. Its 2020-12 twin, `items` beside `prefixItems`, is kept as a degraded lowering beside its list. The shape census now counts `additionalItems` beside a list of items as homed by a Tuple alone, so it is kept with its list under one finding. Beside an `items` that is no list it is the rest of no tuple and stays an unknown keyword.
An `items` list where the schema's dialect reads no tuple now suggests `prefixItems` or naming a draft that reads one, and a schema past the bound on decoding again suggests moving the schemas below it into a component, which reads them.
The exclusive-bound check spelled out the boolean-scalar test the package declares as isBoolean.
A schema whose type list makes it a union lowers each variant at a
pointer of its own, below the schema's. A draft's tuple there read its
entries and rest at that pointer, where the load phase builds none, so
the array variant of `{type: [array, object], items: [...],
additionalItems: ...}` held the top type for each entry and for its
rest, and the census, seeing a Tuple, then left `additionalItems` kept
nowhere at all.
The typed bindings now also place by its model each schema writing a
list of items, whatever the dialects named, and the tuple reads its
entries and rest where the bindings place the schema writing them. That
lookup is now shared with the readers of a schema's dialect (placedAt),
whose crossing into another document a new case pins.
An `items` list where the dialect reads no tuple suggested `prefixItems` or naming a draft by `$schema`, neither of which a 3.0 document can do. A 3.0 document's finding now says that 3.0 writes no tuple, which 3.1 writes as `prefixItems`. Each finding's advice, and the one past the bound on decoding again, is now pinned.
A plain name no grammar admits was reported as naming no JSON pointer even where the document's identifiers were read only up to the merge bound, though a draft 4 `id` past the bound may declare it, while a name a grammar admits reported that bound. Only a name no schema declares, with every identifier read, is now reported as no pointer.
The references, tuples, shape-applicator and dialect-keywords rules now list the tests that pin what they say of the drafts, and the rule for an `additionalItems` beside a list of items says it holds in every dialect, as the census reads it.
The PlainNameURI format's description, used in the finding about an identifier, reads as one clause, and two comments say what they meant.
The finding about a draft 6 or 7 `$id` naming a fragment alone that is no plain name said the reference "names a letter, then ...". It now says it names a plain name, as the sibling formats word theirs, and the message is pinned.
Every row set defining `prefixItems` also defines `$schema`, so asking for both decided nothing the first did not.
The typed bindings' reader of whether a schema writes `items` as a list is now listsItems, the name bind gives its twin.
The tuples rule said a position lowering to no Tuple keeps its list, though a type list's array variant is the tuple and leaves nothing on the union; it now says so apart, and says where an `additionalItems` beside a list no dialect reads is kept, which a 2020-12 and a 3.0 case now pin. Two comments said the typed bindings place nothing where no schema names a dialect, though they place a schema writing a list of items, which their own test now pins.
Four test doc comments opened with their first words twice, joined by a second comment marker, and one left "is" unfinished where "placed" had been dropped.
The PlainNameURI format's description glossed the name with a colon, where the AnchorName and PlainName descriptions say "a name of a letter, then ...". It now says its fragment is such a name, as they do.
The comment on itemsListRemedy named one row set twice and said of a single keyword that 3.0's rows define none.
This was referenced Nov 6, 2026
Three lines the draft readings added to docs/compilers/openapi.md and docs/ir/boundaries.md ran to 102, 115 and 156 characters, and one stopped short mid-paragraph; each is rewrapped at 100, no word changed.
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
A 3.1 or 3.2 schema can name a JSON Schema draft before 2020-12 as its dialect. Until now every
keyword such a draft spells otherwise was read as 2020-12 reads it. This reads each in the schema's
dialect, adds rows for drafts 6 and 7 and 2019-09, and closes a reporting gap the work exposed.
The first seven commits each build and pass on their own:
and each dialect states whether it ignores the keywords beside a
$ref.minimum,maximum,multipleOfand the exclusive bounds to the schema lowering, which reported a malformed one onlywhere it read the schema's constraints. On
main,{not: {exclusiveMinimum: true}}in a 3.1document compiles with no diagnostic, and so does a malformed bound under
if,propertyNamesorcontains, beside anenum, or besidetype: array. The check now handseach such value to its caller (
oas.Options.Owned). The load phase judges it once the bindingssay which dialect the schema at that position is read in, for the source and for every object a
reference reaches. The lowering still reads and drops what it cannot use, and reports neither.
itemswritten as a list is a tuple in drafts 4, 6 and 7 and 2019-09, its rest theadditionalItemsbeside it. In any other dialect a list isopenapi/invalid-items-list,judged by the load phase through the same hand-over, and
itemsreads as absent.dependenciesis kept as a validation-only keyword in drafts 4 to 7.idnames a resource and gives a$refits base, and is kept verbatim as$idis. An identifier naming a fragment alone declares a plain name in drafts 4 to 7, and
definitionsholds schemas, decided as$defsis. An$id's format is judged in theschema's dialect, so draft 7's
$id: '#leaf'is accepted where 2020-12's is refused.$ref, which drafts 4 to 7 ignore, is applied as 2020-12 applies it andwarned of as
openapi/later-version-constructat its key, bar$schema, extensions and theidentifier (item 22).
$schemachooses a dialect: a schemano other holds, or one rooting a resource by an identifier spelled as its embedder spells one
(JSON Schema 2020-12 §9.3.1) or as its own dialect does. A
$schemanaming no dialect,which no absolute URI names, chooses none in either. Before this, the binder switched dialect
at any
$schema.lowering.SchemaRulesAt), so a node an aliasplaces under two roots reads each one's, as the constraint recorded on openapi: keywords a draft before 2020-12 spells otherwise are read as 2020-12 reads them #1457 asks, by every
reader since item 32.
letter, then letters, digits,
-,_,:and., where 2020-12 admits a leading_and no:, and draft 4 states none (item 30). A 2019-09$anchor: a:b, or draft 7's$id: '#a:b', was refused or never declared, and a$refnaming itwent unresolved.
$anchorand$dynamicAnchorare now judged in their schema's dialect, as an$idis, and the binder declares an anchor by that dialect's grammar.idwherever$idis read. Once the binder readsidas a base, every otherreader of
$idmust read it too.$schemaor itsjsonSchemaDialect, is refused for anidnaming a location on disk, as any document is foran
$idreaching a local file (openapi: a document fetched over HTTP can name a local file, and it is read #929). Without this, a remote draft 4 document could rebase arelative
$refonto the disk. Theidis judged as the binder reads it(
bind.IdentifierURI): cut at#, trimmed and resolved by RFC 3986, so' FILE:///x/#'isrefused too, as is a single-letter scheme, a Windows drive's. A relative
idstays on thenetwork, and in a document naming no dialect that reads
id, noidrefuses anything.$idis kept and said to be read for nothing, rather than "read only forreference resolution". The identifier keyword comes from the rows
(
oas.SchemaRules.Identifier), one reading the binder's rule shares.dependenciesis bound, as one in 2020-12'sdependentSchemasis.itemslist read off the raw node in the two predicates that relied on the parser'smodel holding the false schema for one, which no
docs/parser-library.mdentry states.idjudgement made the binder's own, as the previous item describes.Then #1459 and
mainare merged in, the first as its last head, whose treemainlanded, and:$schemanaming no dialect. fix(compilers/openapi): read a schema in the dialect it names #1459's final round had the loadphase skip one, as though it were not written, while this branch's binder read that schema as
the table reads the value, in the minor's own dialect. In a document defaulting to draft 4, a
$schema: 'rel/x'besideidthen left theidundeclared, and a$refnaming it wentunresolved and was fetched where external references are allowed. The binder now asks
(
bind.DialectRule.NamesNone), and the load phase meets no such root.main. ReadingidhaddialectAtmake room for adiagnostic per keyword on every schema, about 30 MB on the Cloudflare schema, and the binder's
frame, which now carries a dialect, made each child slice a walk built 40% larger. The former
starts from no slice again, and a walk appends a node's children to its stack.
idrefuses its document only where a draft reads it. As first written, thecheck refused a remote 3.1 document whose example wrote
{id: 'u:42'}, a one-letter scheme readas a drive's, which
mainreads (exit 0 became 1). It now asks the dialects the document names,as item 5 states.
$refsibling is warned of only where applying it reads otherwise. Over 560public JSON Schema files, 10 of the 49 warnings named a key whose reading applying it leaves
alike: the
definitionsor$defsa root writes beside the$refnaming one of them, and keysno row defines, such as
nullable, or a$idin draft 4, which spells its identifierid. Only a field the draft's rows define is warned of now,OpenAPI's own among them, bar
$schema,definitionsand$comment, and since item 22 theidentifier.
drew a second error at its absent rest, which the document does not write; 2020-12's
prefixItemsdid so onmaintoo, at the absentitems. A schema the document does not writenow lowers as the top type before the depth check.
idis kept without a list per schema, about 3.7 MB on a document of 20,000draft 4 components.
draft 4 to 7 schema to the drafts' plain-name grammar, though neither defines
$anchoror$dynamicAnchor, so draft 7's$anchor: _xdeclared nothing and a$refto it wentunresolved, where
mainresolved it; validation judged such an anchor by no grammar. The drafts'grammar now applies to drafts 6 and 7's fragment identifier and 2019-09's
$anchor, and ananchor keyword the dialect defines none of is read and judged as 2020-12 reads it (openapi: a 3.0 document's prefixItems lowers to a tuple with no diagnostic #820).
docs/ir/boundaries.mdnames the drafts'dependenciesamong the validation-only keywords.idwitha fragment identifier, and a 2019-09 anchor holding a colon, each compiling clean so the
oracles run on it. The
dialect-keywordsstatus names the formats the load phase judges in aschema's dialect beside the shapes.
$idnaming a fragment is refused again. Item 3 judges an$id's format in itsschema's dialect, and 2019-09's row gave
$idthe URI-reference format, so a 2019-09$id: '#foo', which its text forbids ("MUST NOT contain a non-empty fragment") andmainrefused by 3.1's rule, drew no finding. The row now cites that sentence and holds
$idto theformat 2020-12's row does. Draft 4's
idrow gains the URI format its text states, which nocheck reads until openapi: a schema read in a JSON Schema draft is held to its minor's keyword rows #1466, and
docs/ir/boundaries.mdnamesidamong the resource keywords.brought in with
mainread an array's elements fromprefixItemsanditems, where theparser's model holds
falsefor a draft's list of items, so a draft 7 part{type: array, items: [{}, {}], additionalItems: false}was no file where the same tuplewritten with
prefixItemswas. It now reads a draft's entries andadditionalItems(
schema.ListedTuple). Every other reader ofitemsandprefixItems, found by a sweep,already tells a list from one schema.
$refnames nothing. Drafts 4, 6 and 7 ignore every keywordbeside a
$ref, but the binder read an$idthere, or draft 4'sid, as 2020-12 does. On theJSON Schema test suite's "$ref prevents a sibling $id from changing the base uri", a draft 7
$refresolved to the string its sibling$idrebased it onto, where the draft names a number,and a draft 7
{$id: other.json, $ref: '#/definitions/A'}went unresolved. Where a dialectignores a
$ref's siblings (bind.DialectRule.IgnoresRefFields), an identifier beside one nowgives it no base, roots no resource and declares no plain name, and a schema writing a
$schema,an identifier and a
$refinside a schema read in such a draft roots none either. The identifieris not warned of as applied, and its info says it is read for nothing. 2019-09 and 2020-12 still
read it as the
$ref's base.$ref-sibling warning names the dialect as read. It said the schema names the dialectignoring the keyword, though the dialect is as often its document's
jsonSchemaDialector anenclosing root's
$schema.{items: [5], additionalItems: 5}in adraft 7 schema compiled with no finding, where
{prefixItems: [5], items: 5}draws two errors: theload phase's check of the schemas built for the entries skipped any the parser read as a boolean.
Each entry and rest is now reported as its 2020-12 twin is, an entry by the check of what the
parser built there, and the rest as a field beside its
itemslist(
oas.SchemaRules.TupleRestFinding): an error where no schema spells it, and fornullthewarning a
nullfield draws, read as left out.falseschema where a keyword beneath it fails to decode under its own name, though the positionwrites a mapping (
docs/parser-library.md#failure-beneath-itself, pinned by a canary). A draft'slist of
itemsis such a keyword, so a valid tuple as an array'sitems,{type: array, items: {type: array, items: [{type: string}]}}, lowered as a list of a closedempty model, and an array of objects one of whose properties is a tuple lost the whole object, as
a public draft 7
friends.jsondid. The typed builder now decodes each such schema again from itsmapping, in place, so every reader of the model sees it, up to
typed.MaxMendsof them one belowanother; one past them is the top type with an
openapi/degraded-constructerror, as the schemadepth bound gives. openapi: a keyword failing to decode beneath itself turns the outer schema false #1299's invalid inputs now lower as their rule says too:
{type: array, items: {type: string, items: null}}is a list of strings.application/octet-streamwriting draft 4'sid, a draft'sdefinitionsor 2019-09's$recursiveAnchorwas no file, where$id,$defsand$dynamicAnchorleave one, and a draft'sdependenciesin an inline allOf branch kept the branch verbatim, wheredependentRequiredfolds onto the position.
schema.TwinKeywordnames the 2020-12 keyword a spelling stands for inthe writing schema's dialect, for the file-body reading and the allOf fold, and the
validation-only reader keeps a draft's
dependenciesat each branch folding into a position too.Union branches already read the spellings alike.
$schemaand$vocabularymake a body no filein every dialect,
mainincluded: openapi: a $schema or $vocabulary in a body schema makes it no file #1521.main's remembered base walk reads each schema's dialect. fix(compilers/openapi): bound costs that outgrow the document #1470 answersTable.Basefrom awalk it remembers. The merge has that walk carry the dialect and whether a schema holds each
position, stepping and entering as
resources.holdingdoes, and its differential test nowcovers two fixtures naming dialects. test(archtest): hold calls into unimported packages to a ratchet #1504, merged next, counts the calls a package makes
through a function it is handed into one it does not import: this branch adds four, of kinds
its entries keep under skills: say when a function value may cross into another package #1477's rule,
bind.Config.Dialect, a dialect's rows built only once aschema names it, and
oas.Options.Owned, a traversal callback, with theLocatethat placesan
Owned's finding, so those counts rise with entries saying so.bases through the dialects schemas name only once it had found one naming any, a test on the
whole run, and a
$refthat a source$ref's chain meets in another document is resolved beforethat document is read. Where the source names no dialect, a draft 7 document's
$idbeside that$refrebased it, and a draft 4 document's rootidgave it no base, so its{$ref: 'a.json'}beside an entry identified
a.jsonwent unresolved, on953c11b5atoo. The test is now perdocument (
identifiers.readsDialects).a schema's dialect by its node, which folding an inline
allOfbranch and reading a body as afile make, gave a node no
$schemaroot holds the rows of the document whose content thelowering was in. A draft 7 document's
dependenciesin anallOfbranch the source reachesthrough a
$refkept its branch verbatim as though the conjuncts disagreed, and a draft 7source's
definitionsin a body schema another document reaches read as an unknown keyword, sothe body was no file. The load phase now notes another document's own rows by node where they
are not the source's, and the reading falls back to the source's rather than the context's
(
load.Dialects.Of,lowering.SchemaRulesOf).Then
mainis merged in (2123d296e), touching none of the branch's files, and:identifier naming a fragment alone to open with a letter; draft 4 states no grammar, its
idbeing any URI. The binder held all three to the plain-name grammar and nothing judged it, so a
draft 4
id: '#_inner'or'#1x'declared nothing and a$refto it went unresolved, and adraft 7
$id: '#_x'compiled with no finding wheremainreported it. The draft 6 and 7$idrows now cite that sentence and give the identifier a format the load phase judges in the
schema's dialect (
oas.PlainNameURI), and the binder declares a fragment alone by the grammarthe rows give (
bind.DialectRule.PlainFragments), in draft 4 any fragment no JSON pointerspells, read as a
$ref's fragment is. A$reffragment no JSON pointer spells is looked up asa plain name, as every dialect reads one, and keeps "its fragment is no JSON pointer" where no
schema declares it and no grammar admits it.
$refrow quotes its sentence whole, "a URI-Reference" where it read "a URI".allOfbranch, reading a body as a file and reading an exclusive bound asked a schema's dialect of its
node, which an alias shares, so a node under a draft 4 root and a 2020-12 schema read one
dialect at both, chosen by the roots' names. A branch's
dependenciesthen folded at the2020-12 place, where no reader kept it, and was lost with no finding; a body writing
idwas afile there; and a valid draft 4 bound was dropped while a malformed 2020-12 one stayed beside
its error. The typed bindings now place each schema by the model it holds
(
typed.Bindings.SiteOf), which decoding builds afresh for every path to a node(
docs/parser-library.md#alias-expansion), and those readers ask that place's rows. The indexis kept only where a schema names a dialect by
$schema, as otherwise each node reads its owndocument's. This lifts the limit that the fold read a conjunct's bounds by its node.
list of
itemslowers to no tuple, as with notype: array, itsadditionalItemswas kept asan unknown keyword under
out_of_scope, the reason the IR keeps for runtime and SDK policy,while its 2020-12 twin
itemsbesideprefixItemsis a degraded lowering. The shape census nowholds it with its list under one finding (
docs/compilers/openapi.md#shape-applicator); besidean
itemsthat is no list it is still an unknown keyword.itemslist where the dialect reads no tuplesuggests
prefixItemsor naming a draft, or in a 3.0 document what 3.1 writes (item 37), and aschema past the decoding bound suggests moving the schemas below it into a component, which
reads them.
judgeSourceand
dynamicRefName, andmissingArrayItemssays why it reads anitemslist off the raw node.makes it a union lowers each variant at a pointer of its own, where the load phase builds no
tuple entry, so the array variant of a draft's
{type: [array, object], items: [...], additionalItems: ...}held the top type for each entry and its rest, and since item 33 itsadditionalItemswas kept nowhere. The typed bindings now also place by its model each schemawriting a list of items, whatever the dialects named, and the tuple reads its entries and rest
where the schema writing them is placed, a lookup shared with item 32's readers.
items-list advice follows the version. A 3.0 document can write neitherprefixItemsnor a
$schema, so its finding says 3.0 writes no tuple, which 3.1 writes asprefixItems.identifiers were read only up to that bound, as a draft 4
idpast it may declare the name.additionalItemsbeside a list says it holds in every dialect.
pinned; item 46 words it as the sibling formats word theirs.
items-list advice is keyed onprefixItemsalone, as every row set defining itdefines
$schematoo.listsItems.is the tuple, leaving nothing on the union, and an
additionalItemsbeside a list its schema'sdialect reads as no tuple is kept as at a position lowering to no tuple, now pinned at 2020-12
and 3.0. Two
comments that said the typed bindings place nothing where no schema names a dialect now say
they place each schema writing a list of items, which their test pins.
sentence. A check refusing such a line is archtest: refuse a doc comment line holding a second comment marker #1562.
written alone and no JSON pointer, is a name of a letter, then letters, digits,
-,_,:and
..items-list advice names the rows it reads.changed.
Departure from #1457's expected result
#1457 expects
V's description, beside a draft 4$ref, "not read asV's own". This applies itand warns instead. That follows the decision #871 recorded for the same construct in a 3.0 schema,
whose
$refalso ignores its siblings: lower it as the later version reads it, the reading thatloses least, and say so (
docs/compilers/openapi.md#later-version-construct). A consumer can set awarned keyword aside, and could not rebuild the lowering from a verbatim copy. The identifier is the
one sibling not applied (item 22): applying it changes what the
$refnames, and ignoring it, asthe draft does, loses nothing, as it is kept verbatim either way.
Visible change on input that names a draft
A draft's
definitionsand the entries of its tuple now hold schemas whose$refs are bound, so arelative
$refthere resolves against the root's identifier, as one in 2020-12's$defsalreadydoes. Under an
$idnaming a URL it is fetched where external references are allowed: of the 587public JSON files writing
$schemabelow, 6 test fixtures whose$idnames anexample.comURLfor files on disk went from exit 0 to exit 1 this way, as each does on
mainwritten with$defs.#1051 is the remedy, commented there. A seventh, an OpenAPI 3.0 meta-schema, embeds draft 4's
meta-schema under that meta-schema's own
idwhile keeping the definitions its$refs name atits root, where draft 4 reads them against that
id, so they name nothing.A draft's
additionalItemsbeside a list ofitemsat a position lowering to no tuple is keptunder
degraded_loweringwith the list, with oneopenapi/degraded-constructinfo naming both,rather than under
out_of_scopewith anopenapi/unknown-schema-keywordinfo (item 33). A draft 4idnaming a fragment no plain name spells, such as'#1x', now declares it (item 30).Visible changes on input that was already invalid
The code is unchanged.
itemslist where no dialect takes one isopenapi/invalid-items-listat every minor,rather than
openapi/validation/validation-type-mismatch. The spuriousopenapi/false-schemainfo beside it is gone, since the parser's
falsethere was never written.$refnaming a plain name no schema declares reports that no schema declares it, naming adraft's fragment identifiers among the declarations, rather than "its fragment is no JSON
pointer".
the lowering attributed it to the source.
$idnaming a fragment alone that is no plain name, such as'#_x', is refusedfor that, where
mainrefused every$idnaming a fragment.$refnaming a plain name no grammar admits, such as'#1x', in a document whose identifierswere read only up to the merge bound, reports that bound rather than "its fragment is no JSON
pointer" (item 38).
additionalItemsbeside a 2020-12itemslist, which is reported, is kept underdegraded_lowering, as a draft's beside a list no tuple holds is since item 33, rather than as anunknown keyword.
Reworded messages
Each said
$idor$anchorwhere a draft'sidor fragment identifier is now read too:$id".$anchorand$dynamicAnchor, and two declaring one URI say "as their identifier".$dynamicRefleft unexpanded below a resource, and a$defspointer only a resource's owndefinitions would answer, say "an identifier".
Limitations, stated in
docs/compilers/openapi.mdabove, so a keyword only a draft defines, such as
dependenciesor draft 4'sid, is held tonone, and so is a
definitionsentry no$refreaches: openapi: a schema read in a JSON Schema draft is held to its minor's keyword rows #1466. A tuple's entries and rest arejudged since item 24, as 2020-12's are.
prefixItems, is read as 2020-12 reads it in a draft's schema:commented on openapi: a 3.0 document's prefixItems lowers to a tuple with no diagnostic #820, the same mechanism as its 3.0 case.
$recursiveRefis kept verbatim and never expanded as its$dynamicRefis: openapi: a 2019-09 schema's $recursiveRef is kept verbatim, never expanded #1475.idcheck reads no grammar, so an example'sidnaming a location on disk refuses it too (
docs/compilers/openapi.md#references).falsethough it writes a mapping is decoded again no more thantyped.MaxMendsdeep, one below another, since each decoding reads all below it: one past themis the top type, reported (
docs/compilers/openapi.md#whole-schema-fallbacks).$schemanaming a draft by another URI than its meta-schema declares, such ashttps://json-schema.org/draft-07/schemaorhttp://json-schema.org/draft/2019-09/schema#(1 and6 of the 560 files), names no dialect the table has rows for, as fix(compilers/openapi): read a schema in the dialect it names #1459 has it: it reads as the
minor's own, with
openapi/unread-dialect.Closes #1457
Closes #1299
Test plan
FormatFinding.DialectAt, and a$schemanaming no dialect.idrefuses itsdocument.
SchemaRulesAt.ListedTuple, the census,$refsiblings, a union branch, and anallOf fold under
id.TestOlderDrafts_*, andTestOwnedShapes_AreReportedWhereverTheCheckReadsASchemaoverthe gap's sites and a reached document.
schema-draftsconformance case with its canary rows classified.later commits moved their sites. 49 turn a named test red, and so does a fiftieth once the case
pinning an on-host
idnames draft 4, which item 10 had left judging nothing; 2 sites no longerexist, rewritten by items 4 and 5. 16 more for items 8, 10, 13, 14 and 16 each turn one red,
one only after a case was added for it. 9 more for items 19 and 20: 8 turn one red, one only
after a case was added for it, and dropping draft 4's
idformat is equivalent, as no checkreads that row (openapi: a schema read in a JSON Schema draft is held to its minor's keyword rows #1466).
main(4ac3031d7), comparing exit code, IR and diagnostics per spec:testdatabar goldens, 3 differ: the new case, and thereworded rebased-branch and
$dynamicRefmessages.items-list code move. 3 are bound errors now placed at the keyword, one of which also gainstwo reports, on a 3.0 array's bounds the lowering never read.
$schema, each reached from a 3.1 document's$ref,401 differ. 11 that exited 1 now exit 0, on readings the drafts give that
mainrefused: 9tuples, 2 fragment
$ids and 2 plain-name anchors. 7 that exited 0 now exit 1, those under"Visible change on input that names a draft".
039e5a9a7, 560 public JSON Schema files, 380 of them draft 7, 29 draft 4 and 13 2019-09, each reachedfrom a 3.1 document's
$ref, againstmain: 399 differ. Each difference is one of these: anunread-dialectwarning gone (386 files) and unknown-keyword infos gone (175), a tuple accepted(14 files lose a type-mismatch error) and a draft 7
$idnaming a fragment (2 lose a formaterror),
$refsiblings warned of (item 13), anitemslist refused in a file read as 2020-12(4), and the bound
$refs above.cf29b1a62), over 2,077 OpenAPI files, 249 undertestdataand1,828 real-world specs, and the 560 JSON Schema files: none differs in exit code, IR or
diagnostics, while the two binaries differ on the probes for items 19 and 20.
of the 560 files beside a 3.1 wrapper: 468 pass every oracle and 92 stop at an error diagnostic.
Against
main, 9 that stopped now pass, and 7 that passed now stop, each at adefinitions$refthat a sibling file the copy lacks, or anexample.com$id, leaves unresolved. Six large3.1 specs, their
jsonSchemaDialectset to each draft in turn, pass them too, bar one stopping atthe same Parameter Object error in every dialect. Against the same specs read in 2020-12 they
differ only by the
$ref-sibling warnings drafts 4 to 7 draw, besidedescription,minLengthand
maxLength.FuzzLowerSchemafor four minutes from seeds of the drafts' readings: 2.5 million executions,no failure.
main: a tuple of 100,000 entries costs no more thanone written with
prefixItems, linear in its entries; tuples nested past the depth bound reportonce; an alias bomb through tuple lists is refused as before. A document of 20,000 draft 4
components costs 1.0% to 1.1% more instructions retired, medians of three runs at
GOGC=off,whether each names the draft or its
jsonSchemaDialectdoes: the readings the draft adds, ofwhich handing its bounds to the load phase is 0.2%.
openapi.json@9c77db52):morphic validateoutput identical, 1,969lines, and the compiled IR byte-identical. Cost at
GOMAXPROCS=1 GOGC=off, over four paired runsagainst
main: 0.12% to 0.22% more instructions retired, against a base of 87.95G to 88.15G,and 0.7% less peak memory, which with the collector off is what the compile allocates.
morphic-harness testdata: the same outcome for every spec asmain, bar the new case, whichmainstops at the error itsitemslist draws and the head passes through every oracle.mainand the head: a remote document served over HTTP with anexample's
id(read on both) and a draft 4 one whoseidnames the disk (refused on the head);a draft 4
$schema: 'rel/x'besideid; draft 7 duplicate fragment identifiers and draft 4duplicate
ids; a$dynamicRefunder a draft 4id; tuples reached through a component$ref,a path item
$refand a document of schemas; a 2019-09$id: '#foo'(refused on both); a draft7 tuple as a multipart part beside its
prefixItemstwin; and a draft tuple's entries and restwritten as
5,'x',[1]andnull, unreached and reached by a$ref.for them:
writesRefreading a$refwritten as a mapping as one, andisTupleRestcomparingno documents.
953c11b5a) over 2,658 inputs, the 231 YAML and JSONfiles under
testdatabar goldens, the 1,840 real-world specs and 587 JSON files writing$schema, each beside a 3.1 wrapper: 23 differ, no exit code among them. 20 only reword the$ref-sibling warning, 2 are a real-world draft 7schema writing
$idbeside its$ref, whose types are unchanged and whose identifier is nolonger warned of, and 1 is the conformance case, which now writes an
idbeside a$ref.morphic validateoutput and the compiled IR byte-identical to the previoushead's, at 88.17G to 88.29G instructions retired against its 88.18G to 88.24G.
$idcasein drafts 4, 6 and 7.
oas,bind,annotation,schemaandloadby their own tests: 100%.main(4ac3031d7):or rewritten for them: decoding a written
falseagain, pinned by a count of decodes, and therun-wide dialect test and a fast path for an unread document, pinned by a draft spelling its
identifier
$id.A second
mendbefore the landings were built turned none red, as the one after them mendswhat was recorded before, so it was removed.
public
friends.json, whose items model now has its three properties; item 26 changes theorder of 2 infos in the new case; item 28 changes none.
written to its own file with its draft's
$schemaand reached from a 3.1 document: all 1,014compile with no error but
refRemote.json's refused network fetches, and 969 pass everyharness oracle, the other 45 stopping at those fetches. Its "$ref prevents a sibling $id from
changing the base uri" lowers its
$refto the number drafts 4, 6 and 7 name.errors its
prefixItemstwin draws at each.GOMAXPROCS=1 GOGC=off: a chain ofitems1,000 deep ending in alist costs 6.8G instructions retired against the previous head's 1.4G, and would cost 40G at
500 deep with no bound.
morphic validateoutput and the compiled IR byte-identical after each of items25, 26 and 28, its cost within the noise of the head before.
FuzzLowerSchemafor three minutes on the final head: 1.44 million executions, no failure.main(ab953d453):governing a document's root before the roots it holds, falling back to the context's rows,
governing a document whose rows are the source's and noting scalars, those two changing only
what is stored, and walking past lists, which the list-alias case counts.
5c36e79a8) over the same 2,658 inputs: none differ.The new end-to-end cases, one per direction, each lower as the previous head did not.
GOMAXPROCS=1 GOGC=off, three runs alternating with the previous head's: a 3.1 sourcereaching 20 schemas of a 3.0 document of 20,000 retires a median 3.86G instructions (3.85G to
3.89G) against 3.79G (3.79G to 3.80G), 1.8% more, and peaks at 224 MB against 219 MB, at a
byte-identical IR. Noting scalars too, as the walk did, cost 6% and 9%, so it notes none.
diagnostics too, among them 9
openapi/unresolved-security-schemeerrorsmaindraws alike,since that revision names schemes it does not define.
main(2123d296e):for them: holding draft 6's or draft 7's
$idrow to a plain URI, which leaves validation andthe binder agreeing on a reading the draft refuses, indexing the bindings before decoding
again, and indexing them only for a source naming a dialect, not another document. A check that the
bindings place a schema in a document the compile lists survives: no input reaches it, and it
keeps the fallback
schema.ListedTuplekeeps. A check of a fragment's UTF-8 before looking aname up was equivalent, as no dialect declares a name no UTF-8 spells, and was removed.
e9cfa2de5) over 2,664 inputs, the 237 YAML and JSONfiles under
testdatabar goldens, the 1,840 real-world specs and the 587 JSON files writing$schemabeside a 3.1 wrapper: 12 differ, each anopenapi/invalid-items-listerror gainingitem 34's suggestion, no exit code and no other diagnostic among them.
items,additionalItems,ref,refRemote,definitions,dependenciesandanchorfor drafts 4, 6, 7 and 2019-09: 20 differ, each anadditionalItemsbeside a list in an untyped schema, nowdegraded_lowering; none draws anerror but
refRemote's refused fetches.orders of the roots' names, draft 4 fragment identifiers, and a stray rest in an untyped schema,
beside a
$ref, in a property and in a union.morphic validateoutput byte-identical to theprevious head's. Cost at
GOMAXPROCS=1 GOGC=off, three runs alternating: 94.12G to 94.29Ginstructions retired against 94.18G to 94.32G, and 6,028 MB peak against 6,027 MB. A document of
20,000 components each naming draft 4, where the index is kept, retires 68.09G to 68.13G
against 67.92G to 68.02G (0.25%) and peaks at 4,533 MB against 4,490 MB (1.0%), at a
byte-identical IR.
bind,bind/typed,load,oasandschemaby their own tests: all 7,201statements.
main(2123d296e):these items add: reading a variant's tuple at its own pointer, placing no list of items where
no dialect is named, the placement lookup ignoring the document it finds, or skipped by either
of its two readers, 3.1's advice given in a 3.0 document, the merge-bound reason dropped, and
the decoding bound's advice dropped.
cb7798bdc) over the same 2,664 inputs: 7 differ, eacha 3.0 document's
openapi/invalid-items-listerror taking item 37's advice. The JSON SchemaTest Suite's 265 groups: none differ, none draws an error but
refRemote's refused fetches.$schemaand byjsonSchemaDialect, with a tuple among its entries.morphic validateoutput byte-identical. Cost atGOMAXPROCS=1 GOGC=off, three runs alternating with the previous head's: 94.13G to 94.18Ginstructions retired against 94.02G to 94.25G, and 6,030 MB peak against 6,030 MB.
bind,bind/typed,load,oasandschemaby their own tests: all 7,216statements.
these items add (letting a List hold a stray rest, the format's old wording, placing no list of
items where no dialect is named) and the fourth, giving 3.1's advice everywhere, through item
37's 3.0 case. Differential against the previous head (
8cecaadd2) over the same 2,664 inputs:none differ. Coverage of
bind,bind/typed,load,oasandschemaby their own tests: all7,216 statements.
the wording before it.
item 45 fixes and its neighbours: a second comment marker on a line, a word written twice, an
unpaired backtick, two spaces inside a sentence, a cited identifier the tree no longer declares,
a new declaration whose doc comment does not open with its name, a comment ending mid-sentence,
and a line past 100 characters. Only the lines item 48 rewraps remained, bar two of 101, as
docs/compilers/openapi.mdonmainkeeps 64 lines past 100. Theharness over
testdatagives every specmainhas the outcomemaingives, and the new casepasses every oracle.
internal/archtest,golangci-linton the touched packages andsubjects.py. CI ran the gate oncb7798bdc,8cecaadd2,105e7142dand8d05168d1, both jobs green on each, and runs it on thehead.