Static Analysis with PMD
How it Works: Rule Coding
So far we've used JavaCC and JJTree to use an EBNF grammar to turn our
source code into an structured object hierarchy. Now we can put those objects
to use by writing some rules to look for problems.
Generally, a PMD rule is a Visitor that traverses the AST
looking for a particular pattern of objects that indicates a problem. This can be as simple as checking for occurrences of new Thread,
or as complex as determining whether or not a class is correctly overriding
both equals and hashcode.
Here's a simple PMD rule that checks for empty if
statements:
// Extend AbstractRule to enable the Visitor pattern
public class EmptyIfStmtRule extends AbstractRule implements Rule {
// This method gets called when there's a Block in the source code
public Object visit(ASTBlock node, Object data){
// If the parent node is an If statement and there isn't anything
// inside the block
if ((node.jjtGetParent().jjtGetParent() instanceof ASTIfStatement)
&& node.jjtGetNumChildren()==0) {
// then there's a problem, so add a RuleViolation to the Report
RuleContext ctx = (RuleContext)data;
ctx.getReport().addRuleViolation(createRuleViolation(ctx,
node.getBeginLine()));
}
// Now move on to the next node in the tree
return super.visit(node, data);
}
}
The code can be a bit obscure, but the concepts are, for the most part,
straightforward:
- Extend the
AbstractRule base class.
- Put in a "hook" so you'll get a callback when a node you are interested in
is encountered. In the example above, we want to be notified for each
ASTBlock, so we declared the method visit(ASTBlock node,
Object data).
- Once we get a callback, poke around to see if we find the problem for which we're
looking. In the example, we are looking for
if statements
with empty bodies, so we look up the tree to ensure we are in an
ASTIfStatement, and then down the tree to see if there are any
child nodes.
- Note that we can do this another way--we can register for a callback when
we hit an
ASTIfStatement and then look down the tree to check for
an empty block. Which way you do this is up to you; if you run into
performance problems, consider rewriting your rule.
How it Works: Rule Configuration
Once you've written a rule, you need to put it inside a PMD ruleset--which
is, naturally, a set of rules. A PMD ruleset is defined in an XML file, and
looks like this:
<rule name="EmptyIfStmt"
message="Avoid empty 'if' statements"
class="net.sourceforge.pmd.rules.EmptyIfStmtRule">
<description>
Empty If Statement finds instances where a condition is checked but
nothing is done about it.
</description>
<priority>3</priority>
<example>
<![CDATA[
if (absValue < 1) {
// not good
}
</XMLCDATA>
</example>
</rule>
As you can see, the rule configuration file contains a lot of information.
This is primarily so that an Integrated Development Environment (IDE) can show
a complete description of your rule--code samples, description, etc.
To run the new rule, put both the ruleset XML file and the code in your
CLASSPATH and run PMD to see the results.
The best way to learn how to write a custom rule is to find a source code
problem you want to catch, look at some of the many rules that come with PMD to
see how it's done, and then experiment with your own rule. Be sure to post to
the PMD forums if you have problems; your feedback can help us improve the
documentation and examples so that others will find PMD easier to use.
Flotsam and Jetsam
PMD has some nifty features that, although not germane to PMD, deserve a
mention. For example:
- The PMD web site is generated by Jakarta's Maven project, which puts
together a nicely-linked set of web pages including cross-referenced source
code, Javadocs, a team member listing, and a change log.
- We use JUnit to test PMD--currently, we've got over 400 JUnit tests. These
provide a comfortable safety net for use in day-to-day PMD development.
- We've set up a "PMD Scoreboard" to which SourceForge users can add projects
to be checked for unused code several times a day. Over sixty projects have
been added to the list, and the colors are quite lovely. The same report is
also run for Jakarta projects on a different site.
Conclusion
We've discussed static code analysis and how problems can be found without
needing to compile and run the code. We've had a quick tour of EBNF grammars
and JavaCC, as well as a brief discussion of an Abstract Syntax Tree. We've
seen how PMD uses all of that to check source code, and we've seen how to write a
custom rule to implement rules specific to your project. There's much more
information at the PMD web site; give it a try!
Credits
Thanks to David Dixon-Peugh and Dave Craine for proofreading this article.
Thanks also to the many contributors without whom PMD would be a much less
useful utility.
References
- PMD home page
- PMD Scoreboard
(SourceForge)
- PMD Scoreboard
(Jakarta)
- "How to write a
PMD rule"
- DARPA home page
- Cougaar home page
- JUnit home page
- Maven home page
- JavaCC/JJTree home page
Tom Copeland
started programming on a TRS-80 Model III, but demand for that skill has waned and he now programs mostly in Java and Ruby.
Return to ONJava.com.