fix(schema): restriction may not narrow an element to an extension-derived type
XSD 1.0 cos-particle-restrict NameAndTypeOK.2.2: when a complexContent restriction narrows an element, the element's type must be validly derived from the base element's type EXCLUDING extension (and list/union), i.e. by restriction only. The element-type check followed the derivation chain regardless of method, so a type derived from the base by extension was wrongly accepted as a valid restriction (corpus particlesIj008). Add typeDerivesByRestriction (as typeDerivesOrEqual, but the user-declared chain is followed across restriction steps only; an extension step breaks the walk) and use it on the non-union fall-through of elementTypeRestrictionOK. The shared typeDerivesOrEqual is untouched, so substitution-group validity (where extension is legal) is unaffected. Built-in lattice and union-base derivation are unchanged, so valid restrictions, including the union-member restriction addB150, still compile. XSTS invalid-schemas-accepted 37 -> 36; valid-schemas-rejected held at 0 across all 14383 groups. Union-base (Ik011/Ik025/Ik027) and substitution-group (V020) cases need the by-name union-member and ref-resolution clauses and stay deferred.
This commit is contained in:
parent
f6159191db
commit
ee676cb470
@ -28,6 +28,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A complexContent restriction that narrows an element to a type derived from the
|
||||
base element's type by **extension** is now rejected (XSD 1.0 cos-particle-restrict
|
||||
NameAndTypeOK.2.2, #158): the element-type-derivation check follows the derivation
|
||||
chain across `restriction` steps only, so an extension-derived type is not a valid
|
||||
restriction (corpus particlesIj008). Built-in lattice and union-base derivation are
|
||||
unchanged. XSTS invalid-schemas-accepted 37 -> 36, valid-schemas-rejected held at 0.
|
||||
|
||||
- A `fixed` value on an element whose type is a complex type with `simpleContent` is
|
||||
now compared in the simple type's value space (cvc-elt.5.2.2.1), not as a raw string
|
||||
(#202), so `05` satisfies an `xs:int` `fixed="5"`. The complex-element fixed check on
|
||||
|
||||
@ -54,7 +54,12 @@ extension PureXML.Schema.ParticleRestriction {
|
||||
}
|
||||
|
||||
if baseIsUnion(baseTypeName, types) { return true }
|
||||
return typeDerivesOrEqual(restrictedTypeName, baseTypeName, derivation, types)
|
||||
// NameAndTypeOK.2.2: a restricting element's type must be validly derived from
|
||||
// the base element's type EXCLUDING extension (and list/union), i.e. by
|
||||
// restriction only. A type that reaches the base through an extension step is
|
||||
// not a valid restriction. (Union-base cases are still handled above; the
|
||||
// by-name restriction-derivation of a union member is a separate clause.)
|
||||
return typeDerivesByRestriction(restrictedTypeName, baseTypeName, derivation, types)
|
||||
}
|
||||
|
||||
/// Whether the named type resolves (through the type table) to a `union`
|
||||
@ -111,4 +116,31 @@ extension PureXML.Schema.ParticleRestriction {
|
||||
}
|
||||
return current == base
|
||||
}
|
||||
|
||||
/// As ``typeDerivesOrEqual``, but the user-declared chain is followed ONLY across
|
||||
/// `restriction` steps (an `extension` step breaks the walk), so a type derived by
|
||||
/// extension does not count as a restriction of its base. Built-in derivation is
|
||||
/// always by restriction, so the lattice continuation is unchanged. A `restriction`
|
||||
/// of a union (declared `<restriction base="thatUnion">`) is reached through the
|
||||
/// chain; an unrelated union with subset members is not. The ur-type base stays
|
||||
/// permissive (any type restricts the ur-type), matching ``typeDerivesOrEqual``.
|
||||
static func typeDerivesByRestriction(
|
||||
_ derived: String,
|
||||
_ base: String,
|
||||
_ derivation: [String: PureXML.Schema.TypeDerivation],
|
||||
_ types: [String: PureXML.Schema.ElementType],
|
||||
) -> Bool {
|
||||
if isUrTypeName(base, types) { return true }
|
||||
var current = derived
|
||||
var visited: Set<String> = []
|
||||
while visited.insert(current).inserted {
|
||||
if current == base { return true }
|
||||
guard let step = derivation[current], step.method == .restriction else { break }
|
||||
current = step.base
|
||||
}
|
||||
if let derivedBuiltin = PureXML.Schema.BuiltinType(rawValue: current), let baseBuiltin = PureXML.Schema.BuiltinType(rawValue: base) {
|
||||
return derivedBuiltin.derives(from: baseBuiltin)
|
||||
}
|
||||
return current == base
|
||||
}
|
||||
}
|
||||
|
||||
44
Tests/SchemaParticleTypeRestrictionTests.swift
Normal file
44
Tests/SchemaParticleTypeRestrictionTests.swift
Normal file
@ -0,0 +1,44 @@
|
||||
import Testing
|
||||
@testable import PureXML
|
||||
|
||||
/// XSD 1.0 cos-particle-restrict NameAndTypeOK.2.2 (#158): when a complexContent
|
||||
/// restriction narrows an element, the element's type must be validly derived from
|
||||
/// the base element's type EXCLUDING extension (by restriction only). A type derived
|
||||
/// from the base type by extension is not a valid restriction (corpus particlesIj008).
|
||||
@Suite("XSD NameAndTypeOK restriction-only type derivation (#158)")
|
||||
struct SchemaParticleTypeRestrictionTests {
|
||||
private func compiles(_ derivedC1Type: String) -> Bool {
|
||||
let schema = """
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:complexType name="foo"><xs:sequence><xs:element name="a" type="xs:string"/></xs:sequence></xs:complexType>
|
||||
<xs:complexType name="barExt">
|
||||
<xs:complexContent><xs:extension base="foo"><xs:sequence><xs:element name="b" type="xs:string"/></xs:sequence></xs:extension></xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="barRes">
|
||||
<xs:complexContent><xs:restriction base="foo"><xs:sequence><xs:element name="a" type="xs:string"/></xs:sequence></xs:restriction></xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="B"><xs:sequence><xs:element name="c1" type="foo"/></xs:sequence></xs:complexType>
|
||||
<xs:complexType name="R">
|
||||
<xs:complexContent><xs:restriction base="B"><xs:sequence><xs:element name="c1" type="\(derivedC1Type)"/></xs:sequence></xs:restriction></xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:element name="doc" type="R"/>
|
||||
</xs:schema>
|
||||
"""
|
||||
return (try? PureXML.Schema.Document(schema)) != nil
|
||||
}
|
||||
|
||||
@Test("restricting an element to a type derived by EXTENSION is rejected (particlesIj008)")
|
||||
func test_extensionDerivedTypeRejected() {
|
||||
#expect(!compiles("barExt"))
|
||||
}
|
||||
|
||||
@Test("restricting an element to a type derived by RESTRICTION is accepted")
|
||||
func test_restrictionDerivedTypeAccepted() {
|
||||
#expect(compiles("barRes"))
|
||||
}
|
||||
|
||||
@Test("restricting an element to the same type is accepted")
|
||||
func test_sameTypeAccepted() {
|
||||
#expect(compiles("foo"))
|
||||
}
|
||||
}
|
||||
@ -146,9 +146,9 @@ struct XSTSSuiteTests {
|
||||
/// model-group occurrence, identity-constraint content, derivation base kind,
|
||||
/// attribute/simple-type rules, composition, simpleContent inline-type checks,
|
||||
/// complexContent mixedness, and substitution-group anySimpleType checks.
|
||||
/// Substitution barriers and element-ref metadata 45 -> 43; src-resolve unresolvable foreign-ref (#185) 43 -> 39; xsi: Not Allowed attr decls (#183) 39 -> 37.
|
||||
/// element-ref metadata 45->43; src-resolve foreign-ref (#185) 43->39; xsi:Not-Allowed attrs (#183) 39->37; NameAndTypeOK restriction-only (#158) 37->36.
|
||||
private let knownSchemaValidRejected = 0
|
||||
private let knownSchemaInvalidAccepted = 37
|
||||
private let knownSchemaInvalidAccepted = 36
|
||||
private let knownInstanceValidRejected = 0
|
||||
/// Instance bucket (133 -> 26), per-step deltas in CHANGELOG.md: xsi:type must derive from the
|
||||
/// declared type (complex/atomic/list/union via the element-ref chain); anyType cannot stand in
|
||||
|
||||
Loading…
Reference in New Issue
Block a user