A type inside xs:redefine must restrict or extend the type it redefines, which lives in the redefining schema's own target namespace. checkRedefine compared local names only (derivationInfo strips the prefix), so a base like foo:b-ct -- an imported type sharing the redefined type's local name -- was treated as self and accepted. Now the base's namespace is resolved: a base bound to an explicit namespace other than the redefining schema's target namespace is not self and is rejected (src-redefine.5). redefineBaseIsForeign (SchemaImportNamespace.swift) leaves a base with no resolvable namespace alone, so an unprefixed or own-namespace self-reference never over-rejects -- the false-positive boundary, the same namespace-aware fix applied across the campaign. XSTS: invalid-schemas-accepted 84 to 81 (schQ2/schQ4/schP3), valid-schemas-rejected held at 0, no other bucket moved. Differential: agree-valid unchanged at 3161, PureXML-stricter unchanged at 87 over 9618 schemas. Third step of the cross-document composition campaign (#161).
48 lines
2.8 KiB
Swift
48 lines
2.8 KiB
Swift
private typealias ImportNode = PureXML.Schema.XSDNode
|
|
|
|
extension PureXML.Schema.XSDParser {
|
|
/// XSD 1.0 `src-import.3.1`/`3.2`: an `import`'s `namespace` attribute must equal
|
|
/// the `targetNamespace` of the schema it resolves to (both present and equal, or
|
|
/// both absent). Runs only past the cross-document skip guard, so the imported
|
|
/// schema is loaded. Checked only when the `schemaLocation` resolved to a loaded
|
|
/// container (an unresolved import is the processor's choice and not an error),
|
|
/// which is the false-positive boundary. Imports keep their own target namespace
|
|
/// (no chameleon), so the imported document's literal `targetNamespace` is used.
|
|
static func importNamespaceErrors(_ context: PureXML.Schema.XSDContext, _ containers: [XSDTree]) -> [String] {
|
|
var locationTargetNS: [String: String?] = [:]
|
|
for container in containers where ImportNode.localName(container) == "schema" {
|
|
guard let location = context.containerLocations[ObjectIdentifier(container)] ?? nil else { continue }
|
|
locationTargetNS[location] = ImportNode.attribute(container, "targetNamespace")
|
|
}
|
|
var errors: [String] = []
|
|
for container in containers {
|
|
for node in descendants(container, named: "import") where node.name?.namespaceURI == xsdNamespace {
|
|
guard let location = ImportNode.attribute(node, "schemaLocation"),
|
|
locationTargetNS.keys.contains(location)
|
|
else { continue }
|
|
let imported = locationTargetNS[location] ?? nil
|
|
let declared = ImportNode.attribute(node, "namespace")
|
|
if declared != imported {
|
|
errors.append("import of '\(location)' declares namespace '\(declared ?? "(none)")', not the imported target namespace '\(imported ?? "(none)")'")
|
|
}
|
|
}
|
|
}
|
|
return errors
|
|
}
|
|
|
|
/// src-redefine.5: a type inside `xs:redefine` must restrict or extend the type it
|
|
/// redefines, which lives in the redefining schema's OWN target namespace. So a
|
|
/// base bound to an EXPLICIT different namespace (for example an imported type
|
|
/// with the same local name) is not self and is invalid. A base with no resolvable
|
|
/// namespace is left alone, which keeps this from ever over-rejecting a
|
|
/// same-local-name self-reference.
|
|
static func redefineBaseIsForeign(_ type: XSDTree, _ rawBase: String?) -> Bool {
|
|
guard let rawBase else { return false }
|
|
let schema = ImportNode.schemaOwner(type)
|
|
let targetNamespace = ImportNode.attribute(schema, "targetNamespace")
|
|
let bindings = ImportNode.namespaceBindings(of: schema)
|
|
guard let baseNamespace = ImportNode.referenceNamespace(rawBase, bindings) else { return false }
|
|
return baseNamespace != targetNamespace
|
|
}
|
|
}
|