Preserve namespaces of literal result elements in output#155
Open
ajbufort wants to merge 1 commit into
Open
Conversation
Literal result elements and their attributes lost their namespaces entirely: xml_name in the XSLT compiler hardcoded an empty namespace when building element and attribute names, so a stylesheet producing XHTML emitted <html> with no xmlns and xml:lang became lang. Fix this in three connected places: - xml_name now passes the parsed name's namespace through to the XmlName IR instruction. - A literal result element copies its in-scope namespace bindings to the result (XSLT 3.0 section 11.1.3), so output keeps the prefixes the stylesheet author chose, including default namespace declarations. The parser computes these per element, minus the XSLT namespace and excluded namespaces, and minus bindings the nearest enclosing literal result element already declares, so nested elements don't redeclare them. Content that constructs a separate tree (xsl:variable, xsl:param, xsl:with-param, xsl:message) resets that suppression, since its elements cannot rely on an enclosing element's declarations. exclude-result-prefixes and extension-element-prefixes now resolve each prefix to a namespace at the element bearing the attribute, as the spec requires, replacing the stored prefix lists and the ExcludeResultPrefixes::combine whose TODO asked for exactly this. (An unbound prefix should be a static error, XTSE0808; left as a TODO since the parser has no error-code machinery yet.) - Serialization performs namespace fixup on the normalized document: every top-level element gets create_missing_prefixes, which invents prefixes for namespaces with no binding (e.g. from xsl:element with a namespace attribute, which previously always failed with XPST0081), after explicitly binding the implicit xml prefix so it isn't given an invented one. The html output method needs no fixup: the html5 serializer emits no namespace declarations (covered by a regression test). XSLT conformance goes from 1098 to 1169 tests passing with the filters updated; XPath conformance is unchanged. Fixes Paligo#139. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #139.
The problem
Literal result elements and their attributes lost their namespaces entirely. The stylesheet from #139 produced
<html lang="en-GB">- noxmlns, andxml:langdegraded tolang. The root cause:xml_namein the XSLT compiler hardcoded an empty namespace when compiling element and attribute names, so every name reached the interpreter namespace-less. Separately,xsl:elementwith anamespaceattribute always failed withXPST0081, because the constructed name did carry a namespace but nothing ever created a prefix binding for it, so serialization failed withMissingPrefix.The fix, in three connected parts
xml_namepasses the namespace through to theXmlNameIR instruction instead of the empty string. The interpreter and Xot already handled namespaced names correctly - nothing downstream needed changes.Literal result elements copy their in-scope namespace bindings to the result (XSLT 3.0 section 11.1.3), so output preserves the prefixes the stylesheet author chose - a default
xmlnsstays a defaultxmlns. The parser computes the bindings per element: in-scope namespaces minus the XSLT namespace and excluded namespaces, and minus whatever the nearest statically-enclosing literal result element already declares, so nested elements don't redeclare. Content that constructs a separate tree (xsl:variable,xsl:param,xsl:with-param,xsl:message) resets that suppression, since its elements can't rely on an enclosing element's declarations. The compiler emits the existingXmlNamespace/XmlAppendinstructions.Along the way,
exclude-result-prefixesandextension-element-prefixesnow resolve each prefix to a namespace at the element bearing the attribute, as the spec requires (so a later rebinding of the same prefix isn't wrongly excluded, and#alldesignates only what was in scope where it was written). This replaces the stored prefix lists andExcludeResultPrefixes::combine- whose own TODO asked for exactly this resolution.Serialization performs namespace fixup on the normalized document: every top-level element gets
create_missing_prefixes, which invents prefixes for namespaces with no binding anywhere (this is what makesxsl:elementwithnamespacework), after explicitly binding the implicitxmlprefix so it isn't given an invented one.With this, the stylesheet from the issue produces:
Testing
xee-xslt-compiler/tests/test_xslt.rs: default-namespace LRE, prefixed LRE (prefix preserved), stylesheet-level default namespace, unused namespace copied,xml:langattribute,xsl:elementwithnamespacesurviving serialization, namespaces insidexsl:variablecontent,exclude-result-prefixesresolved at the bearing element,#allnot stripping later local declarations, multi-element sequence fixup, and the html output method (which needs no fixup - the html5 serializer emits no namespace declarations)update/checkflow, no regressions; XPath conformance unchanged (20221, including in debug mode as CI runs it)cargo fmt,clippy --all-targets --all-features -- -D warnings, and fullcargo testclean (AST snapshots regenerated for the newnamespacesfield)Notes and limitations
inherit-namespaces/copy-namespacesattribute support (tracked in conformance/xslt.md) is not part of this change; this implements the default section 11.1.3 behavior.exclude-result-prefixesshould be the static error XTSE0808; the parser has no error-code machinery today, so it's a marked TODO rather than a new error architecture in this PR.deduplicate_namespacescould clean this up at serialization time, but calling it unconditionally would also change output for parsed documents, so I left it out; happy to add it if you'd prefer.create_missing_prefixesin Xot invents a prefix for the implicitxmlnamespace, which the serializer then skips declaring - the explicit pre-binding here works around that; happy to file that upstream on Xot if you agree it's a bug.MissingPrefix) if that cost matters to you.Generated with Claude Code