PureXML/Sources/Schema/XSDCompiled.swift
Mihaela Mihaljevic 8f2725b62a
fix(schema): reject an identity-constraint field that selects a non-simple node (#381)
XSD 1.0 §3.11.4 (cvc-identity-constraint.3) requires each key/keyref/unique
field to identify a node of simple type. A field that reaches an element
declared with no type (the ur-type anyType) or an inline complex type without
simple content does not, so the instance is invalid; the reference (libxml2)
rejects it identically. PureXML accepted such instances because its identity
validator could resolve a field target's type only for a global element reached
by a direct-child name, not for a local element reached through a path (the W3C
XSTS case idZ010 nests anyType fields several levels deep, e.g. a key field
./b/c where c has no type).

A compile-time pass (XSDParser.identityNonSimpleFieldKeys) resolves each field's
target element through its selector and field paths, by element-name steps, and
records the fields whose declared target is provably non-simple. The validator
rejects those at runtime unless the instance gives the node a simple type
through xsi:type, since cvc reads the node's governing type (a text-only anyType
element with xsi:type="xsd:int" is a valid field). The resolution is
conservative: any wildcard, descendant axis, predicate, ref, or named
non-builtin type leaves the field unflagged, so a valid field is never rejected.
The one residual divergence path, two same-name child elements of differing
simplicity in one inline model, is an Element-Declarations-Consistent violation
the compiler already rejects.

Verified over the full W3C XSTS corpus in release: invalid-instances-accepted
8 -> 7 (idZ010 now rejects), valid-instances-rejected held at 0 across all 14383
groups (no false positive), full suite (1837 tests), wasm, and lint all green.
An adversarial critic that read every file and probed for a false positive could
not construct one; the failure mode is under-rejection, the safe direction.
2026-06-29 09:08:25 +02:00

63 lines
3.6 KiB
Swift

extension PureXML.Schema {
/// The result of compiling a schema document: its global element
/// declarations, its named-type table, and the identity constraints declared
/// on each element name.
struct XSDCompiled {
var elements: [String: ElementType]
var types: [String: ElementType]
var constraints: [String: [IdentityConstraint]]
/// Simple types for identity-constraint field paths.
var identityFieldTypes: [String: SimpleType] = [:]
/// `default`/`fixed` value constraints on identity-constraint field
/// targets, so an absent attribute or empty element takes that value as its
/// identity-tuple component.
var identityFieldConstraints: [String: ValueConstraint] = [:]
/// Identity fields whose declared target is not a simple type, so the field
/// violates cvc-identity-constraint.3 (idZ010).
var identityNonSimpleFields: Set<String> = []
/// Local names of elements declared `nillable="true"`.
var nillableElements: Set<String> = []
/// The `default`/`fixed` value constraint declared on each element name.
var elementConstraints: [String: ValueConstraint] = [:]
/// Local names of complex types declared `abstract="true"`. Bare-keyed, for
/// the schema-consistency rules and direct unit tests.
var abstractTypes: Set<String> = []
/// Local names of element declarations declared `abstract="true"`: they may
/// not appear in an instance directly, only through a substitution member.
var abstractElements: Set<String> = []
/// Derivation methods the named type forbids when used through `xsi:type`.
/// Bare-keyed.
var typeBlock: [String: Set<DerivationMethod>] = [:]
/// Derivation methods each element declaration forbids through `xsi:type`.
/// Bare-keyed.
var elementBlock: [String: Set<DerivationMethod>] = [:]
/// Each named complex type's base type and derivation method, the backbone
/// the schema-consistency rules walk. Bare-keyed.
var typeDerivation: [String: TypeDerivation] = [:]
/// Namespaced (`{ns}local`) views of the abstract-type set, `block` tables,
/// and derivation backbone, for the INSTANCE-validity subsystem, where two
/// imported types sharing a local name in different namespaces must not
/// collide.
var nsAbstractTypes: Set<String> = []
var nsTypeBlock: [String: Set<DerivationMethod>] = [:]
var nsElementBlock: [String: Set<DerivationMethod>] = [:]
var nsTypeDerivation: [String: TypeDerivation] = [:]
/// Each named union simple type's member type keys (`{ns}local`), so the
/// `xsi:type`-`block` check can follow a derivation that reaches the
/// declared union through one of its members (cos-st-derived-OK 2.2.4).
var nsUnionMembers: [String: [String]] = [:]
/// Derivation methods each named type declares `final`, for the
/// schema-consistency rules.
var typeFinal: [String: Set<DerivationMethod>] = [:]
/// The schema's target namespace, so the root element's namespace can be
/// checked against it.
var targetNamespace: String?
/// Schema-validity findings gathered while parsing (malformed facet
/// definitions and the like), reported together with the model-level
/// consistency findings when the schema is compiled.
var schemaErrors: [PureXML.Validation.ValidationError] = []
/// Global attribute declarations keyed by ``attributeDeclarationKey(_:)``.
var globalAttributes: [String: AttributeUse] = [:]
}
}