0
votes

The PMD source code analyzer allows to write rules as XPath expressions.

I'm working at cleaning up an Apex codebase where I find frequently the following mistake: !someCollection.isEmpty() && someCollection != null.

The correct approach would be to check for null first: someCollection != null && !someCollection.isEmpty().

Using my trusted editor I can use a RegEx to find these items: && [a-zA-Z0-9]* != null. Works like a charm. Now I try to create a custom PMD rule for it, but my XPath regex doesn't return any value in the PMD Designer:

<rule message="Apex code must check != null before .isEmpty()" name="NullValueCheckBeforeEmptyCheck" class="net.sourceforge.pmd.lang.rule.XPathRule">
    <description>Apex code must check != null before .isEmpty()</description>
    <priority>1</priority>
    <properties>
      <property name="xpath">
        <value>
            <![CDATA[
                //*[matches(@Image,'&& [a-zA-Z0-9]* != null')]
            ]]>
        </value>
      </property>
    </properties>
  </rule>

I tried to initial check //*[matches(@Image,'if')] but even that returned no result.

What do I miss?

Sample Apex that should trigger the rule:

global class caseShareBatch {

global void execute(List&lt;Case&gt; caseShareList){
     if(!caseShareList.isEmpty() &amp;&amp; caseShareList != null) {
            insert caseShareList;
     }
}
}
3

3 Answers

1
votes

Are you sure PMD supports XPath 2.0? From a quick glance at the documentation, it isn't obvious. Very often people who talk of XPath without a version number are referring to XPath 1.0, which of course has no matches() function.

1
votes

PMD uses an Apex parser to convert the source code to an abstract syntax tree (AST). Your XPath expressions are applied to the AST, not the source code. E.g. the AST for the if statement would look like this:

IfElseBlockStatement {
    IfBlockStatement {
        StandardCondition {
            BooleanExpression {
                PrefixExpression {
                    MethodCallExpression {
                        ReferenceExpression 
                    }
                }
                BooleanExpression {
                    VariableExpression 
                    LiteralExpression 
                }
            }
        }
        BlockStatement {
            DmlInsertStatement {
                VariableExpression 
            }
        }
    }
}

PMD has a graphical rule designer that helps alot when writing XPath rules.

PMD Rule Designer

For this AST the XPath //BooleanExpression/BooleanExpression[position() > 0 and VariableExpression and LiteralExpression] matches && caseShareList != null. But it would also match || i == 42 so you have to refine the query further. Unfortunately I didn't see anything that would let me distinguish between && and || but you might have better luck.

0
votes

Turns out, what I needed to do wasn't possible with PMD 6.10. The latest release 6.11.0 didn't expose the @Image property as I was hoping for, but added sufficient new properties in the boolean operator to form an XPath expression worthy of @michael-kay without the need to add Regex to the mix:

//BooleanExpression[@Op="&&"][
  child::*[2][
    self::BooleanExpression[@Op="!="][
      child::*[1][self::VariableExpression] and
      child::*[2][self::LiteralExpression[@LiteralType="NULL"]]
    ]
  ]
]

YMMV