PureXML/Tests/SchemaSimpleTypeLocatedDiagnosticsTests.swift
Mihaela Mihaljevic 356e87581c 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.
2026-06-21 15:55:59 +02:00

92 lines
4.3 KiB
Swift

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)
}
}