diff --git a/CHANGELOG.md b/CHANGELOG.md
index bb94bc8..413f984 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
+- An unprefixed `type`/`base`/`itemType`/`memberTypes` reference to a type declared in a non-empty target namespace is now rejected as undeclared. With no default `xmlns`, an unprefixed QName resolves to the absent namespace, so `type="t"` is `{}t`, not `{urn:x}t` where the type lives; `isUndeclaredReferenceType` matched the type by local name and accepted it. It now mirrors `isUndeclaredReferenceRef`: a no-namespace reference is undeclared when the schema has a non-empty target namespace (a chameleon-included global, and an imported no-namespace type via the foreign pools, remain accepted). XSTS invalid-schemas-accepted 27 → 25 (addB009/test66059 and one more), valid-schemas-rejected held at 0.
- A schema reference (`ref`/`substitutionGroup`) that resolves to the XSD namespace is now rejected as undeclared. The XSD namespace declares no referenceable user component, so `isUndeclaredReferenceType` already rejected a `type`/`base` in it, but `isUndeclaredReferenceRef` fell through to a lenient accept. This is reached when an unprefixed reference picks up a default `xmlns` of the XSD namespace, so it is not in the target namespace where the schema's own components live: e.g. `xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="foo"` with `` resolves to `{XSD}root`, not `{foo}root`, and is undeclared. A schema that itself targets the XSD namespace, and a genuinely loaded XSD-namespace document, are unaffected. XSTS invalid-schemas-accepted 29 → 27 (xsd013/xsd014), valid-schemas-rejected held at 0.
- Unique Particle Attribution now checks a `complexContent` extension against its EFFECTIVE content model (the base type's content followed by the extension's), not just the extension's own particles. A base whose trailing particle can recur or end optionally competes with the extension's leading particles, so an element could match either across the extension boundary; that cross-boundary non-determinism was missed and the schema wrongly accepted. The base content is resolved and prepended only for a local (target-namespace), non-cyclic base chain; a foreign, built-in (`xs:anyType`), unresolved, or cyclic base leaves the check on the extension's own content, so no valid schema is newly rejected. XSTS invalid-schemas-accepted 31 → 29 (particlesZ022 and one more), valid-schemas-rejected held at 0.
- Unique Particle Attribution now detects overlapping `xs:any` wildcards involving `##other`. `wildcardsOverlap` returned false for any pair involving `##other`, so two competing wildcards whose namespaces actually intersect, two `##other` constraints (which always share infinitely many namespaces) or `##other` against an enumerated set naming a non-target, non-absent namespace, were not flagged as non-deterministic, and the schema was wrongly accepted. The overlap is now computed correctly: `##other`∩`##other` always overlaps, and `##other`∩enumerated overlaps iff the set names a namespace that is neither absent (`##local`) nor the target. Disjoint cases (`##other` against `##local` or against the target namespace) remain deterministic, so no valid schema is newly rejected. XSTS invalid-schemas-accepted 35 → 31 (wildI009/wildI013/wildI014 and one more), valid-schemas-rejected held at 0.
diff --git a/Sources/Schema/SchemaReferenceResolution.swift b/Sources/Schema/SchemaReferenceResolution.swift
index 835ccb8..fdc3d43 100644
--- a/Sources/Schema/SchemaReferenceResolution.swift
+++ b/Sources/Schema/SchemaReferenceResolution.swift
@@ -54,6 +54,14 @@ extension PureXML.Schema.XSDParser {
return !foreignTypes.contains(localPart)
}
if uri == nil || uri == "" {
+ // A no-namespace (unprefixed, no default `xmlns`) reference resolves to
+ // the absent namespace. It matches a chameleon-included global adopting
+ // the includer's namespace; otherwise, when the schema has a non-empty
+ // target namespace, it cannot name a type that lives there, so it is
+ // undeclared (XSTS addB009 / test66059). This mirrors the same branch in
+ // `isUndeclaredReferenceRef`.
+ if context.chameleonNamespace, context.types.contains(localPart) { return false }
+ if !(context.targetNamespace == nil || context.targetNamespace == "") { return true }
return !context.types.contains(localPart)
}
return false
diff --git a/Tests/SchemaReferenceTests.swift b/Tests/SchemaReferenceTests.swift
index 93fab0f..d043068 100644
--- a/Tests/SchemaReferenceTests.swift
+++ b/Tests/SchemaReferenceTests.swift
@@ -71,6 +71,27 @@ struct SchemaReferenceTests {
_ = try PureXML.Schema.Document(qualified)
}
+ @Test("an unprefixed type reference to a target-namespace type is undeclared")
+ func test_unprefixedTypeRefToTargetNamespace() throws {
+ // No default xmlns, so `type="t"` resolves to {}t, not {urn:x}t where the
+ // type is declared: undeclared (XSTS addB009 / test66059).
+ let unprefixed = """
+
+
+
+
+ """
+ #expect(throws: (any Error).self) { _ = try PureXML.Schema.Document(unprefixed) }
+ // Guard: qualifying the reference to the target namespace resolves.
+ let qualified = """
+
+
+
+
+ """
+ _ = try PureXML.Schema.Document(qualified)
+ }
+
@Test("references are not checked when an import may supply them")
func test_importSkipsCheck() throws {
// A dangling reference into an imported namespace is tolerated: the default
diff --git a/Tests/SchemaXSIAttributeTests.swift b/Tests/SchemaXSIAttributeTests.swift
index 90c8402..0bd74bd 100644
--- a/Tests/SchemaXSIAttributeTests.swift
+++ b/Tests/SchemaXSIAttributeTests.swift
@@ -80,13 +80,16 @@ struct SchemaXSIAttributeTests {
@Test("the same qualified attribute targeting a non-XSI namespace is valid")
func test_qualifiedAttributeOutsideXSIValid() {
+ // The type reference is qualified to the target namespace (`tns:attRef`); an
+ // unprefixed `attRef` with no default xmlns would resolve to {}attRef, not
+ // {urn:ok}attRef, and is itself invalid (see SchemaReferenceTests).
let schema = """
-
-
+
"""
#expect(compiles(schema), "qualifying into a non-XSI namespace is fine")
diff --git a/Tests/XSTSSuiteTests.swift b/Tests/XSTSSuiteTests.swift
index 563ef3c..6e92759 100644
--- a/Tests/XSTSSuiteTests.swift
+++ b/Tests/XSTSSuiteTests.swift
@@ -146,9 +146,9 @@ struct XSTSSuiteTests {
/// model-group occurrence, identity-constraint content, derivation base kind, attribute/simple-type,
/// composition, simpleContent inline-type, complexContent mixedness, and substitution-group anySimpleType checks.
/// src-resolve (#185) 43->39; xsi:Not-Allowed (#183) 39->37; NameAndTypeOK restriction-only + cos-all-limited group-ref (#158) 37->35.
- /// UPA `##other` overlap 35->31; UPA effective extension model 31->29; ref to XSD namespace is undeclared 29->27 (xsd013/xsd014); valid-rejected held at 0.
+ /// UPA `##other` 35->31; UPA effective-extension model 31->29; ref to XSD ns undeclared 29->27; unprefixed type-ref to target ns undeclared 27->25; valid-rejected 0.
private let knownSchemaValidRejected = 0
- private let knownSchemaInvalidAccepted = 27
+ private let knownSchemaInvalidAccepted = 25
private let knownInstanceValidRejected = 0
/// Instance bucket (133 -> 26), per-step deltas in CHANGELOG.md: xsi:type must derive from the
/// declared type (complex/atomic/list/union via the element-ref chain); anyType cannot stand in