Commit Graph

16 Commits

Author SHA1 Message Date
ca65a334f7 refactor: split oversized types across companion files for lint budgets 2026-08-31 05:00:44 +02:00
380160cc2b refactor(validation): decompose XSD instance validation into composable rules
The two monolithic XSD content/identity rules only satisfied the OpenAPIKit
validation idiom at the wrong granularity: no per-constraint removability,
no isolable failing/near-miss tests. Split them into 32 named rules, each
finding tagged at its origin and cached per XSDContext so the walk still
runs once per document (the #101 decision against duplicating XSD's
recursive type resolution still holds). Wired the streaming path's ID/IDREF
and identity checks through the same framework instead of calling the
validators imperatively, and added the unwrapAndLookup combinator the rule
already named as required.

Also includes the mandated repo-wide swiftformat/swiftlint pass.
2026-07-12 14:12:17 +02:00
4dd1d330cb feat(schema): locate reference-resolution schema diagnostics
Close the last superficially-located checker (#169): referenceFindings was
dispatched as located but its body wrapped the whole aggregate in
.unlocated, so every reference-resolution error still collapsed onto the
schema root. Convert all six sub-collectors to located findings, each
attaching to the node in scope: the core type/base/itemType/memberTypes/ref/
substitutionGroup walk and the xs:-namespace builtin check locate on the
referencing element; the simpleContent base-kind check on the restriction;
the un-imported-namespace check on the referencing element; the
redefine-component-existence check on the redefine's child component; and
the import-namespace-agreement check on the import element.

Every predicate, aggregation order, and reason string is unchanged; only the
location each finding carries differs, verified conformance-neutral by the
full XSTS suite (14383 groups, pinned counts unchanged). Removes two dead
helpers exposed along the way: the unused referenceErrors [String] wrapper
and the unused locatedFindings helper in SchemaCompileValidation. The outer
aggregation is rewritten from a single + chain to incremental appends to keep
the type-checker within budget. Adds a located-diagnostics test per
node-locatable sub-collector; the loader-dependent redefine/import families
are exercised by the XSTS suite.
2026-06-21 18:09:20 +02:00
356e87581c feat(schema): locate simple-type-pass schema diagnostics
Convert the four simple-type-pass compile checkers to located findings so
each error attaches to its own declaring node instead of the schema root
(#169): a list whose item type is itself a list or a union containing a
list and a list declaring both or neither of itemType/inline simpleType
(simpleTypeVarietyFindings), an empty union, a simpleType restriction whose
base resolves to a complex type (simpleTypeBaseNotComplexFindings), an
itemType/memberType referencing a type final for list/union derivation
(simpleTypeFinalFindings), and an attribute typed by a complex type
(attributeTypeMustBeSimpleFindings).

Each predicate, traversal order, and reason string is unchanged; only the
location each finding carries differs, from the schema root to the
offending list, union, restriction, or attribute node. The four checkers
had exactly one caller each (their compile-rule dispatch), so each is
renamed in place and the .unlocated wrap dropped, leaving no dead wrapper.
Adds a located-diagnostics test per checker in a new suite kept within the
type-body budget.
2026-06-21 15:55:59 +02:00
7256df1ceb fix(schema): reject references into never-imported namespaces
On the cross-document skip path (a schema declares import/include/redefine
but no external document was loaded), the reference check stood down whole,
so a prefixed reference whose namespace is never imported was accepted even
though it can resolve to no component under any loading (corpus schZ011's
d:d, where namespaces a/b/c are imported but d is not).

Add undeclaredNamespaceReferenceErrors, run only on that skip path, to flag
a reference whose namespace is neither the target, the XSD or XML namespace,
nor named by any xs:import. To stay sound when the import closure is unknown,
it stands down entirely the moment any external carries a schemaLocation:
that document may transitively import the referenced namespace (corpus
schZ004 imports only a, yet a's document imports b, so b:b resolves). It
applies only when every external is location-less and the closure is
provably complete.

XSTS invalid-schemas-accepted 43 -> 39 (schZ011_a-d); valid-schemas-rejected
held at 0; no other bucket moved.
2026-06-20 13:50:55 +02:00
c418147d30 fix(schema): an import namespace must match the imported targetNamespace (src-import.3); invalid-schemas-accepted 87 to 84 (#161)
src-import.3.1/3.2: an xs:import's namespace attribute must equal the
targetNamespace of the schema document it resolves to (both present and equal,
or both absent). Importing a no-namespace document under a declared namespace,
or one whose target namespace differs, is now rejected.

importNamespaceErrors (new SchemaImportNamespace.swift) builds a resolved
schemaLocation -> targetNamespace map from the loaded containers and compares
each import node's namespace to it. Checked only when the schemaLocation
resolved to a loaded schema (an unresolved import is the processor's choice,
not an error) -- the false-positive boundary; imports keep their own target
namespace so no chameleon adjustment applies.

XSTS: invalid-schemas-accepted 87 to 84 (schF6/schG10/schG13),
valid-schemas-rejected held at 0, no other bucket moved. Differential:
agree-valid unchanged at 3161, PureXML-stricter unchanged at 87 over 9618
schemas. Second step of the cross-document composition campaign (#161).
2026-06-17 22:47:24 +02:00
97817ab611 fix(schema): reject a redefine that redefines a component twice (#145)
A redefine may redefine a given component at most once (src-redefine.7.2.2);
two redefinitions of the same kind and name in one redefine are invalid.

XSTS invalid-schemas-accepted 182 to 181, no other bucket moved.
2026-06-17 16:17:00 +02:00
4551508dd9 fix(schema): a redefine must name components that exist in the redefined schema (#145)
Once the redefined document is loaded through a schemaLoader, every component a
redefine names (complexType, simpleType, group, attributeGroup) must have a
same-kind component of the same name in the redefined schema (src-redefine.6 /
7.2.1). A redefinition of a component that does not exist there is rejected; a
stand-alone schema (target not loaded) is unaffected.

XSTS invalid-schemas-accepted 183 to 182, no other bucket moved.
2026-06-17 16:10:57 +02:00
489275c6b8 feat(schema): locate compile diagnostics with validation coding paths (#169)
Schema compile findings now carry ValidationError coding paths that resolve to source spans on a ranged tree, enabling IDE underlines for id, structure, include, and facet violations.
2026-06-16 10:26:11 +02:00
229d7df3d0 feat(validation,schema): composable validation framework and cross-document composition
Ship the OpenAPIKit-style validation layer (builtin rules, warnings channel,
Validator tiers, coverage gates) and wire XSD compile checks through it. Advance
#161 with chameleon include validation, multi-container reference resolution,
and include targetNamespace composition rules so merged schema sets compile
correctly when a schemaLoader is provided.
2026-06-16 09:57:43 +02:00
5a2e2f3a40 fix(schema): enforce targetNamespace matching on local references 2026-06-15 20:08:39 +02:00
0496c6948a fix(schema): implement namespace-aware reference resolution checks 2026-06-15 19:44:32 +02:00
ffff13da57 fix(schema): enforce simpleType restriction structure and QName prefix bindings 2026-06-15 19:22:10 +02:00
42a87b132c feat(schema): resolve keyref refer to a declared key or unique
A keyref's refer must name a key or unique constraint. A refer pointing at a
non-existent constraint was accepted. keyrefReferErrors collects all key/unique
names and all keyref refers across the document (so a forward refer, textually
before its key, is handled), then flags any refer with no matching name. Matched
by local name, like the rest of the compiler. Skipped when the document declares
import/include/redefine, whose externals the default compile does not load.

XSTS: invalid schemas accepted 891 to 884 (-7); valid schemas rejected held at
72; instance buckets unchanged.
2026-06-14 08:04:34 +02:00
6fbc92593e feat(schema): enforce the complexType content-model shape
XSD 1.0 allows a complexType's content to be simpleContent, or complexContent,
or a model group followed by attributes, never a mix. The allowedChildren
set-membership check permitted illegal combinations, so a complexType with two
simpleContent, simpleContent beside a sequence, or two model groups was accepted.

complexTypeContentErrors now enforces, over the complexType's direct children:
at most one of simpleContent/complexContent; when one is present, no direct model
group and no direct attribute (those belong inside the extension/restriction);
and at most one model group. Only direct children are examined, so attributes
nested inside a derivation are not flagged.

XSTS: invalid schemas accepted 1051 to 997 (-54); valid schemas rejected held at
72; instance buckets unchanged. Also folds in a behavior-identical lint cleanup
of the reference-walk external-definition check.
2026-06-14 00:32:54 +02:00
cbeea0e3c2 feat(schema): reject unresolvable references at compile time
Every QName a schema names must resolve to a declared component or a built-in:
type/base/itemType/memberTypes to a type, element/attribute/group/attributeGroup
ref to the matching global component, and element substitutionGroup to a global
element. An undeclared reference was previously accepted at compile time and only
surfaced lazily during instance validation, if at all.

References are matched by local name, with the value trimmed (a QName attribute
is whiteSpace=collapse). The whole check is skipped when the document declares an
import/include/redefine, since the default compile does not load externals and
the pools would be incomplete; that guard is gated on the XSD namespace so a
foreign element named "import" does not trip it.

XSTS: invalid schemas accepted 1092 to 1051 (-41); valid schemas rejected held
at 72 (an interim version regressed one valid schema on a whitespace-padded
base="  xs:string ", fixed by trimming); instance buckets unchanged.
2026-06-14 00:17:29 +02:00