The two monolithic XSD content/identity rules only satisfied the OpenAPIKit validation idiom at the wrong granularity: no per-constraint removability, no isolable failing/near-miss tests. Split them into 32 named rules, each finding tagged at its origin and cached per XSDContext so the walk still runs once per document (the #101 decision against duplicating XSD's recursive type resolution still holds). Wired the streaming path's ID/IDREF and identity checks through the same framework instead of calling the validators imperatively, and added the unwrapAndLookup combinator the rule already named as required. Also includes the mandated repo-wide swiftformat/swiftlint pass.
74 lines
3.8 KiB
Swift
74 lines
3.8 KiB
Swift
private typealias IDVNode = PureXML.Schema.XSDNode
|
|
|
|
extension PureXML.Schema.XSDParser {
|
|
/// XSD 1.0 `a-props-correct.3` / `e-props-correct.5`: an attribute or element
|
|
/// declaration whose type is or derives from `xs:ID` must not carry a `default`
|
|
/// or `fixed` value constraint. Runs after named simple types are resolved, so a
|
|
/// named user type derived from `xs:ID` is recognized, not only the built-in and
|
|
/// inline forms. Only XSD-namespace declarations are checked; foreign content is
|
|
/// skipped.
|
|
/// Located findings (#169): each error is attached to the declaring `element`/
|
|
/// `attribute` node, so an editor can underline it by line/column.
|
|
static func idValueConstraintFindings(_ schema: XSDTree, _ context: PureXML.Schema.XSDContext) -> [PureXML.Schema.SchemaLocatedFinding] {
|
|
var findings: [PureXML.Schema.SchemaLocatedFinding] = []
|
|
collectIDValueConstraintFindings(schema, context, into: &findings)
|
|
return findings
|
|
}
|
|
|
|
private static func collectIDValueConstraintFindings(
|
|
_ node: XSDTree,
|
|
_ context: PureXML.Schema.XSDContext,
|
|
into findings: inout [PureXML.Schema.SchemaLocatedFinding],
|
|
) {
|
|
let local = IDVNode.localName(node)
|
|
if local == "appinfo" || local == "documentation" {
|
|
return
|
|
}
|
|
if let error = idValueConstraintError(node, context) {
|
|
findings.append(PureXML.Schema.SchemaLocatedFinding(reason: error, node: node))
|
|
}
|
|
for child in IDVNode.elementChildren(node) {
|
|
collectIDValueConstraintFindings(child, context, into: &findings)
|
|
}
|
|
}
|
|
|
|
private static func idValueConstraintError(_ node: XSDTree, _ context: PureXML.Schema.XSDContext) -> String? {
|
|
guard node.name?.namespaceURI == xsdNamespace, let kind = IDVNode.localName(node),
|
|
kind == "element" || kind == "attribute",
|
|
let name = IDVNode.attribute(node, "name"),
|
|
IDVNode.attribute(node, "default") != nil || IDVNode.attribute(node, "fixed") != nil,
|
|
isIDDerived(node, context)
|
|
else { return nil }
|
|
return "'\(kind)' '\(name)' must not have a default or fixed value because its type is derived from ID"
|
|
}
|
|
|
|
/// Whether the declaration's type is or derives from `xs:ID`. A `type` attribute
|
|
/// is resolved by reference; an inline `simpleType` is ID only when it restricts
|
|
/// an ID-derived base (a list or union is not derived from ID).
|
|
private static func isIDDerived(_ node: XSDTree, _ context: PureXML.Schema.XSDContext) -> Bool {
|
|
if let typeName = IDVNode.attribute(node, "type") {
|
|
return typeReferenceIsID(typeName, context)
|
|
}
|
|
guard let inline = IDVNode.firstChild(node, named: "simpleType"),
|
|
let restriction = IDVNode.firstChild(inline, named: "restriction"),
|
|
let base = IDVNode.attribute(restriction, "base")
|
|
else {
|
|
return false
|
|
}
|
|
return typeReferenceIsID(base, context)
|
|
}
|
|
|
|
/// Whether a type reference resolves to, or derives from, the built-in `xs:ID`.
|
|
/// The reference's namespace is resolved from its prefix: only an `ID` in the
|
|
/// XSD namespace is the built-in; a user type is ID only if it resolves to one
|
|
/// whose base is `xs:ID`; a foreign or unresolved reference is not ID. (Matching
|
|
/// by local name alone would mistake a user type named `ID`, or an imported
|
|
/// `foo:ID`, for the built-in.)
|
|
private static func typeReferenceIsID(_ typeName: String, _ context: PureXML.Schema.XSDContext) -> Bool {
|
|
if IDVNode.referenceNamespace(typeName, context.namespaceBindings) == xsdNamespace {
|
|
return IDVNode.stripPrefix(typeName) == "ID"
|
|
}
|
|
return context.simpleTypes[IDVNode.stripPrefix(typeName)]?.base == .id
|
|
}
|
|
}
|