Commit Graph

20 Commits

Author SHA1 Message Date
83e6e27d12 style(format): adopt --importgrouping testable-bottom from canonical rules-swift
Group @testable imports at the bottom, per public/rules-swift's swiftformat. A
behavior-preserving import-ordering reformat across 252 files (mostly tests).
--trailingclosures was deliberately not adopted (cosmetic, and swiftformat flags
it as caution-worthy).
2026-06-18 20:33:07 +02:00
0994f5dd2a fix(schema): include and redefine require a schemaLocation; invalid-schemas-accepted 71 to 69 (#145)
xs:include and xs:redefine require a schemaLocation attribute (only xs:import's
is optional, being a resolution hint). An include or redefine declared without
it is now rejected in the structure walk.

A required-attribute check with no name resolution or reference walking; it
cannot over-reject (schemaLocation is mandatory on these per the
schema-for-schemas), so the full critic loop is not needed here -- reserved for
the false-positive-prone resolution rules. XSTS: invalid-schemas-accepted 71 to
69 (schB3 and a sibling), valid-schemas-rejected held at 0, no other bucket moved.
2026-06-18 01:33:00 +02:00
c45e1eb856 fix(schema): a local element must have a name or a ref; invalid-schemas-accepted 80 to 79 (#145)
A local element declaration (one not a child of schema) must identify itself
with a name or a ref; an xs:element with neither is invalid. localElementErrors
now flags it, alongside the existing local-element forbidden-attribute checks.

XSTS: invalid-schemas-accepted 80 to 79 (elemP005), valid-schemas-rejected held
at 0, no other bucket moved.
2026-06-18 00:34:50 +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
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
926ff99b5a fix(schema): validate xml:lang and xml:space on documentation elements 2026-06-15 22:36:51 +02:00
be5d260b52 feat(schema): top-level attribute may not have use/form/ref (topLevelAttribute)
A top-level xs:attribute declaration (a direct child of schema, or of redefine)
was accepted with use, form, or ref. Those belong to a local attribute use, not
a global declaration: the schema-for-schemas topLevelAttribute marks ref, form,
and use as prohibited, allowing only default, fixed, id, name, type and an inline
simpleType. The per-component applicability table is the permissive global/local
union, so it could not draw the distinction; a focused scan of the direct
attribute children of schema and redefine supplies the global-only constraint. A
local attribute use inside a complex type or attribute group is unaffected.

The existing test_validAttributeValues asserted a top-level attribute with use
compiles, which is itself invalid; corrected to a local attribute use.

XSTS: invalid schemas accepted 454 to 439, no other bucket raised.
2026-06-15 14:18:25 +02:00
dd10f32abb feat(schema): a simpleType must have exactly one of restriction/list/union
Per XSD 1.0 Datatypes a simpleType's content is
(annotation?, (restriction | list | union)). The membership table admitted each
name without the cardinality, so an empty simpleType or one combining two
derivations (two unions, a union and a list, two restrictions) was accepted.
The exactly-one rule is now enforced, mirroring the named-group compositor check.

XSTS: invalid schemas accepted 537 to 527, valid schemas rejected held at 72,
instance buckets unchanged.
2026-06-14 14:30:25 +02:00
9d4ce304c2 feat(schema): enforce complexType shorthand content order
A complexType's shorthand content (no simpleContent/complexContent) has the
model (annotation?, (group|all|choice|sequence)?, (attribute|attributeGroup)*,
anyAttribute?). The existing content-shape check caught the wrong number of
model groups and content-spec exclusivity, but not the order, so a model group
after an attribute, an attribute after anyAttribute, or a second anyAttribute
(the ctB family) was accepted.

The complexType's direct children, excluding annotation and the content specs,
are matched against the same ordered slot model used for a complexContent
derivation.

XSTS: invalid schemas accepted 687 to 664, valid schemas rejected held at 72,
instance buckets unchanged.
2026-06-14 11:15:24 +02:00
8a7445c5bb feat(schema): enforce complexContent derivation content order
A restriction or extension inside complexContent has the content model
(annotation?, (group|all|choice|sequence)?, (attribute|attributeGroup)*,
anyAttribute?). The allowedChildren membership table admitted the right child
names but not their order or cardinality, so a second model group, a model
group after an attribute, an attribute after anyAttribute, or a second
anyAttribute was accepted.

A forward-only walk matches the children against an ordered slot model (one
model group, unbounded attributes, one anyAttribute). The check runs only in
the complexContent context, where a model group belongs to the model, so a
simpleContent derivation (facets, no model group) keeps its own rules and no
valid schema is rejected.

XSTS: invalid schemas accepted 864 to 785, valid schemas rejected held at 72,
instance buckets unchanged.
2026-06-14 09:56:04 +02:00
90ae16cba1 feat(schema): enforce Unique Particle Attribution (cos-nonambig)
Reject ambiguous content models at compile time via a Glushkov position
automaton over a literal, QName-resolved view of the raw schema tree, built
just for this rule and independent of the instance-validation content model.

The literal view resolves a ref's QName through the schema's prefix bindings
(not the blanket target namespace) and does not expand substitution groups,
removing the two false-overlap sources a check over the compiled model
produced. Determinism is per particle: a particle competing with a repetition
of itself is never a violation, so a fixed count {n,n} unrolls into copies in
sequence while a true range or unbounded repeat uses a self-loop, and only two
distinct source particles with overlapping labels conflict. The overlap test
is QName-only, an under-rejection that can never over-reject.

A position cap bounds pathological nesting (skipping the check, never
over-rejecting). The element-declarations-consistent test loses its
incidentally-ambiguous (a?, a) case in favor of a deterministic (a, b, a).

XSTS: invalid schemas accepted 884 to 864, valid schemas rejected held at 72,
instance buckets unchanged.
2026-06-14 09:35:33 +02:00
243a6dbc9b feat(schema): enforce Element Declarations Consistent (cos-element-consistent)
Within one complex type's content model, two element declarations with the same
name must have the same type definition. Two same-name elements with different
types were accepted. elementDeclsConsistentErrors collects element particles
across the content model -- descending through model groups and the content-
derivation wrappers, but never into an element's own nested type, which is a
separate content model -- and flags a name seen with more than one distinct type
key (a type reference by local name, a distinct token per inline type, or the
untyped ur-type).

Modeled clean-room on Xerces (libxml2 enforces this only partially). Substitution-
group expansion is not folded in, an accepted under-rejection.

XSTS: invalid schemas accepted 925 to 909 (-16); valid schemas rejected held at
72; instance buckets unchanged.
2026-06-14 01:47:43 +02:00
39222ca760 feat(schema): enforce schema-vocabulary attribute applicability
The attribute sibling of the content-model table. Each XSD component admits a
fixed set of attributes; an unprefixed attribute outside that set (a typo like
nullable, a stray foo, name on a sequence) was accepted because nothing checked
attribute applicability. allowedAttributes now lists each component's admitted
unprefixed attributes (the permissive union of global and local use), and
attributeApplicabilityErrors rejects the rest, plus ref co-occurring with name
or type.

Foreign (prefixed) attributes and namespace declarations are the schema author's
own and are not checked, so no valid schema is rejected.

XSTS: invalid schemas accepted 956 to 925 (-31); valid schemas rejected held at
72; instance buckets unchanged.
2026-06-14 01:18:03 +02:00
b37e97b5a9 feat(schema): a named group must contain exactly one model group
XSD 1.0 gives a named model-group definition's content as
(annotation?, (all | choice | sequence)) -- exactly one compositor. The
allowedChildren set-membership check allowed any number, so an xs:group with two
compositors, or with none, was accepted. namedGroupErrors now enforces the
cardinality, gated on the node being a definition (has a name) so a group
reference is never flagged.

XSTS: invalid schemas accepted 977 to 956 (-21); valid schemas rejected held at
72; instance buckets unchanged.
2026-06-14 01:04:25 +02:00
09a0f9bf5c feat(schema): reject a particle whose minOccurs exceeds its maxOccurs
An occurrence range like minOccurs="5" maxOccurs="2" is empty and illegal in
XSD, but the validator accepted it (only the individual values were checked).
occurrenceOrderErrors now compares the two on a particle that carries both: the
comparison is by canonical magnitude (sign and leading zeros stripped, length
then lexicographic), so it holds beyond a machine integer; maxOccurs="unbounded"
is never exceeded and an equal single-point range is allowed.

XSTS: invalid schemas accepted 997 to 977 (-20); valid schemas rejected held at
72; instance buckets unchanged.
2026-06-14 00:47:51 +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
ca8fd27877 feat(schema): check name and reference attribute lexical validity
Extends the compile-time attribute-model check. A component's name must be a
valid NCName, the reference attributes type/base/ref/itemType/refer/
substitutionGroup must be lexical QNames, and memberTypes a list of QNames.
These were unchecked, so name="", name="123", name="a:b", or type=":_" left the
schema wrongly accepted.

Only the lexical form is checked (a QName's prefix is resolved elsewhere), so a
valid reference is never rejected; each of these attribute names has one type
throughout XSD, so checking by local name is safe.

XSTS: invalid schemas accepted 1190 to 1129 (-61); valid schemas rejected held
at 72 (no over-rejection); instance buckets unchanged.
2026-06-13 23:16:47 +02:00
8011f3d2e8 feat(schema): validate schema-vocabulary attribute values at compile time
The attribute-model sibling of the structural content-model check, folded into
the same schema-document walk. Enumerated attributes (form,
elementFormDefault, attributeFormDefault, use, processContents, mixed, abstract,
nillable) and the occurrence attributes (minOccurs a nonNegativeInteger,
maxOccurs a nonNegativeInteger or unbounded) are checked against their fixed
value space. An out-of-space value (use="foo", mixed="yes", minOccurs="-1") was
previously ignored and the schema accepted.

Each of these names has one meaning throughout XSD, so checking by local name is
safe. Only unprefixed attributes on XSD-namespace elements are checked, so a
foreign attribute is never flagged; values are trimmed before comparison.

XSTS: invalid schemas accepted 1313 to 1190 (-123); valid schemas rejected held
at 72 (no over-rejection); instance buckets unchanged.
2026-06-13 23:01:42 +02:00
6147666e49 feat(schema): structural validity against the schema-for-schemas content model
The general mechanism for the structural tail of the schema-validity gap, in
place of hand-coding a rule per component. A data-driven table encodes the XSD
1.0 schema-for-schemas child content model; at compile time it rejects any
component child the model does not admit, more than one annotation (or an
annotation that is not first, except on schema/redefine, which allow several),
and an identity constraint missing its selector or field.

The walk visits only XSD-namespace elements and skips appinfo/documentation
foreign content, so foreign children are never flagged; restriction/extension
use the permissive union of their simple/complex/simpleContent contexts, so a
child valid in some context is never rejected. One table covers every component.

XSTS: invalid schemas accepted 1688 to 1313 (-375); valid schemas rejected held
at 72 (no over-rejection); instance buckets unchanged.
2026-06-13 22:48:20 +02:00