Commit Graph

5 Commits

Author SHA1 Message Date
3a6aad0eb2
fix(schema): reject a redefine whose self-reference duplicates a particle (UPA) (#253)
A redefined model group whose body references itself expands, in the compiled
content model, to the original group it redefines. Two gaps let an ambiguity
created that way slip through the Unique Particle Attribution check (cos-nonambig).

The compositional determinism engine treated the self-reference as contributing
an empty decision set, so the original group's particles were invisible to the
overlap test. It now expands one level to the redefine base, exactly as the
instance content-model path does, so a particle the original contributes is seen.
The expansion only surfaces genuinely reachable items, so the decision sets stay
a subset of the true model: the check can catch a real ambiguity but never invent
one, holding the false-positive direction at zero.

Separately, the UPA check walked only the root document, so an ambiguity a root
redefine creates in a complex type that lives in an included document was never
evaluated. It now runs over every schema root in the composition under the shared
redefine-effective context, mirroring the existing cross-document top-level
declaration check.

Closes XSTS schN10 (a three-level redefine chain). XSTS invalid-schemas-accepted
11 -> 10, valid-schemas-rejected held at 0. Full suite 1609 tests green in debug
and release.
2026-06-24 00:06:23 +02:00
0a76658a28 fix(schema): check UPA over an extension's effective content model
Unique Particle Attribution checked only a complexContent extension's own
particles, missing the cross-boundary non-determinism with its base: the
effective content of an extension is the base type's content followed by the
extension's, so a base particle that can recur or end optionally competes
with the extension's leading particles. An element matching either was a UPA
violation the single-node check did not see, and the schema was wrongly
accepted (XSTS particlesZ022).

Build the content model over the effective sequence (base content prepended,
resolved recursively). The base is followed 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 model that cannot be fully assembled is rejected (an under-rejection, never
a false positive). For a non-extension the effective list is a single node, so
sequence([x]) reproduces the prior automaton exactly.

Verified against the full W3C XSTS 2006-11-06 suite: invalid-schemas-accepted
31 -> 29 (particlesZ022 and one more), valid-schemas-rejected held at 0, no
other bucket moved. Adds UPA tests for an ambiguous extension and a
deterministic one (the over-rejection guard); moves the content-model-node and
effective-model helpers to a new file to stay within the type-body and
file-length budgets.
2026-06-21 22:12:32 +02:00
9a2cb7eab3 fix(schema): detect UPA overlap of ##other wildcards
Unique Particle Attribution missed overlapping xs:any wildcards involving
##other: wildcardsOverlap returned false for any pair touching .other, so two
competing wildcards whose namespaces actually intersect were not flagged as
non-deterministic and the schema was wrongly accepted.

Compute the overlap correctly: ##other against ##other always overlaps (both
admit every namespace but their target and the absent one, infinite sets that
always intersect), and ##other against an enumerated set overlaps iff the set
names a namespace that is neither absent (##local) nor the target. The new
predicate matches Wildcard.admits exactly, so disjoint cases (##other vs
##local, or vs the target namespace) stay deterministic and no valid schema
is newly rejected.

Verified against the full W3C XSTS 2006-11-06 suite: invalid-schemas-accepted
35 -> 31 (wildI009/wildI013/wildI014 and one more), valid-schemas-rejected
held at 0 (no other bucket moved). Adds UPA unit tests for both rejection
shapes and a ##other-vs-##local determinism guard; updates the pinned XSTS
count 35 -> 31.
2026-06-21 21:40:56 +02:00
83e6e27d12 style(format): adopt --importgrouping testable-bottom from canonical rules-swift
Group @testable imports at the bottom, per public/rules-swift's swiftformat. A
behavior-preserving import-ordering reformat across 252 files (mostly tests).
--trailingclosures was deliberately not adopted (cosmetic, and swiftformat flags
it as caution-worthy).
2026-06-18 20:33:07 +02:00
90ae16cba1 feat(schema): enforce Unique Particle Attribution (cos-nonambig)
Reject ambiguous content models at compile time via a Glushkov position
automaton over a literal, QName-resolved view of the raw schema tree, built
just for this rule and independent of the instance-validation content model.

The literal view resolves a ref's QName through the schema's prefix bindings
(not the blanket target namespace) and does not expand substitution groups,
removing the two false-overlap sources a check over the compiled model
produced. Determinism is per particle: a particle competing with a repetition
of itself is never a violation, so a fixed count {n,n} unrolls into copies in
sequence while a true range or unbounded repeat uses a self-loop, and only two
distinct source particles with overlapping labels conflict. The overlap test
is QName-only, an under-rejection that can never over-reject.

A position cap bounds pathological nesting (skipping the check, never
over-rejecting). The element-declarations-consistent test loses its
incidentally-ambiguous (a?, a) case in favor of a deterministic (a, b, a).

XSTS: invalid schemas accepted 884 to 864, valid schemas rejected held at 72,
instance buckets unchanged.
2026-06-14 09:35:33 +02:00