--fix rewrites this one: the correction is
deterministic, and leaves the stylesheet meaning what it meant.
count($x) > 0 and count($x) = 0 ask the processor to walk the whole
sequence and tally it, only to find out whether it holds anything. The question
is existence, and every version of XPath can state it directly — and let the
engine stop at the first item instead of counting every one.
Incorrect:
<xsl:if test="count($items) > 0">
<xsl:if test="count($items) = 0">
Correct, on XSLT 2.0 and later:
<xsl:if test="exists($items)">
<xsl:if test="empty($items)">
Correct, on XSLT 1.0 (where exists()/empty() do not exist):
<xsl:if test="$items">
<xsl:if test="not($items)">
A node-set in a boolean context is already true exactly when it is non-empty, so
in an xsl:if/xsl:when @test the bare $items says it; in a @select (or
any value context) write boolean($items), and for the empty case not($items)
either way.
The --fix picks the form the stylesheet's version can run: exists()/empty()
on 2.0/3.0, and boolean()/not() — or the bare node-set in a whole @test —
on 1.0. The 1.0 forms are valid in every version, so an unversioned stylesheet
gets them too.
The call is the standard fn:count however its namespace is spelled: bare, as
the default function namespace; behind a prefix bound to that namespace, which is
the idiomatic fn:count($items) = 0 of a 2.0 stylesheet; or with the namespace
written inline. A function of your own that happens to be called count is
another function and is left alone, and so is a call spelling no argument or
several, fn:count taking exactly one.
Nor does the operator's spelling. XSLT 2.0 brought the value comparisons, which
ask the same six questions in words, so count($items) eq 0,
count($items) ne 0 and 0 lt count($items) are the existence tests = 0,
!= 0 and 0 < are, and collapse to the same exists()/empty() — the
direct form is a call and carries no operator, so nothing of the original
spelling is lost in dropping it.
The operand order does not matter: 0 < count($items) and
0 = count($items) are flagged the same way. A comparison that is not an
existence test — count($x) > 1, count($x) = 5 — is a genuine count and is
left alone. So is a 0 or 1 that is only part of a wider operand: in
$max + 1 > count($x) the left side is $max + 1, and in
count($x) > 0 + $n the right side is 0 + $n, so neither compares the call
against a bare 0 or 1.
The comparison is caught in the XPath and pattern attributes of XSLT elements and
inside an attribute value template, where <div empty="{count($items) = 0}"/>
holds a real expression; both a comparison and its rewrite print true/false
there, so the fix stays safe. An attribute of your output vocabulary that happens
to be called test or select carries text for the result tree, not XPath, and
is never touched.