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.
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.
The W3C "Particle Valid (Restriction)" MapAndSum rule (XSD 1.0 §3.9.6): a derived
sequence restricting a base choice is valid iff each derived particle restricts
some base branch AND the derived sequence's occurrence count times its number of
particles lies within the base choice's occurrence range.
PureXML applied the same-compositor occurrence subsumption (the sequence's own
range against the choice's), which both rejected a valid restriction
(sequence{2,4}(e1,e2) of choice{3,9}(e1,e2): 4..8 elements fit 3..9) and accepted
an invalid one (a two-member sequence restricting a single-occurrence choice,
count-product 2 > 1). The count-product is the spec's measure, matched to the
reference implementation (checkMapAndSum/checkOccurrenceRange).
The (sequence, choice) branch in groupValid is retained: it is reached by the
(element, group) RecurseAsIfGroup path, which wraps an element as a one-particle
sequence against a base choice.
XSTS: valid schemas rejected 2 to 1, invalid schemas accepted 520 to 519, no
other bucket raised. The remaining valid-rejected case (particlesZ001) is a
documented spec ambiguity (RecurseAsIfGroup), recorded in docs/xsts-deviations.md.
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.
Within a schema, global type names (simpleType and complexType share one symbol
space), global element names, global attribute names, named model-group names,
and named attribute-group names must each be unique, and identity-constraint
names must be unique across the document. A duplicate was accepted, with the
later definition silently overwriting the earlier one.
Only the document's own globals are examined (xs:redefine children and unloaded
included documents are not direct globals), so a redefinition is not mistaken
for a clash; the symbol spaces are kept separate, so a type and an element may
share a name.
XSTS: invalid schemas accepted 909 to 891 (-18); valid schemas rejected held at
72; instance buckets unchanged.
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.
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.
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.
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.
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.
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.
Each pattern facet value is now compiled when the schema is, instead of only
lazily at instance time (try?) where an unparseable pattern was silently
ignored. A structurally malformed pattern (unbalanced parentheses, a quantifier
with nothing to repeat) is rejected.
The check is conservative against over-rejection: it rejects only the
unambiguous structural RegexError kinds (.unbalanced, .danglingQuantifier) and
tolerates the kinds that fire on otherwise-valid patterns the engine cannot
fully handle (.empty for the empty pattern, .unsupported for an untabulated
\p{Is...} block such as a surrogate or historic block, .badQuantifier for the
lenient {,m} form, and .badEscape/.badClass which could fire on valid character-
class subtraction). So a{2,1} and a trailing backslash remain accepted (an
under-rejection), but no valid schema is rejected.
XSTS: invalid schemas accepted 1129 to 1092 (-37); valid schemas rejected held
at 72; instance buckets unchanged.
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.
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.
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.
The id attribute on any XSD component is of type xs:ID, so each value must be a
valid NCName and unique within the schema document. The values were never
checked, so a malformed (id="", id="123") or duplicated id left the schema
wrongly accepted across many categories (idA-idK, attribute groups, complex
types).
The schema-document tree is walked once, collecting id attributes. Two guards
keep valid schemas from being rejected: appinfo/documentation subtrees are
skipped (their foreign content carries its own non-xs:ID id attributes), and
only the unprefixed id is treated as the component identifier (a foreign xml:id
or pre:id is a different attribute). Findings join the shared parse-time
diagnostics.
XSTS: invalid schemas accepted 1789 to 1688; no other bucket raised.
Continue the facet-validity work. Per XSD Part 2 Datatypes 4.3, each
minInclusive/maxInclusive/minExclusive/maxExclusive and enumeration value must
be a valid value of the base type; minInclusive excludes minExclusive and
maxInclusive excludes maxExclusive; and the lower bound may not exceed the upper
bound (nor equal it when either side is exclusive, an empty value space),
compared in the base primitive's value space. Bound and enumeration values were
previously unchecked, so a malformed or out-of-space value (maxInclusive="",
enumeration="" on float) was accepted.
The bound-value check (which validates a bound against its base type) exposed a
gMonth lexical gap: only --MM was recognized, not the XSD 1.0 form --MM-- used
by valid schemas across the corpus. parseMonthOnly now accepts an optional
trailing -- (the --MM erratum form still works), with an optional timezone,
which removes 26 false schema rejections the bound check would otherwise have
caused and also fixes instance-side gMonth validation.
XSTS: invalid schemas accepted 2082 to 1789; valid instances rejected 233 to
214 (the gMonth fix); valid schemas rejected held at 72; invalid instances
accepted held at 165.
First cut at the schema-validity gap: the validator ran instance-validity rules
but not schema-validity rules, so it silently accepted invalid schemas. Per XSD
Part 2 Datatypes 4.3, a length-family facet value (length, minLength, maxLength,
totalDigits, fractionDigits) must be a valid nonNegativeInteger (totalDigits a
positiveInteger), length may not co-occur with minLength/maxLength, and the
integer ranges must be ordered. Malformed values (length="", "-1", "a", "1e2")
were silently dropped by numeric facet parsing and the schema accepted.
The definition is now read raw from the restriction and validated at compile
time. Well-formedness is checked independently of machine-integer range (via a
canonical digit string), so a huge but valid value is not misreported as
malformed, and ordering compares by length then lexically. Findings travel
through a shared reference-type diagnostics collector on the value-type parsing
context (surviving its copies) and join the model-level consistency findings
thrown from Schema.Document.init.
Limited to facets declared on the local restriction, so an inherited facet is
never wrongly flagged. The min/maxInclusive value-space rules, facet
applicability per base, and inherited co-occurrence remain for later iterations
(see docs/schema-validity-burndown.md).
XSTS: invalid schemas accepted 2461 to 2082, no change to any other bucket.
Resolve a built-in datatype named by xsi:type (xsd:int, xsd:decimal,
xsd:string), which previously resolved only against schema-declared types and
so reported "unknown" and wrongly rejected the element. The built-in is now
resolved when it validly substitutes for the declared type per Schema-Validity
Assessment: the ur-type (anyType/anySimpleType) admits any type, and a bare
built-in declared type admits any built-in derived from it through the XSD
Part 2 datatype lattice (new BuiltinType.derives(from:)). An invalid built-in
substitution is a located error, not a silent acceptance.
Resolving the override correctly exposed two pre-existing gaps, both closed so
the change carries no invalid-instances-accepted regression:
- A simple-typed element rejects non-schema-instance attributes. A simple type
declares no attribute uses, so an element of simple type (including via an
xsi:type override, and including an xsi:nil element) may carry only xsi:* and
namespace attributes. Checked before the nil short-circuit.
- Identity-constraint fields compare in their value space when the selected
node carries an xsi:type. unique/key/keyref tuples compared lexically, so two
xsi:type="xsd:decimal" fields 3.0 and 3 were treated as distinct and a unique
violation was missed. Each field value now carries the type read from the
node's xsi:type and compares via SimpleType.valueMatches when both share a
built-in base, otherwise lexically.
XSTS: valid instances rejected 248 to 233; invalid instances accepted held at
165. Identity tests corrected to spec-valid schemas (an element carrying an
attribute must be a complex type, not type="xs:string").
Updates the worklist to the current settled-expectation baseline
(72/2461/308/165) and records the twelve fixes landed. The remaining instance
deviations are root-caused individually: the particle clusters need
multi-document schema loading and xsi:schemaLocation honoring under a strict
wildcard (a new capability, the largest single lever); reI is pattern-attribute
whitespace normalization; test/elemT are heterogeneous grab-bags needing
per-case triage; the rest are identity-XPath and wildcard/simple-type edges.
The clean single-root-cause fixes are exhausted; the tail is deliberate work.
Refs #129, #122.
The runner read <expected validity> but ignored <current status>, so it counted
disagreements with W3C-disputed expectations as deviations. The 2006-11-06
archive marks 748 instanceTests (plus schemaTests) status="queried" with the
expected validity contested in W3C Bugzilla (for example reJ11.i: \p{Lu}* over
two uppercase letters, prior expectation valid, current invalid+queried, where
PureXML's accept is defensible). Those are now skipped: neither agreement nor
deviation, since the expectation is not authoritative.
Recalibrated baseline (settled expectations only): valid schemas rejected 74 to
72, invalid schemas accepted 2467 to 2461, valid instances rejected 369 to 338,
invalid instances accepted 210 to 179. About 70 apparent deviations were
disagreements with contested expectations, never bugs.
Refs #129, #122.
XSD 1.0 Datatypes 4.3.1 defines the unit of length only for string, anyURI,
binary, and list; for QName it is implementation-defined. We had counted
characters, rejecting valid QName values. Like Xerces and the XSTS NIST set we
now treat length/minLength/maxLength on QName as always satisfied. Lexical
QName validity is still enforced.
The suite's contributors disagree here (NIST: non-constraining; Microsoft: a
character count), which the spec does not resolve, so a single behavior cannot
satisfy both. Net XSTS: valid instances rejected 547 to 502; 4 Microsoft cases
move to invalid instances accepted (257 to 261), recorded as a deliberate
interpretation in docs/xsts-deviations.md.
Refs #147, #129.
Turns the raw failure list into a worklist driven by understanding each rule,
not by chasing the count. Three categories: (A) genuine correctness bugs in
instance validation worth fixing (union restriction facets ~200, QName length
~36, regex Unicode blocks ~155, duration ordering ~50, particles/int facets);
(B) schema-for-schemas structural validity, the bulk of the 2467 invalid
schemas accepted, unimplemented by design and documented as a deliberate
exclusion; (C) arguable cases needing investigation. Records the evidence
sampled per cluster.
Refs #145, #146, #147, #148.