diff --git a/CHANGELOG.md b/CHANGELOG.md index 7600e4f..cb8517e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ Second pre-release. Continues the W3C XSTS schema-validity campaign: **invalid-s ### Fixed +- XSTS invalid-schemas-accepted (#145): a `simpleContent` restriction's base must be a complex type. Restricting a built-in (`xs:integer`, `xs:anySimpleType`) or a user `simpleType` through `` is rejected (`src-ct.2`); a complex type with simple content is built by extending a simple type, not by restricting one. Caught five invalid schemas: invalid-schemas-accepted 142 → 137, no other bucket moved. + - XSTS invalid-schemas-accepted (#145): Particle Valid (Restriction) now enforces the NameAndTypeOK nillable clause. A restricting element may not be `nillable` unless the base element it restricts is also nillable. Caught two invalid schemas: invalid-schemas-accepted 144 → 142, no other bucket moved. - XSTS invalid-schemas-accepted (#145): Particle Valid (Restriction) now enforces the NameAndTypeOK fixed-value clause. When the base element has a `fixed` value constraint, the restricting element must also be `fixed` to the same value (compared in the base type's value space, so a value-equal pair in different lexical forms is not wrongly flagged). Caught four invalid schemas: invalid-schemas-accepted 148 → 144, no other bucket moved. diff --git a/Sources/Schema/SchemaCompileValidation.swift b/Sources/Schema/SchemaCompileValidation.swift index 209ca7e..9055a0c 100644 --- a/Sources/Schema/SchemaCompileValidation.swift +++ b/Sources/Schema/SchemaCompileValidation.swift @@ -360,11 +360,12 @@ extension PureXML.Validation.SchemaCompile { /// cos-ct-extends.1.4.2.2: a complexContent extension adding element content /// must have a base with complex (or empty) content, not simpleContent. static var complexExtensionBaseValid: PureXML.Validation.Validation { - compileRule("A complexContent extension is consistent with its base type") { document in + compileRule("A content-model derivation is consistent with its base type") { document in guard let namedTypes = document.namedTypes else { return [] } return PureXML.Schema.SchemaLocatedFinding.unlocated( PureXML.Schema.XSDParser.simpleContentExtensionBaseErrors(document.schema, document.context, namedTypes) - + PureXML.Schema.XSDParser.extensionMixedAgreementErrors(document.schema, document.context, namedTypes), + + PureXML.Schema.XSDParser.extensionMixedAgreementErrors(document.schema, document.context, namedTypes) + + PureXML.Schema.XSDParser.simpleContentRestrictionBaseErrors(document.schema, document.context, namedTypes), ) } } diff --git a/Sources/Schema/SchemaExtensionAllGroup.swift b/Sources/Schema/SchemaExtensionAllGroup.swift index 754558e..2f9a312 100644 --- a/Sources/Schema/SchemaExtensionAllGroup.swift +++ b/Sources/Schema/SchemaExtensionAllGroup.swift @@ -113,6 +113,31 @@ extension PureXML.Schema.XSDParser { return errors } + /// XSD 1.0 `src-ct.2` / Derivation Valid (Restriction): the base of a + /// `simpleContent` RESTRICTION must be a complex type (whose content is simple). + /// A built-in (`xs:integer`, `xs:anySimpleType`) or a user `simpleType` base is + /// invalid: a complex type with simple content is built by EXTENDING a simple + /// type, never by restricting one through `simpleContent`. Self-contained schema + /// only; a foreign or unresolved base is left alone. + static func simpleContentRestrictionBaseErrors(_ schema: XSDTree, _ context: PureXML.Schema.XSDContext, _ types: [String: PureXML.Schema.ElementType]) -> [String] { + guard !skipsCrossDocumentRules(schema, compositionLoaded: context.compositionLoaded) else { return [] } + let bindings = context.namespaceBindings + var errors: [String] = [] + for content in descendants(schema, named: "simpleContent") { + guard let restriction = ExtAllNode.firstChild(content, named: "restriction"), + let base = ExtAllNode.attribute(restriction, "base") + else { continue } + let namespace = ExtAllNode.referenceNamespace(base, bindings) + let baseName = ExtAllNode.stripPrefix(base) + if namespace == xsdNamespace { + errors.append("a simpleContent restriction's base must be a complex type, not the built-in simple type '\(baseName)'") + } else if namespace == context.targetNamespace, case .simple? = types[baseName] { + errors.append("a simpleContent restriction's base must be a complex type, not the simple type '\(baseName)'") + } + } + return errors + } + /// XSD 1.0 `cos-ct-extends.1.4.3.2.2.1`: a `complexContent` extension's mixedness /// must match its base type's. The effective content joins the base content and /// the extension's, so a mixed extension may extend only a mixed base and an diff --git a/Tests/SchemaContentOrderTests.swift b/Tests/SchemaContentOrderTests.swift index 6b3d708..c688466 100644 --- a/Tests/SchemaContentOrderTests.swift +++ b/Tests/SchemaContentOrderTests.swift @@ -44,6 +44,11 @@ struct SchemaContentOrderTests { @Test("well-formed simpleContent derivations compile") func test_valid() { #expect(!rejects(simpleContent(#""#))) - #expect(!rejects(simpleContent(#""#))) + // A simpleContent restriction's base must be a complex type (src-ct.2), so it + // restricts a complexType with simple content, not xs:string directly. + #expect(!rejects(#""" + + + """#)) } } diff --git a/Tests/ValidationConfigurationTests.swift b/Tests/ValidationConfigurationTests.swift index af09471..676c02a 100644 --- a/Tests/ValidationConfigurationTests.swift +++ b/Tests/ValidationConfigurationTests.swift @@ -101,7 +101,7 @@ struct ValidationConfigurationTests { "Element value constraints are valid against their declared types", "Complex types extending xs:all groups satisfy XSD placement rules", "Anonymous complex-type restrictions accept a subset of their base's", - "A complexContent extension is consistent with its base type", + "A content-model derivation is consistent with its base type", "Attribute restrictions are faithful to their bases", "Simple types do not derive from complex types", "Simple-type varieties are declared consistently", diff --git a/Tests/XSTSSuiteTests.swift b/Tests/XSTSSuiteTests.swift index e132669..0a7c471 100644 --- a/Tests/XSTSSuiteTests.swift +++ b/Tests/XSTSSuiteTests.swift @@ -102,8 +102,11 @@ struct XSTSSuiteTests { /// Enforcing the NameAndTypeOK nillable clause (a restriction may not be nillable /// unless the base element is) then caught two more invalid schemas: /// invalid-schemas accepted 144 → 142, no other bucket moved. + /// Requiring a `simpleContent` restriction's base to be a complex type, not a + /// built-in or user simple type (src-ct.2), then caught five more invalid + /// schemas: invalid-schemas accepted 142 → 137, no other bucket moved. private let knownSchemaValidRejected = 1 - private let knownSchemaInvalidAccepted = 142 + private let knownSchemaInvalidAccepted = 137 private let knownInstanceValidRejected = 3 private let knownInstanceInvalidAccepted = 132