← all checks
warning //xsl:variable[not(@select) and not(@_select) and (count(*) = 1) and (count(xsl:value-of) = 1) and not(text()[xslint:normalize-space(.) or ancestor::*[@xml:space][1]/@xml:space = 'preserve'])]

Setting value of variable incorrectly

A variable whose body is one xsl:value-of builds a result tree fragment to hold a value it has already computed: the instruction evaluates its select, turns the result into a string, and writes that string into a fragment the variable then names. The select attribute binds the value itself, without the fragment. It is shorter to read and cheaper to run.

Incorrect:

<xsl:variable name="title">
  <xsl:value-of select="heading"/>
</xsl:variable>

Correct:

<xsl:variable name="title" select="heading"/>

The two forms are close in intent and not equal in type, so the change is one to make deliberately. The body binds a fragment whose string value is the first heading; select binds the heading nodes themselves. Where the variable is only ever read as a string the two agree, and where it is not they part: count($title) counts one fragment against however many headings there are, $title/@lang reaches an attribute the fragment does not carry, and node identity is the fragment's rather than the document's. Read how the variable is used before rewriting it, and where the string is what is wanted, say so — select="string(heading)" states the conversion the body was doing silently.

The shorthand needs the xsl:value-of to be the whole of the body. Text beside it belongs to the fragment too, and select has nowhere to put it:

<xsl:variable name="title">Chapter: <xsl:value-of select="heading"/></xsl:variable>

There the value is the Chapter: text and the heading, which one attribute cannot express — use select="concat('Chapter: ', heading)" instead. Indentation is usually not content, XSLT stripping whitespace-only text from a stylesheet, and neither a comment nor a processing instruction reaches the fragment.

Usually, because xml:space decides it. Where the nearest ancestor declaring that attribute sets it to preserve, the whitespace is part of the fragment:

<xsl:variable name="title" xml:space="preserve">  <xsl:value-of select="heading"/></xsl:variable>

That binds two spaces and the heading, which select="heading" does not — select="concat(' ', heading)" is the shorthand there. A nearer xml:space="default" cancels a preserve higher up.

The shorthand also needs the variable to carry no select of its own. One holding both a select and a body offers a processor two candidate values and no rule for choosing between them, which XSLT forbids outright — 1.0 calls it an error, 2.0 and 3.0 raise XTSE0620, and the stylesheet compiles on no version:

<xsl:variable name="title" select="'Chapter'">
  <xsl:value-of select="heading"/>
</xsl:variable>

Neither form of the shorthand resolves that one, both values being spelled already. Drop whichever of the two says what was meant, and the variable binds one value again. XSLT 3.0's shadow spelling _select is the same attribute computed at compile time, so it stands in the way exactly as the plain one does.