This page describes some points of XPath rule support in more details. See also the tutorial about how to write an XPath rule.
PMD supports three XPath versions for now: 1.0, 2.0, and 1.0 compatibility mode.
The version can be specified with the
version property in the rule definition, like so:
<property version="2.0" /> <!-- or "1.0", or "1.0 compatibility" -->
The default has always been version 1.0.
As of PMD version 6.22.0, XPath versions 1.0 and the 1.0 compatibility mode are deprecated. XPath 2.0 is superior in many ways, for example for its support for type checking, sequence values, or quantified expressions. For a detailed but approachable review of the features of XPath 2.0 and above, see the Saxon documentation.
It is recommended that you migrate to 2.0 before 7.0.0, but we expect to be able to provide an automatic migration tool when releasing 7.0.0. See the migration guide below.
DOM representation of ASTs
XPath rules view the AST as an XML-like DOM, which is what the XPath language is defined on. Concretely, this means:
- Every AST node is viewed as an XML element
- The element has for local name the value of
getXPathNodeNamefor the given node
- The element has for local name the value of
- Some Java getters are exposed as XML attributes on those elements
- This means, that documentation for attributes can be found in our Javadocs. For
example, the attribute
@SimpleNameof the Java node
EnumDeclarationis backed by the Java getter
- This means, that documentation for attributes can be found in our Javadocs. For example, the attribute
To represent attributes, we must map Java values to XPath Data Model (XDM) values. The conversion depends on the XPath version used.
On XPath 1.0 we map every Java value to an
xs:string value by using the
of the object. Since XPath 1.0 allows many implicit conversions this works, but it
causes some incompatibilities with XPath 2.0 (see the section about migration further
XPath 2.0 is a strongly typed language, and so we use more precise type annotations.
In the following table we refer to the type conversion function as
function from Java types to XDM types.
⚠️ List support is deprecated with 6.25.0. See below.
conv function is used to translate rule property values to XDM values.
List<E>has been deprecated with PMD 6.25.0 and will be removed completely with PMD 7. The reason is that newer Saxon versions don’t support sequences as attributes anymore. Lists are still possible in Java-based rules but not with XPath. Multivalued rule properties are still supported.
Migrating from 1.0 to 2.0
XPath 1.0 and 2.0 have some incompatibilities. The XPath 2.0 specification describes them precisely. Those are however mostly corner cases and XPath rules usually don’t feature any of them.
The incompatibilities that are most relevant to migrating your rules are not caused by the specification, but by the different engines we use to run XPath 1.0 and 2.0 queries. Here’s a list of known incompatibilities:
- The namespace prefixes
string:should not be mentioned explicitly. In XPath 2.0 mode, the engine will complain about an undeclared namespace, but the functions are in the default namespace. Removing the namespace prefixes fixes it.
- Conversely, calls to custom PMD functions like
typeIsmust be prefixed with the namespace of the declaring module (
- Boolean attribute values on our 1.0 engine are represented as the string values
"false". In 2.0 mode though, boolean values are truly represented as boolean values, which in XPath may only be obtained through the functions
false(). If your XPath 1.0 rule tests an attribute like
@Private="true", then it just needs to be changed to
@Private=true()when migrating. A type error will warn you that you must update the comparison. More is explained on issue #1244.
- In XPath 1.0, comparing a number to a string coerces the string to a number.
In XPath 2.0, a type error occurs. Like for boolean values, numeric values are
represented by our 1.0 implementation as strings, meaning that
@BeginLine > "1"worked —that’s not the case in 2.0 mode.
@ArgumentCount > '1'→
@ArgumentCount > 1
- In XPath 1.0, the expression
/Foomatches the children of the root named
Foo. In XPath 2.0, that expression matches the root, if it is named
Foo. Consider the following tree:
Foo └─ Foo └─ Foo
/Foowill match the root in XPath 2, and the other nodes (but not the root) in XPath 1. See eg an issue caused by this in Apex, with nested classes.
PMD extension functions
PMD provides some language-specific XPath functions to access semantic information from the AST.
On XPath 2.0, the namespace of custom PMD function must be explicitly mentioned.
Java functions are in the namespace
|Function name||Description (click for details)|
|typeIs||Tests a node's static type|
pmd-java:typeIs(xs:string) as xs:boolean
|typeIsExactly||Tests a node's static type, ignoring subtypes|
pmd-java:typeIsExactly(xs:string) as xs:boolean
|metric||Computes and returns the value of a metric|
typeOffunction which is deprecated and whose usages should be replaced with uses of
typeIsExactly. That one will be removed with PMD 7.0.0.