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.
This commit is contained in:
Mihaela Mihaljevic 2026-06-16 01:27:10 +02:00
parent 55daca46be
commit baa90cfcaa
6 changed files with 174 additions and 5 deletions

View File

@ -7,8 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.2.0] - 2026-06-16
Second pre-release. Continues the W3C XSTS schema-validity campaign: **invalid-schemas-accepted 394 → 266** (settled expectations), **valid-instances-rejected 180 → 171**, **invalid-instances-accepted 158 → 155**, **valid-schemas-rejected held at 1** (`particlesZ001`, spec-ambiguous). Not yet 1.0: see `docs/production-readiness.md`.
### Fixed
- Enforce element, attribute, attributeGroup, and group child-content shape (schema-for-schemas cardinality and order): at most one inline type per element/attribute, inline type before identity constraints, at most one compositor per group, at most one `anyAttribute` per attributeGroup with attribute references preceding it (`SchemaStructureHelpers.swift`).
- Enforce facet applicability per base type, fractionDigits constraints on integer types, ordered duration range bounds, and built-in list non-emptiness constraints (XSD 1.0 Datatypes §4.3). Ensure that built-in list types (xs:IDREFS, xs:ENTITIES, xs:NMTOKENS) are validated as non-empty unless restricted with an explicit minLength. Resolve type references namespace-aware to avoid prefix conflation with user types of the same name. Ignore foreign elements matching facet names inside xs:restriction. XSTS: invalid schemas accepted 296 to 266, no other bucket raised.
- Enforce schema element order (imports, includes, and redefines must precede other global declarations) and validate import constraints (namespace attribute must not match the schema's targetNamespace, and if missing, the schema must have a targetNamespace) (XSD 1.0 Structures §3.10.6, §3.15.2). XSTS: invalid schemas accepted 302 to 296, no other bucket raised.

View File

@ -99,8 +99,8 @@ flowchart TB
EncodingMismatch["#137 encoding-declaration mismatches: complete"]:::done
BaseURI["#138 per-entity base URIs: complete"]:::done
Epic122["#122 official spec suites"]:::epic
XSTS["#129 W3C XSD test suite (XSTS): first measurement 2026-06-13 baselined (deviations 75/2467/604/582); occurrence-explosion and reversed-range crashes fixed, suite green in 8s"]:::review
XSLTTS["#130 XSLT 1.0 suite (xalan-test): runner + baselines in, burning down (125)"]:::review
XSTS["#129 W3C XSD test suite (XSTS): runner shipped; settled deviations 1/266/171/155 (#145–#148)"]:::review
XSLTTS["#130 XSLT 1.0 suite (xalan-test): runner + baselines in, burning down (~126)"]:::review
Parity100["Behavioral parity closed: #141 top-level params, #142 public streaming, #143 XPath budget, #144 WASI runtime proof, pl/ru sort collation"]:::done
Epic139["#139 faster than libxml2, measured + streaming (benchmarks in docs/benchmarks.md; quadratics killed; unsafe authorized)"]:::epic
Audit140["#140 XSLT validation-framework audit"]:::todo

View File

@ -133,6 +133,14 @@ extension PureXML.Schema.XSDParser {
errors += schemaChildrenOrderErrors(names)
case "import":
errors += importErrors(node)
case "element":
errors += elementChildrenErrors(node, names: names)
case "attribute":
errors += attributeChildrenErrors(node, names: names)
case "attributeGroup":
errors += attributeGroupChildrenErrors(node, names: names)
case "group":
errors += groupChildrenErrors(node, names: names)
default:
break
}

View File

@ -138,4 +138,63 @@ extension PureXML.Schema.XSDParser {
}
return errors
}
static func elementChildrenErrors(_: XSDTree, names: [String]) -> [String] {
var errors: [String] = []
let simpleTypes = names.count { $0 == "simpleType" }
let complexTypes = names.count { $0 == "complexType" }
if (simpleTypes + complexTypes) > 1 {
errors.append("element may have at most one inline type definition (simpleType or complexType)")
}
var seenIdentityConstraint = false
for name in names {
if name == "annotation" { continue }
if name == "simpleType" || name == "complexType" {
if seenIdentityConstraint {
errors.append("inline type definition must appear before identity constraints in element")
}
} else if ["unique", "key", "keyref"].contains(name) {
seenIdentityConstraint = true
}
}
return errors
}
static func attributeChildrenErrors(_: XSDTree, names: [String]) -> [String] {
var errors: [String] = []
let simpleTypes = names.count { $0 == "simpleType" }
if simpleTypes > 1 {
errors.append("attribute may have at most one inline simpleType")
}
return errors
}
static func attributeGroupChildrenErrors(_: XSDTree, names: [String]) -> [String] {
var errors: [String] = []
let anyAttributes = names.count { $0 == "anyAttribute" }
if anyAttributes > 1 {
errors.append("attributeGroup may have at most one anyAttribute")
}
var seenAnyAttribute = false
for name in names {
if name == "annotation" { continue }
if name == "attribute" || name == "attributeGroup" {
if seenAnyAttribute {
errors.append("attribute and attributeGroup references must appear before anyAttribute in attributeGroup")
}
} else if name == "anyAttribute" {
seenAnyAttribute = true
}
}
return errors
}
static func groupChildrenErrors(_: XSDTree, names: [String]) -> [String] {
var errors: [String] = []
let compositors = names.count { $0 == "all" || $0 == "choice" || $0 == "sequence" }
if compositors > 1 {
errors.append("group may have at most one compositor (all, choice, or sequence)")
}
return errors
}
}

