fix(schema): hold xsd:notation to its (annotation?) content model (#145)

A notation declaration's content model is (annotation?): it may carry only an
optional annotation child. Any other element child, or any non-whitespace
character content, is now rejected.

XSTS invalid-schemas-accepted 214 to 211, no other bucket moved.
This commit is contained in:
Mihaela Mihaljevic 2026-06-17 14:17:27 +02:00
parent d2438a957a
commit 616ec4876a
3 changed files with 24 additions and 1 deletions

View File

@ -22,6 +22,8 @@ Second pre-release. Continues the W3C XSTS schema-validity campaign: **invalid-s
### Fixed
- XSTS invalid-schemas-accepted (#145): an `xsd:notation` declaration is held to its `(annotation?)` content model. A notation may carry only an optional `annotation` child, so any other element child, or any non-whitespace character content, is now rejected (`notatG`). Caught three invalid schemas: invalid-schemas-accepted 214 → 211, no other bucket moved.
- XSTS invalid-schemas-accepted (#145): a `list` simple type must draw its item type from exactly one of an `itemType` attribute or an inline `simpleType` child (neither, or both, is invalid), and a `union` must declare at least one member type through a `memberTypes` attribute or an inline `simpleType` child (an empty `union` declares none), per `src-simple-type.2`/`.3`. Caught one invalid schema: invalid-schemas-accepted 215 → 214, no other bucket moved.
- XSTS invalid-schemas-accepted (#145): a simple type without its own `final` attribute now inherits the schema's `finalDefault` as its `{final}` property (XSD 1.0 Schemas §3.14.2), so `finalDefault="list"` or `"union"` blocks the matching list-item or union-member derivation just as an explicit `final` would. An explicit `final=""` still overrides the default back to no restriction. Caught two invalid schemas: invalid-schemas-accepted 217 → 215, no other bucket moved.

View File

@ -21,6 +21,9 @@ extension PureXML.Schema.XSDParser {
if PureXML.Schema.XSDNode.attribute(child, "public") == nil, PureXML.Schema.XSDNode.attribute(child, "system") == nil {
errors.append("notation '\(name)' must specify at least one of public or system attributes")
}
if notationHasForbiddenContent(child) {
errors.append("notation '\(name)' may contain only an optional annotation, no other element or character content")
}
let qName = PureXML.Model.QualifiedName(localName: name, namespaceURI: containerNamespace)
declaredNotations.insert(qName)
}
@ -35,6 +38,20 @@ extension PureXML.Schema.XSDParser {
return errors
}
/// Whether a `notation` declaration carries content its `(annotation?)` model
/// forbids: any element child other than an `xsd:annotation`, or any
/// non-whitespace character content. Whitespace and a single optional
/// annotation are allowed.
private static func notationHasForbiddenContent(_ node: XSDTree) -> Bool {
let hasForeignElement = PureXML.Schema.XSDNode.elementChildren(node).contains {
$0.name?.namespaceURI != xsdNamespace || PureXML.Schema.XSDNode.localName($0) != "annotation"
}
let hasText = node.children.contains {
($0.kind == .text || $0.kind == .cdata) && $0.value.contains { !$0.isWhitespace }
}
return hasForeignElement || hasText
}
private static func walkSimpleTypes(
_ node: XSDTree,
context: PureXML.Schema.XSDContext,

View File

@ -105,8 +105,12 @@ struct XSTSSuiteTests {
/// `memberTypes` or an inline simpleType child (src-simple-type.2/.3), then
/// caught one more invalid schema: invalid-schemas accepted 215 → 214, no other
/// bucket moved.
/// Enforcing the `(annotation?)` content model of an `xsd:notation` declaration
/// (no element child other than an annotation, no non-whitespace character
/// content) then caught three more invalid schemas: invalid-schemas accepted
/// 214 → 211, no other bucket moved.
private let knownSchemaValidRejected = 1
private let knownSchemaInvalidAccepted = 214
private let knownSchemaInvalidAccepted = 211
private let knownInstanceValidRejected = 3
private let knownInstanceInvalidAccepted = 132