Commit Graph

12 Commits

Author SHA1 Message Date
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
3a56d9f400 feat(schema): locate structure-pass schema diagnostics
Convert the seven structure-pass compile checkers to located findings so
each error attaches to its own declaring node instead of the schema root
(#169): a facet inapplicable to a list/union variety, a restriction of
xs:anySimpleType and a constraining facet on anySimpleType-based simple
content, a top-level definition missing its name or carrying a local-only
attribute, a nested named definition, an empty targetNamespace or import
namespace, and an all-group reference with an illegal minOccurs/maxOccurs.

The rewire in structureFindings preserves the prior finding order and
count; each error predicate and reason string is unchanged, only the
location each finding carries differs. Adds a located-diagnostics test
per checker family in a new suite kept within the type-body budget.
2026-06-21 15:23:13 +02:00
ca7a7d1433 fix(schema): a reference to an all group must have maxOccurs 1
XSD 1.0 cos-all-limited: a reference to a model group whose content is an
`all` group is itself an all-group particle, so its maxOccurs must be 1 and
its minOccurs 0 or 1. The direct `<all>` element was already checked by
allGroupLimitedErrors, but a `<group ref>` resolving to an all group was not,
so corpus particlesEa025 (`<group ref="x:G" maxOccurs="2">` to an all group)
was accepted.

Add allGroupReferenceMaxOccursErrors: collect the names of group definitions
whose content is an all group, then flag a group reference to one whose
maxOccurs is not 1 or whose minOccurs is not 0/1. The check runs over a single
schema document tree, where group names are unique, so the by-local-name match
cannot collide; a cross-document reference lives in a separate container and is
under-rejected (never falsely rejected).

XSTS invalid-schemas-accepted 36 -> 35; valid-schemas-rejected held at 0.
2026-06-20 19:14:33 +02:00
75dd044d93 fix(schema): a local complexType must be anonymous; invalid-schemas-accepted 81 to 80 (#145)
A complexType nested in an element is a local (anonymous) type definition and
may not carry a name attribute; only a top-level complexType (a child of schema,
or a redefine redefinition) is named. localComplexTypeNameErrors flags a named
complexType whose parent is an element, wired into the structure-walk validation.

XSTS: invalid-schemas-accepted 81 to 80 (ctA042), valid-schemas-rejected held
at 0, no other bucket moved.
2026-06-18 00:17:35 +02:00
606bc35a45 fix(schema): enforce identity-constraint content model; invalid-schemas-accepted 110 to 104 (#145)
An xs:unique / xs:key / xs:keyref content model is (annotation?, selector,
field+): exactly one selector, then one or more fields, in that order. The
structure walk previously checked only that the children were drawn from
{annotation, selector, field}, not their cardinality or order, so a field
before the selector, a second selector, or a missing field was accepted.

identityConstraintContentErrors enforces it, wired into the per-component
structural dispatch. XSTS: invalid-schemas-accepted 110 to 104 (six caught:
idA044/046, idB044/046, idC047/049), valid-schemas-rejected held at 0, no
other bucket moved.
2026-06-17 21:04:16 +02:00
b391db6a74 fix(schema): enforce model-group occurrence ranges; invalid-schemas-accepted 130 to 110 (#145)
Enforce the two occurrence-range constraints on model groups that PureXML
was not checking:

- p-props-correct.1: a particle's minOccurs may not exceed its maxOccurs,
  both defaulting to 1 when the attribute is absent. occurrenceOrderErrors
  previously fired only when BOTH attributes were present, so it missed
  minOccurs="2" with a defaulted maxOccurs (2 > 1) and maxOccurs="0" with a
  defaulted minOccurs (1 > 0). It now orders against the effective defaults.

- cos-all-limited.2: an all group's maxOccurs must be 1 and its minOccurs
  0 or 1, and every particle it contains is limited to maxOccurs 0 or 1.

Both are zero-false-positive rules (no valid schema violates them). XSTS:
invalid-schemas-accepted 130 to 110 (20 caught), valid-schemas-rejected
held at 0, no other bucket moved. The differential harness corroborates:
agree-invalid 1751 to 1767, PureXML-stricter unchanged at 87 (no new
over-rejection across 9618 schemas).
2026-06-17 20:55:22 +02:00
f61e7d0613 fix(schema): reject an empty-string namespace value (#145)
A namespace name must be a non-empty anyURI, so <schema targetNamespace=""> and
<import namespace=""> are invalid. Omitting the attribute (no target namespace,
or a no-namespace import) stays valid.

XSTS invalid-schemas-accepted 134 to 132, no other bucket moved.
2026-06-17 18:07:11 +02:00
d402732d23 fix(schema): hold an attributeGroup reference to (annotation?) content (#145)
A referencing attributeGroup (one with a ref) names a definition elsewhere; its
own content model is (annotation?), so it may not also declare attributes,
nested attributeGroup references, or an anyAttribute. Only a defining
attributeGroup (with a name) carries those.

XSTS invalid-schemas-accepted 210 to 207, no other bucket moved.
2026-06-17 14:27:21 +02:00
39649a1940 fix(schema): reject named-group compositor occurrence and XSD-namespace attributes (#145)
Two schema-for-schemas structural constraints were not enforced, so the
malformed schemas were accepted:

- The compositor of a top-level named xs:group (all/choice/sequence) may not
  carry minOccurs/maxOccurs; those belong on a group reference, not the
  definition.
- An attribute in the XML Schema namespace (e.g. xsd:type) is never valid on a
  schema element, since the vocabulary defines no namespaced attributes. A
  foreign attribute in any other namespace stays the author's own.

XSTS invalid-schemas-accepted 244 to 235, no other bucket moved. (A third
candidate, form on a top-level declaration, was already enforced by the
existing top-level attribute rule, so no new check was needed.)
2026-06-17 11:08:33 +02:00
baa90cfcaa chore(release): prepare 0.2.0
Cut the second pre-release: schema-validity burn-down to XSTS baselines
1/266/171/155, component child-content shape checks, and synced conformance
docs. CHANGELOG updated for v0.2.0.
2026-06-16 01:27:10 +02:00
5cdcde9055 fix(schema): enforce element order and import constraints 2026-06-15 23:29:47 +02:00
ffff13da57 fix(schema): enforce simpleType restriction structure and QName prefix bindings 2026-06-15 19:22:10 +02:00