View File

@ -276,3 +276,99 @@ struct SchemaStructureTests {
}
}
}
@Suite("Schema structural order and cardinality validity")
struct SchemaStructureOrderTests {
private func compile(_ body: String) throws {
_ = try PureXML.Schema.Document("""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
\(body)
</xs:schema>
""")
}
private func rejects(_ body: String) -> Bool {
do {
try compile(body)
return false
} catch {
return true
}
}
@Test("element child order and type exclusivity constraints")
func test_elementChildOrderAndExclusivity() throws {
// More than one type definition is rejected
#expect(rejects(#"""
<xs:element name="root">
<xs:simpleType><xs:restriction base="xs:string"/></xs:simpleType>
<xs:complexType><xs:sequence/></xs:complexType>
</xs:element>
"""#))
// Type definition after identity constraints is rejected
#expect(rejects(#"""
<xs:element name="root">
<xs:key name="k"><xs:selector xpath="a"/><xs:field xpath="@id"/></xs:key>
<xs:complexType><xs:sequence/></xs:complexType>
</xs:element>
"""#))
// Correct order compiles successfully
try compile(#"""
<xs:element name="root">
<xs:complexType><xs:sequence/></xs:complexType>
<xs:key name="k"><xs:selector xpath="a"/><xs:field xpath="@id"/></xs:key>
</xs:element>
"""#)
}
@Test("attribute child cardinality constraints")
func test_attributeChildCardinality() {
// More than one simpleType is rejected
#expect(rejects(#"""
<xs:attribute name="a">
<xs:simpleType><xs:restriction base="xs:string"/></xs:simpleType>
<xs:simpleType><xs:restriction base="xs:integer"/></xs:simpleType>
</xs:attribute>
"""#))
}
@Test("attributeGroup child order and cardinality constraints")
func test_attributeGroupChildOrderAndCardinality() throws {
// More than one anyAttribute is rejected
#expect(rejects(#"""
<xs:attributeGroup name="myGroup">
<xs:anyAttribute namespace="##any"/>
<xs:anyAttribute namespace="##other"/>
</xs:attributeGroup>
"""#))
// Attribute reference after anyAttribute is rejected
#expect(rejects(#"""
<xs:attributeGroup name="myGroup">
<xs:anyAttribute namespace="##any"/>
<xs:attribute name="x" type="xs:string"/>
</xs:attributeGroup>
"""#))
// Correct order compiles successfully
try compile(#"""
<xs:attributeGroup name="myGroup">
<xs:attribute name="x" type="xs:string"/>
<xs:anyAttribute namespace="##any"/>
</xs:attributeGroup>
"""#)
}
@Test("group compositor cardinality constraints")
func test_groupCompositorCardinality() {
// More than one compositor is rejected
#expect(rejects(#"""
<xs:group name="myGroup">
<xs:sequence/>
<xs:choice/>
</xs:group>
"""#))
}
}

View File

@ -29,12 +29,12 @@ which is an astral math capital); agreeing or disagreeing with a contested
expectation is not meaningful, so the runner skips them. Excluding the queried
entries removed about 70 apparent deviations that were never bugs.
## Baseline (settled expectations; after twelve fixes)
## Baseline (settled expectations; synced 2026-06-16)
| Kind | First measurement | Now |
|---|---|---|
| valid schema rejected | 75 | 1 |
| invalid schema accepted | 2467 | 296 |
| invalid schema accepted | 2467 | 266 |
| valid instance rejected | 604 | 171 |
| invalid instance accepted | 582 | 155 |
@ -65,7 +65,7 @@ validity (`min`/`maxInclusive`, `min`/`maxExclusive`, `enumeration` values valid
in the base space; inclusive/exclusive exclusions; bound ordering) · `gMonth`
`--MM--` lexical form · schema `id` attribute validity (`xs:ID`: NCName and
unique within the document). The schema-validity work brought invalid schemas
accepted 2461 to 296 (schema-document structural validity: content model + complexType content shape, attribute values, name/reference + pattern-regex lexical validity, and reference resolution, against the schema-for-schemas, plus simpleType variety constraints on list itemType and union memberTypes, plus targetNamespace scope matching on local schema references, plus substitution group exclusions based on element final and schema finalDefault attributes, duplicate schema container validation across separate files with identical content, schema element order, and import constraints validation); see `schema-validity-burndown.md`.
accepted 2461 to 266 (schema-document structural validity: content model + complexType content shape, attribute values, name/reference + pattern-regex lexical validity, and reference resolution, against the schema-for-schemas, plus simpleType variety constraints on list itemType and union memberTypes, plus targetNamespace scope matching on local schema references, plus substitution group exclusions based on element final and schema finalDefault attributes, duplicate schema container validation across separate files with identical content, schema element order, and import constraints validation); see `schema-validity-burndown.md`.
Plus the measurement fix (exclude W3C-disputed `status="queried"` entries).
### Remaining, root-caused (the hard tail)