For static analysis tools I often use CPD, PMD, FindBugs, and Checkstyle.
CPD is the PMD "Copy/Paste Detector" tool. I was using PMD for a little while before I noticed the "Finding Duplicated Code" link on the PMD web page.
I'd like to point out that these tools can sometimes be extended beyond their "out-of-the-box" set of rules. And not just because they're open source so that you can rewrite them. Some of these tools come with applications or "hooks" that allow them to be extended. For example, PMD comes with the "designer" tool that allows you to create new rules. Also, Checkstyle has the DescendantToken check that has properties that allow for substantial customization.
I integrate these tools with an Ant-based build. You can follow the link to see my commented configuration.
In addition to the simple integration into the build, I find it helpful to configure the tools to be somewhat "integrated" in a couple of other ways. Namely, report generation and warning suppression uniformity. I'd like to add these aspects to this discussion (which should probably have the "static-analysis" tag also): how are folks configuring these tools to create a "unified" solution? (I've asked this question separately here)
First, for warning reports, I transform the output so that each warning has the simple format:
/absolute-path/filename:line-number:column-number: warning(tool-name): message
This is often called the "Emacs format," but even if you aren't using Emacs, it's a reasonable format for homogenizing reports. For example:
/project/src/com/example/Foo.java:425:9: warning(Checkstyle):Missing a Javadoc comment.
My warning format transformations are done by my Ant script with Ant filterchains.
The second "integration" that I do is for warning suppression. By default, each tool supports comments or an annotation (or both) that you can place in your code to silence a warning that you want to ignore. But these various warning suppression requests do not have a consistent look which seems somewhat silly. When you're suppressing a warning, you're suppressing a warning, so why not always write "SuppressWarning
?"
For example, PMD's default configuration suppresses warning generation on lines of code with the string "NOPMD
" in a comment. Also, PMD supports Java's @SuppressWarnings
annotation. I configure PMD to use comments containing "SuppressWarning(PMD.
" instead of NOPMD
so that PMD suppressions look alike. I fill in the particular rule that is violated when using the comment style suppression:
// SuppressWarnings(PMD.PreserveStackTrace) justification: (false positive) exceptions are chained
Only the "SuppressWarnings(PMD.
" part is significant for a comment, but it is consistent with PMD's support for the @SuppressWarning
annotation which does recognize individual rule violations by name:
@SuppressWarnings("PMD.CompareObjectsWithEquals") // justification: identity comparision intended
Similarly, Checkstyle suppresses warning generation between pairs of comments (no annotation support is provided). By default, comments to turn Checkstyle off and on contain the strings CHECKSTYLE:OFF
and CHECKSTYLE:ON
, respectively. Changing this configuration (with Checkstyle's "SuppressionCommentFilter") to use the strings "BEGIN SuppressWarnings(CheckStyle.
" and "END SuppressWarnings(CheckStyle.
" makes the controls look more like PMD:
// BEGIN SuppressWarnings(Checkstyle.HiddenField) justification: "Effective Java," 2nd ed., Bloch, Item 2
// END SuppressWarnings(Checkstyle.HiddenField)
With Checkstyle comments, the particular check violation (HiddenField
) is significant because each check has its own "BEGIN/END
" comment pair.
FindBugs also supports warning generation suppression with a @SuppressWarnings
annotation, so no further configuration is required to achieve some level of uniformity with other tools. Unfortunately, Findbugs has to support a custom @SuppressWarnings
annotation because the built-in Java @SuppressWarnings
annotation has a SOURCE
retention policy which is not strong enough to retain the annotation in the class file where FindBugs needs it. I fully qualify FindBugs warnings suppressions to avoid clashing with Java's @SuppressWarnings
annotation:
@edu.umd.cs.findbugs.annotations.SuppressWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR")
These techniques makes things look reasonably consistent across tools. Note that having each warning suppression contain the string "SuppressWarnings
" makes it easy to run a simple search to find all instances for all tools over an entire code base.