feat(schema): locate simple-type-pass schema diagnostics
Convert the four simple-type-pass compile checkers to located findings so each error attaches to its own declaring node instead of the schema root (#169): a list whose item type is itself a list or a union containing a list and a list declaring both or neither of itemType/inline simpleType (simpleTypeVarietyFindings), an empty union, a simpleType restriction whose base resolves to a complex type (simpleTypeBaseNotComplexFindings), an itemType/memberType referencing a type final for list/union derivation (simpleTypeFinalFindings), and an attribute typed by a complex type (attributeTypeMustBeSimpleFindings). Each predicate, traversal order, and reason string is unchanged; only the location each finding carries differs, from the schema root to the offending list, union, restriction, or attribute node. The four checkers had exactly one caller each (their compile-rule dispatch), so each is renamed in place and the .unlocated wrap dropped, leaving no dead wrapper. Adds a located-diagnostics test per checker in a new suite kept within the type-body budget.
This commit is contained in:
parent
3a56d9f400
commit
356e87581c
@ -32,7 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Schema compile diagnostics are now located: `SchemaError.inconsistent` carries `[ValidationError]` with coding paths that resolve to `TreeNode.sourceRange` on a ranged schema tree (#169). Parse-time facet and structure findings attach to their declaring nodes where possible; `SchemaError.inconsistentFindings` exposes the full list. Schematron reuses the shared `validationCodingPath()` helper. Value-constraint errors now locate to their declaring element or attribute: a `default`/`fixed` on an ID-derived type, a value that is invalid for its built-in, named user, or inline type, and an illegal combination of value constraints each underline the offending declaration rather than the schema root. The structure-pass checkers are likewise located: a facet inapplicable to a list or union variety, a derivation by restriction of `xs:anySimpleType` and a constraining facet on `anySimpleType`-based simple content, a top-level definition missing its name or carrying a local-only attribute, a nested named definition, an empty `targetNamespace` or `import` namespace, and an all-group reference with an illegal `minOccurs`/`maxOccurs` each underline their own node instead of the schema root.
|
||||
- Schema compile diagnostics are now located: `SchemaError.inconsistent` carries `[ValidationError]` with coding paths that resolve to `TreeNode.sourceRange` on a ranged schema tree (#169). Parse-time facet and structure findings attach to their declaring nodes where possible; `SchemaError.inconsistentFindings` exposes the full list. Schematron reuses the shared `validationCodingPath()` helper. Value-constraint errors now locate to their declaring element or attribute: a `default`/`fixed` on an ID-derived type, a value that is invalid for its built-in, named user, or inline type, and an illegal combination of value constraints each underline the offending declaration rather than the schema root. The structure-pass checkers are likewise located: a facet inapplicable to a list or union variety, a derivation by restriction of `xs:anySimpleType` and a constraining facet on `anySimpleType`-based simple content, a top-level definition missing its name or carrying a local-only attribute, a nested named definition, an empty `targetNamespace` or `import` namespace, and an all-group reference with an illegal `minOccurs`/`maxOccurs` each underline their own node instead of the schema root. The simple-type-pass checkers join them: a list whose item type is itself a list or a union containing a list, a list declaring both or neither of `itemType`/inline `simpleType`, an empty union, a simpleType restriction whose base resolves to a complex type, an `itemType`/`memberType` referencing a type that is `final` for list/union derivation, and an attribute typed by a complex type each underline the offending `list`, `union`, `restriction`, or `attribute` node rather than the schema root.
|
||||
|
||||
- DTD parse-time validity findings are warnings until strict validation promotes them; `validateAgainstInternalDTD` returns full findings (errors and warnings).
|
||||
|
||||
|
||||
@ -19,21 +19,31 @@ extension PureXML.Schema.XSDParser {
|
||||
/// `types` first. Self-contained schema; a foreign or unresolved type is left
|
||||
/// alone. Strictly zero false positive: an attribute can never carry a complex
|
||||
/// type, so no valid schema is affected.
|
||||
static func attributeTypeMustBeSimpleErrors(_ schema: XSDTree, _ context: PureXML.Schema.XSDContext, _ types: [String: PureXML.Schema.ElementType]) -> [String] {
|
||||
static func attributeTypeMustBeSimpleFindings(
|
||||
_ schema: XSDTree,
|
||||
_ context: PureXML.Schema.XSDContext,
|
||||
_ types: [String: PureXML.Schema.ElementType],
|
||||
) -> [PureXML.Schema.SchemaLocatedFinding] {
|
||||
guard !skipsCrossDocumentRules(schema, compositionLoaded: context.compositionLoaded) else { return [] }
|
||||
let bindings = context.namespaceBindings
|
||||
var errors: [String] = []
|
||||
var findings: [PureXML.Schema.SchemaLocatedFinding] = []
|
||||
for attribute in descendants(schema, named: "attribute") where attribute.name?.namespaceURI == xsdNamespace {
|
||||
guard let typeName = AttrNode.attribute(attribute, "type") else { continue }
|
||||
let namespace = AttrNode.referenceNamespace(typeName, bindings)
|
||||
let local = AttrNode.stripPrefix(typeName)
|
||||
if namespace == context.targetNamespace, case .complex? = types[local] {
|
||||
errors.append("an attribute's type must be a simple type, not the complex type '\(local)'")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "an attribute's type must be a simple type, not the complex type '\(local)'",
|
||||
node: attribute,
|
||||
))
|
||||
} else if namespace == xsdNamespace, types[local] == nil, local == "anyType" {
|
||||
errors.append("an attribute's type must be a simple type, not 'anyType'")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "an attribute's type must be a simple type, not 'anyType'",
|
||||
node: attribute,
|
||||
))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
return findings
|
||||
}
|
||||
|
||||
/// Schema-validity findings for attribute-use uniqueness (XSD 1.0
|
||||
|
||||
@ -77,12 +77,10 @@ extension PureXML.Validation {
|
||||
|
||||
static var simpleTypeFinalControlsValid: Validation<PureXML.Schema.SchemaCompileRoot, PureXML.Schema.SchemaCompileContext> {
|
||||
compileRule("Simple-type final controls are declared consistently") { document in
|
||||
PureXML.Schema.SchemaLocatedFinding.unlocated(
|
||||
PureXML.Schema.XSDParser.simpleTypeFinalErrors(
|
||||
document.schema,
|
||||
compositionLoaded: document.context.compositionLoaded,
|
||||
containers: document.containers,
|
||||
),
|
||||
PureXML.Schema.XSDParser.simpleTypeFinalFindings(
|
||||
document.schema,
|
||||
compositionLoaded: document.context.compositionLoaded,
|
||||
containers: document.containers,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -213,17 +211,13 @@ extension PureXML.Validation {
|
||||
|
||||
static var simpleTypeBasesAreSimple: Validation<PureXML.Schema.SchemaCompileRoot, PureXML.Schema.SchemaCompileContext> {
|
||||
compileRule("Simple types do not derive from complex types") { document in
|
||||
PureXML.Schema.SchemaLocatedFinding.unlocated(
|
||||
PureXML.Schema.XSDParser.simpleTypeBaseNotComplexErrors(document.schema, in: document.context),
|
||||
)
|
||||
PureXML.Schema.XSDParser.simpleTypeBaseNotComplexFindings(document.schema, in: document.context)
|
||||
}
|
||||
}
|
||||
|
||||
static var simpleTypeVarietiesValid: Validation<PureXML.Schema.SchemaCompileRoot, PureXML.Schema.SchemaCompileContext> {
|
||||
compileRule("Simple-type varieties are declared consistently") { document in
|
||||
PureXML.Schema.SchemaLocatedFinding.unlocated(
|
||||
PureXML.Schema.XSDParser.simpleTypeVarietyErrors(document.schema, document.context),
|
||||
)
|
||||
PureXML.Schema.XSDParser.simpleTypeVarietyFindings(document.schema, document.context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,9 +345,7 @@ extension PureXML.Validation.SchemaCompile {
|
||||
static var attributeTypesSimple: PureXML.Validation.Validation<PureXML.Schema.SchemaCompileRoot, PureXML.Schema.SchemaCompileContext> {
|
||||
compileRule("An attribute's type is a simple type") { document in
|
||||
guard let namedTypes = document.namedTypes else { return [] }
|
||||
return PureXML.Schema.SchemaLocatedFinding.unlocated(
|
||||
PureXML.Schema.XSDParser.attributeTypeMustBeSimpleErrors(document.schema, document.context, namedTypes),
|
||||
)
|
||||
return PureXML.Schema.XSDParser.attributeTypeMustBeSimpleFindings(document.schema, document.context, namedTypes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -232,8 +232,8 @@ extension PureXML.Schema.XSDParser {
|
||||
return errors
|
||||
}
|
||||
|
||||
static func simpleTypeBaseNotComplexErrors(_ schema: XSDTree, in context: PureXML.Schema.XSDContext) -> [String] {
|
||||
var errors: [String] = []
|
||||
static func simpleTypeBaseNotComplexFindings(_ schema: XSDTree, in context: PureXML.Schema.XSDContext) -> [PureXML.Schema.SchemaLocatedFinding] {
|
||||
var findings: [PureXML.Schema.SchemaLocatedFinding] = []
|
||||
let bindings = PureXML.Schema.XSDNode.namespaceBindings(of: schema)
|
||||
let targetNamespace = context.targetNamespace
|
||||
|
||||
@ -262,11 +262,11 @@ extension PureXML.Schema.XSDParser {
|
||||
if node.name?.namespaceURI == xsdNamespace {
|
||||
switch local {
|
||||
case "restriction":
|
||||
Self.checkRestrictionUnderSimpleType(node, isComplex: isComplex, errors: &errors)
|
||||
Self.checkRestrictionUnderSimpleType(node, isComplex: isComplex, findings: &findings)
|
||||
case "list":
|
||||
Self.checkList(node, isComplex: isComplex, errors: &errors)
|
||||
Self.checkList(node, isComplex: isComplex, findings: &findings)
|
||||
case "union":
|
||||
Self.checkUnion(node, isComplex: isComplex, errors: &errors)
|
||||
Self.checkUnion(node, isComplex: isComplex, findings: &findings)
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -277,26 +277,35 @@ extension PureXML.Schema.XSDParser {
|
||||
}
|
||||
}
|
||||
check(schema)
|
||||
return errors
|
||||
return findings
|
||||
}
|
||||
|
||||
private static func checkRestrictionUnderSimpleType(_ node: XSDTree, isComplex: (String) -> Bool, errors: inout [String]) {
|
||||
private static func checkRestrictionUnderSimpleType(_ node: XSDTree, isComplex: (String) -> Bool, findings: inout [PureXML.Schema.SchemaLocatedFinding]) {
|
||||
guard let parent = node.parent, parent.name?.namespaceURI == xsdNamespace, PureXML.Schema.XSDNode.localName(parent) == "simpleType" else { return }
|
||||
if let base = PureXML.Schema.XSDNode.attribute(node, "base"), isComplex(base) {
|
||||
errors.append("base type '\(base)' of simpleType restriction must be a simple type, not a complex type")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "base type '\(base)' of simpleType restriction must be a simple type, not a complex type",
|
||||
node: node,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private static func checkList(_ node: XSDTree, isComplex: (String) -> Bool, errors: inout [String]) {
|
||||
private static func checkList(_ node: XSDTree, isComplex: (String) -> Bool, findings: inout [PureXML.Schema.SchemaLocatedFinding]) {
|
||||
if let itemType = PureXML.Schema.XSDNode.attribute(node, "itemType"), isComplex(itemType) {
|
||||
errors.append("itemType '\(itemType)' of list must be a simple type, not a complex type")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "itemType '\(itemType)' of list must be a simple type, not a complex type",
|
||||
node: node,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private static func checkUnion(_ node: XSDTree, isComplex: (String) -> Bool, errors: inout [String]) {
|
||||
private static func checkUnion(_ node: XSDTree, isComplex: (String) -> Bool, findings: inout [PureXML.Schema.SchemaLocatedFinding]) {
|
||||
if let members = PureXML.Schema.XSDNode.attribute(node, "memberTypes") {
|
||||
for token in members.split(whereSeparator: \.isWhitespace) where isComplex(String(token)) {
|
||||
errors.append("memberType '\(token)' of union must be a simple type, not a complex type")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "memberType '\(token)' of union must be a simple type, not a complex type",
|
||||
node: node,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ extension PureXML.Schema.XSDParser {
|
||||
/// where the single target namespace makes the names resolved by local name
|
||||
/// unambiguous; with external definitions the check stands down (a disclosed
|
||||
/// under-rejection).
|
||||
static func simpleTypeFinalErrors(_ schema: XSDTree, compositionLoaded: Bool, containers: [XSDTree]) -> [String] {
|
||||
static func simpleTypeFinalFindings(_ schema: XSDTree, compositionLoaded: Bool, containers: [XSDTree]) -> [PureXML.Schema.SchemaLocatedFinding] {
|
||||
guard !skipsCrossDocumentRules(schema, compositionLoaded: compositionLoaded) else { return [] }
|
||||
let finalOf = compositionLoaded ? mergedSimpleTypeFinalMap(containers) : simpleTypeFinalMap(schema)
|
||||
guard finalOf.values.contains(where: { !$0.isEmpty }) else { return [] }
|
||||
@ -24,8 +24,8 @@ extension PureXML.Schema.XSDParser {
|
||||
// confused with a user type of the same local name.
|
||||
let bindings = FinalNode.namespaceBindings(of: schema)
|
||||
let target = FinalNode.attribute(schema, "targetNamespace")
|
||||
return listItemFinalErrors(schema, finalOf, bindings, target)
|
||||
+ unionMemberFinalErrors(schema, finalOf, bindings, target)
|
||||
return listItemFinalFindings(schema, finalOf, bindings, target)
|
||||
+ unionMemberFinalFindings(schema, finalOf, bindings, target)
|
||||
}
|
||||
|
||||
private static func mergedSimpleTypeFinalMap(_ containers: [XSDTree]) -> [String: Set<String>] {
|
||||
@ -57,33 +57,49 @@ extension PureXML.Schema.XSDParser {
|
||||
return finalOf
|
||||
}
|
||||
|
||||
private static func listItemFinalErrors(_ schema: XSDTree, _ finalOf: [String: Set<String>], _ bindings: [String: String], _ target: String?) -> [String] {
|
||||
var errors: [String] = []
|
||||
private static func listItemFinalFindings(
|
||||
_ schema: XSDTree,
|
||||
_ finalOf: [String: Set<String>],
|
||||
_ bindings: [String: String],
|
||||
_ target: String?,
|
||||
) -> [PureXML.Schema.SchemaLocatedFinding] {
|
||||
var findings: [PureXML.Schema.SchemaLocatedFinding] = []
|
||||
for list in descendants(schema, named: "list") {
|
||||
guard let item = FinalNode.attribute(list, "itemType"),
|
||||
FinalNode.referenceNamespace(item, bindings) == target
|
||||
else { continue }
|
||||
let name = FinalNode.stripPrefix(item)
|
||||
if finalOf[name]?.contains("list") == true {
|
||||
errors.append("simple type '\(name)' is final for 'list' and may not be a list item type")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "simple type '\(name)' is final for 'list' and may not be a list item type",
|
||||
node: list,
|
||||
))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
return findings
|
||||
}
|
||||
|
||||
private static func unionMemberFinalErrors(_ schema: XSDTree, _ finalOf: [String: Set<String>], _ bindings: [String: String], _ target: String?) -> [String] {
|
||||
var errors: [String] = []
|
||||
private static func unionMemberFinalFindings(
|
||||
_ schema: XSDTree,
|
||||
_ finalOf: [String: Set<String>],
|
||||
_ bindings: [String: String],
|
||||
_ target: String?,
|
||||
) -> [PureXML.Schema.SchemaLocatedFinding] {
|
||||
var findings: [PureXML.Schema.SchemaLocatedFinding] = []
|
||||
for union in descendants(schema, named: "union") {
|
||||
guard let members = FinalNode.attribute(union, "memberTypes") else { continue }
|
||||
for token in members.split(whereSeparator: \.isWhitespace) {
|
||||
guard FinalNode.referenceNamespace(String(token), bindings) == target else { continue }
|
||||
let name = FinalNode.stripPrefix(String(token))
|
||||
if finalOf[name]?.contains("union") == true {
|
||||
errors.append("simple type '\(name)' is final for 'union' and may not be a union member type")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "simple type '\(name)' is final for 'union' and may not be a union member type",
|
||||
node: union,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors
|
||||
return findings
|
||||
}
|
||||
|
||||
/// The derivation tokens a `final` value forbids; `#all` forbids every simple-type
|
||||
|
||||
@ -3,9 +3,9 @@ extension PureXML.Schema.XSDParser {
|
||||
/// the itemType of a list must be atomic or union (where all member types are atomic).
|
||||
/// That is, a list's itemType cannot be a list or a union containing a list, and
|
||||
/// cannot resolve to the simple ur-type 'anySimpleType'.
|
||||
static func simpleTypeVarietyErrors(_ schema: XSDTree, _ context: PureXML.Schema.XSDContext) -> [String] {
|
||||
static func simpleTypeVarietyFindings(_ schema: XSDTree, _ context: PureXML.Schema.XSDContext) -> [PureXML.Schema.SchemaLocatedFinding] {
|
||||
let targetNamespace = context.targetNamespace
|
||||
var errors: [String] = []
|
||||
var findings: [PureXML.Schema.SchemaLocatedFinding] = []
|
||||
|
||||
func check(_ node: XSDTree) {
|
||||
let local = PureXML.Schema.XSDNode.localName(node)
|
||||
@ -31,13 +31,19 @@ extension PureXML.Schema.XSDParser {
|
||||
}
|
||||
|
||||
if !isForeign, isListOrUnionContainingList(item) {
|
||||
errors.append("the item type of a list must be atomic or union of atomic, not a list or union containing list")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "the item type of a list must be atomic or union of atomic, not a list or union containing list",
|
||||
node: node,
|
||||
))
|
||||
}
|
||||
// src-simple-type.2: a list has its item type from EITHER the
|
||||
// itemType attribute OR an inline simpleType child, never both and
|
||||
// never neither.
|
||||
if hasItemTypeAttribute(node) == hasSimpleTypeChild(node) {
|
||||
errors.append("a list must have either an 'itemType' attribute or an inline simpleType child, but not both")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "a list must have either an 'itemType' attribute or an inline simpleType child, but not both",
|
||||
node: node,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +51,10 @@ extension PureXML.Schema.XSDParser {
|
||||
// attribute, inline simpleType children, or both, so it must declare at
|
||||
// least one of them (an empty union has no member type definitions).
|
||||
if isEmptyUnion(node, local) {
|
||||
errors.append("a union must declare at least one member type, through 'memberTypes' or an inline simpleType child")
|
||||
findings.append(PureXML.Schema.SchemaLocatedFinding(
|
||||
reason: "a union must declare at least one member type, through 'memberTypes' or an inline simpleType child",
|
||||
node: node,
|
||||
))
|
||||
}
|
||||
|
||||
for child in PureXML.Schema.XSDNode.elementChildren(node) {
|
||||
@ -54,7 +63,7 @@ extension PureXML.Schema.XSDParser {
|
||||
}
|
||||
|
||||
check(schema)
|
||||
return errors
|
||||
return findings
|
||||
}
|
||||
|
||||
/// A `union` element in the schema namespace that declares no member types at
|
||||
|
||||
91
Tests/SchemaSimpleTypeLocatedDiagnosticsTests.swift
Normal file
91
Tests/SchemaSimpleTypeLocatedDiagnosticsTests.swift
Normal file
@ -0,0 +1,91 @@
|
||||
import Testing
|
||||
@testable import PureXML
|
||||
|
||||
/// Simple-type-pass schema compile findings (variety constraints, a complex base
|
||||
/// under a simpleType restriction, `final` enforcement for list/union derivation,
|
||||
/// and an attribute typed by a complex type) carry validation coding paths that
|
||||
/// resolve to source spans, the IDE underline flow for #169. Split from the other
|
||||
/// located-diagnostics suites to keep each suite within the type-body length budget.
|
||||
@Suite("Schema simple-type-pass located compile diagnostics")
|
||||
struct SchemaSimpleTypeLocatedDiagnosticsTests {
|
||||
private func inconsistentFindings(in xsd: String) throws -> [PureXML.Validation.ValidationError] {
|
||||
do {
|
||||
_ = try PureXML.Schema.Document(xsd)
|
||||
Issue.record("expected schema compilation to fail")
|
||||
return []
|
||||
} catch let error as PureXML.Schema.SchemaError {
|
||||
guard case let .inconsistent(findings) = error else {
|
||||
Issue.record("expected .inconsistent, got \(error)")
|
||||
return []
|
||||
}
|
||||
return findings
|
||||
}
|
||||
}
|
||||
|
||||
@Test("An empty union is located on the union element")
|
||||
func test_emptyUnionLocated() throws {
|
||||
let xsd = """
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:simpleType name="t"><xs:union/></xs:simpleType>
|
||||
</xs:schema>
|
||||
"""
|
||||
let findings = try inconsistentFindings(in: xsd)
|
||||
let located = try #require(findings.first { $0.reason.contains("a union must declare at least one member type") })
|
||||
#expect(located.codingPath.map(\.stringValue) == ["xs:schema", "xs:simpleType", "xs:union"])
|
||||
|
||||
let (tree, _) = PureXML.readTree(xsd)
|
||||
#expect(tree.sourceRange(at: located.codingPath)?.start.line == 2)
|
||||
}
|
||||
|
||||
@Test("A simpleType restriction of a complex base is located on the restriction")
|
||||
func test_simpleTypeBaseComplexLocated() throws {
|
||||
let xsd = """
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:complexType name="ct"><xs:sequence/></xs:complexType>
|
||||
<xs:simpleType name="t"><xs:restriction base="ct"/></xs:simpleType>
|
||||
</xs:schema>
|
||||
"""
|
||||
let findings = try inconsistentFindings(in: xsd)
|
||||
let located = try #require(findings.first { $0.reason.contains("of simpleType restriction must be a simple type") })
|
||||
#expect(located.codingPath.map(\.stringValue) == ["xs:schema", "xs:simpleType", "xs:restriction"])
|
||||
|
||||
let (tree, _) = PureXML.readTree(xsd)
|
||||
#expect(tree.sourceRange(at: located.codingPath)?.start.line == 3)
|
||||
}
|
||||
|
||||
@Test("A list item type that is final for list is located on the list element")
|
||||
func test_listItemFinalLocated() throws {
|
||||
let xsd = """
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:simpleType name="it" final="list"><xs:restriction base="xs:string"/></xs:simpleType>
|
||||
<xs:simpleType name="t"><xs:list itemType="it"/></xs:simpleType>
|
||||
</xs:schema>
|
||||
"""
|
||||
let findings = try inconsistentFindings(in: xsd)
|
||||
let located = try #require(findings.first { $0.reason.contains("is final for 'list' and may not be a list item type") })
|
||||
#expect(located.codingPath.map(\.stringValue) == ["xs:schema", "xs:simpleType", "xs:list"])
|
||||
|
||||
let (tree, _) = PureXML.readTree(xsd)
|
||||
#expect(tree.sourceRange(at: located.codingPath)?.start.line == 3)
|
||||
}
|
||||
|
||||
@Test("An attribute typed by a complex type is located on the attribute")
|
||||
func test_attributeTypeComplexLocated() throws {
|
||||
let xsd = """
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:complexType name="ct"><xs:sequence/></xs:complexType>
|
||||
<xs:complexType name="t">
|
||||
<xs:attribute name="a" type="ct"/>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
||||
"""
|
||||
let findings = try inconsistentFindings(in: xsd)
|
||||
let located = try #require(findings.first { $0.reason.contains("an attribute's type must be a simple type, not the complex type") })
|
||||
#expect(located.codingPath.map(\.stringValue) == ["xs:schema", "xs:complexType", "xs:attribute"])
|
||||
|
||||
let (tree, _) = PureXML.readTree(xsd)
|
||||
let range = tree.sourceRange(at: located.codingPath)
|
||||
#expect(range?.start.line == 4)
|
||||
#expect(range?.start.column == 5)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user