Files
JIMRI/help/en/html/doc/Technical/SpotBugs.shtml
T
2026-06-17 14:00:51 +02:00

487 lines
20 KiB
Plaintext

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content="HTML Tidy for HTML5 for Apple macOS version 5.8.0">
<title>JMRI: Static Analysis with SpotBugs</title>
<meta name="author" content="Bob Jacobsen">
<meta name="keywords" content="JMRI technical code bugs"><!--#include virtual="/help/en/parts/Style.shtml" -->
</head>
<body>
<!--#include virtual="/help/en/parts/Header.shtml" -->
<div id="mBody">
<!--#include virtual="Sidebar.shtml" -->
<div id="mainContent">
<h1>JMRI Code: Static Analysis with SpotBugs</h1>
SpotBugs is a tool that can examine code to find possible problems. It does a "static
analysis", looking through the code to find certain "known bad ideas": Things that are likely
to cause occasional/intermittent problems, poor performance, etc. Because those kinds of
problems are hard to find with tests, finding them by inspection is often the only realistic
approach. Having a tool that can do those inspections automatically, so that they can be done
every time something is changed, keeps the code from slowly getting worse and worse without
anybody noticing until it's too late. See the <a href=
"https://builds.jmri.org/jenkins/job/development/job/spotbugs/">spotbugs history page.</a>
<p>For more information on SpotBugs, see <a href="https://spotbugs.github.io">the SpotBugs
home page</a>.</p>
<p>We routinely run SpotBugs as part of our <a href="ContinuousIntegration.shtml">continuous
integration process</a>. You can see the <a href=
"https://builds.jmri.org/jenkins/job/development/job/spotbugs/lastBuild/spotbugs/">detailed
results of the most recent (daily) run</a> along with <a href=
"https://builds.jmri.org/jenkins/job/development/job/spotbugs/">recent progress trend</a>
online.</p>
<p>If SpotBugs is finding a false positive, a bug that's not really a bug, you can turn it
off with an annotation such as:</p>
<pre style="font-family: monospace;">
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", justification = "Even tiny differences should trigger update")
</pre>
<p>Although Java itself considers it optional, we require the second "justification" argument.
Explaining why you've added this annotation to suppress a message will help whoever comes after you
and is trying to understand the code.
It will also help make sure you properly understand the cause of the underlying bug report:
Sometimes what seems a false positive really isn't.</p>
<p>Annotations without a justification clause will periodically be removed.</p>
<p>Note that the <code>@SuppressFBWarnings</code> contents in this form should be all on one line
so that automated scanners can more reliably see it.</p>
<p>If you need to put more than one message type in an annotation, use array syntax:</p>
<pre style="font-family: monospace;">
@SuppressFBWarnings(value = {"type1", "type2"}, justification = "Why both are needed")
</pre>
<p>There are also Java and SpotBugs annotations that can help it better understand your code.
Sometimes they'll give it enough understanding of e.g. when a variable can be null, that
it'll no longer make false-positive mistakes. For more on this, see the <a href=
"https://docs.oracle.com/javase/tutorial/java/annotations/">Java annotations</a> and <a href=
"https://findbugs.sourceforge.net/manual/annotations.html">SpotBugs annotation pages</a>.</p>
<p>The basics of annotations are covered in a <a href=
"https://docs.oracle.com/javase/tutorial/java/annotations/">Java annotations
tutorial</a>.</p>
<p id="annotations">It can be useful to mark code with one of the following annotations so
that SpotBugs does a good job of reasoning about it:</p>
<ul>
<li>
<a href=
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.1/javax/annotation/Nonnull.html">
<code>javax.annotation.Nonnull</code></a> - The annotated element must not be null.
Annotated fields must not be null after construction has completed. Annotated methods
must have non-null return values. Use <a href=
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.1/javax/annotation/ParametersAreNonnullByDefault.html">
javax.annotation.ParametersAreNonnullByDefault</a> on the class declaration (the start of
the class) to set @Nonnull for an entire class. Note that SpotBugs won't let you compare
a @Nonnull value to null; that's an error that SpotBugs wants to find via static
analysis. If you are annotating a part of the external API, and you want to double-check at
runtime, use <a href=
"https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#requireNonNull-T-">java.util.Objects.requireNonNull(..)</a>,
for example:
<pre style="font-family: monospace;">
public Car(@Nonnull Engine engine, @Nonnull Transmission transmission) {
this.engine = java.util.Objects.requireNonNull(engine, "Engine cannot be null");
this.transmission = java.util.Objects.requireNonNull(transmission, "Transmission cannot be null");
}
</pre>
</li>
<li>
<a href=
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.1/javax/annotation/CheckForNull.html">
<code>javax.annotation.CheckForNull</code></a> - the annotated variable, parameter or
return value may have a null value, so all uses should appropriately handle that. Please
put this on method definitions to say that the return value should be checked for null.
</li>
<li>
<a href=
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.1/javax/annotation/OverridingMethodsMustInvokeSuper.html">
<code>javax.annotation.OverridingMethodsMustInvokeSuper</code></a> - Used to annotate a
method that, if overridden, must (or should) invoke super in the overriding method.
Examples of such methods include <code>finalize()</code> and <code>clone()</code>.
<p>Note this replaces the deprecated <a href=
"http://findbugs.sourceforge.net/manual/annotations.html"><code>edu.umd.cs.findbugs.annotations.OverrideMustInvoke</code></a>.</p>
</li>
<li>
<a href=
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.1/javax/annotation/CheckReturnValue.html">
<code>javax.annotation.CheckReturnValue</code></a> - annotates a method to say the method
has no side effects, so there is no point in calling it without checking its return value.
</li>
<li>
<a href=
"https://jcip.net/annotations/doc/net/jcip/annotations/Immutable.html"><code>net.jcip.annotations.Immutable</code></a>
- An object of this class can't be changed after it was created. This allows both better
checking of logic, and also simplifies use by your colleagues, so it's good to create
classes that have this property and then annotate them.
</li>
<li>
<a href=
"https://jcip.net/annotations/doc/net/jcip/annotations/NotThreadSafe.html"><code>net.jcip.annotations.NotThreadSafe</code></a>
- a class that isn't thread-safe, so shouldn't be checked for concurrency issues. Often
used for Swing-based classes, but note that some Swing components (e.g. monitor windows,
classes with listeners) do have to accept input from other threads.
</li>
<li>
<a href=
"https://jcip.net/annotations/doc/net/jcip/annotations/ThreadSafe.html"><code>net.jcip.annotations.ThreadSafe</code></a>
- classes that do have to be usable for multiple threads. SpotBugs generally assumes
this, but it's good to put it on a class that's intended to be thread-safe as a reminder
to future developers.
</li>
<li>
<a href=
"https://jcip.net/annotations/doc/net/jcip/annotations/GuardedBy.html"><code>net.jcip.annotations.GuardedBy</code></a>
- "The field or method to which this annotation is applied can only be accessed when
holding a particular lock, which may be a built-in (synchronization) lock, or may be an
explicit java.util.concurrent.Lock."
<p>Your code should ensure that the synchronization is done around references to the
annotated field or method. SpotBugs will (sometimes) check that.</p>
</li>
</ul>
For more information about these annotations, please see links above and
<ul>
<li>The JSR-305 <a href="https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.1/index.html">
code annotations page</a>,
</li>
<li>the <a href="https://jcip.net/annotations/doc/index.html">Concurrency API annotations
page</a>, and
</li>
<li>for some older information, the <a href=
"http://findbugs.sourceforge.net/manual/annotations.html">SpotBugs annotation page</a>.
</li>
</ul>
<p>We do not use these annotations:</p>
<ul>
<li>
<a id="nullable"></a> <a href=
"http://findbugs.sourceforge.net/manual/annotations.html"><code>javax.annotation.Nullable</code></a>
- This doesn't really mean what people think it does, as SpotBugs doesn't really check
anything when this is used. From the SpotBugs documentation:
<blockquote>
SpotBugs will treat the annotated items as though they had no annotation. In practice
this annotation is useful only for overriding an overarching NonNull annotation. Use
javax.annotation.ParametersAreNullableByDefault to set it for an entire class. Prefer
the use of <code>CheckForNull</code>.
</blockquote>
</li>
</ul>
<h3>Running SpotBugs</h3>
SpotBugs is automatically run by <a href="ContinuousIntegration.shtml#travis">Travis CI</a>
as part of the <a href="gitdeveloper.shtml">Pull Request</a> process, and by <a href=
"ContinuousIntegration.shtml#jenkins">Jenkins</a> as part of the <a href=
"https://builds.jmri.org/jenkins/job/development/job/spotbugs/lastBuild/">daily
builds</a>. If new errors appear during CI, the job will (usually) fail. You can look at the
Jenkins output to see where there are existing errors to work on.
There's a very nice browser
that lets you select items by type, by file, by package, etc,
available by clicking on the "SpotBugs Warnings" button on the left of the
<a href="https://builds.jmri.org/jenkins/job/development/job/spotbugs/">Jenkins SpotBugs job</a>.
<p>To run it locally:</p>
<ul>
<li>Install a local copy of SpotBugs. There are multiple ways to do this, see the
<a href="https://spotbugs.github.io">SpotBugs website</a>.
</li>
<li>You have to tell the JMRI build systems where that installation is. You can do that
with various environment variables etc. but the most straight-forward is to add a line
like the following to your <a href="StartUpScripts.shtml">local.properties</a> file:
<pre style="font-family: monospace;">
spotbugs.home = /Users/jake/.spotbugs/spotbugs-4.2.3
</pre>
</li>
<li>In order to check for spotbugs issues in logging statements, we use the <a href=
"https://www.kengo-toda.jp/findbugs-slf4j/">Findbugs-slf4j</a> plugin by KengoTODA. To use
this plugin in ant builds, download the plugin and install it in the plugin directory of
your Spotbugs installation.
</li>
<li>Then run SpotBugs in ant via:
<pre style="font-family: monospace;">
ant spotbugs
</pre>
or for the test code
<pre style="font-family: monospace;">
ant tests-spotbugs
</pre>
which will create a <code>spotbugs.html</code> or <code>tests-spotbugs.html</code> file in the JMRI base directory that you can open in a browser.
</li>
</ul>
The <code>ant spotbugs-ci</code> target is similar, except it creates the output as a
<code>spotbugs.xml</code> XML file that Jenkins uses to track which items have been fixed,
etc.
<p>The <a href="https://github.com/JMRI/JMRI/blob/master/.spotbugs.xml">.spotbugs.xml</a>
file specifies which items in SpotBugs reports are suppressed for the JMRI project's <a href=
"https://builds.jmri.org/jenkins/job/development/job/spotbugs/lastBuild/">usual
reporting via Jenkins</a>. The <a href=
"https://github.com/JMRI/JMRI/blob/master/.spotbugs-check.xml">.spotbugs-check.xml</a> file
is a super-set which identifies which are significant enough to fail CI. (This is specified
two places in the <code>pom.xml</code>, which is used by the <code>scripts/travis.sh</code>
to control Maven running, which in turn is used by the <code>.travis.yml</code> file as the
Travis run-time script)</p>
<h3>Background</h3>
<p>Simon White added FindBugs support to our Ant-based build chain during the development of
JMRI 2.5.5.</p>
<h4>Suppressed Warnings</h4>
We have turned off the routine SpotBugs checking of certain kinds of conditions:
<dl>
<dt>
<a href=
"https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#ri-class-implements-same-interface-as-superclass-ri-redundant-interfaces">
RI_REDUNDANT_INTERFACES</a>
</dt>
<dd>This flags cases where a class implements an interface, and also inherits from a
superclass that already implements that interface. This is redundant and untidy, but it
can't cause the code to malfunction. We have enough of them that we've turned off the
warning, and will come back to it some later time.</dd>
<dt>
<a href=
"https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#sic-could-be-refactored-into-a-named-static-inner-class-sic-inner-should-be-static-anon">
SIC_INNER_SHOULD_BE_STATIC_ANON</a>, <a href=
"https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#sic-could-be-refactored-into-a-static-inner-class-sic-inner-should-be-static-needs-this">
SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS</a>
</dt>
<dd>Static, as opposed to non-static, inner classes (classes defined within another class)
take less space because they don't maintain references to the containing object. This
warning suggests moving an anonymous (defined in-line to the code) inner class to a regular
(defined not in-line) class, so it can be made static. Although probably a small
improvement, it's a bit of work for a small improvement. We have enough of them that we've
turned off the warning, and we'll come back to it later.</dd>
<dt>
<a href=
"https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#dm-consider-using-locale-parameterized-version-of-invoked-method-dm-convert-case">
DM_CONVERT_CASE</a>, <a href=
"https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html#dm-reliance-on-default-encoding-dm-default-encoding">
DM_DEFAULT_ENCODING</a>,
</dt>
<dd>These cover aspects of handling characters in non-Western languages. Most of our String
handling doesn't have anything to do with this, so it would be a huge number of annotations
for only a small amount of I18N gain. Perhaps we'll revisit these later.</dd>
<dt>Malicious Code</dt>
<dd>This is a class of warnings centered around the idea that data and code methods
shouldn't be too visible, especially when static. This is true, but JMRI isn't a hardened
library that's being released into a world of people trying to break it, as these changes
are a lower priority.</dd>
<dt>Se,SvVI</dt>
<dd>This is a large class of warnings associated with Java serialization. JMRI doesn't use
serialization, and is unlikely to do so in the future, so we suppress these to raise the
average quality of the issued warnings.</dd>
<dt>SLF4J_LOGGER_SHOULD_BE_NON_STATIC</dt>
<dd>This warning indicates that loggers should be non-static variables, but the JMRI
standard is for the logger to be a static final variable, so every logger would be flagged
if this were enabled.</dd>
</dl>
<h4>Status and Counts</h4>
<p>We've been making slow but continuous progress on removing these, although new Bug patterns are constantly being added to Spotbugs:</p>
<h4>Main JMRI Codebase</h4>
<table border="1">
<tr>
<th>Category</th>
<th>JMRI 2.5.4</th>
<th>JMRI 4.13.3</th>
<th>JMRI 4.17.3</th>
<th>JMRI 5.5.3</th>
</tr>
<tr>
<td>Bad practice Warnings</td>
<td>164</td>
<td>13</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>Correctness Warnings</td>
<td>77</td>
<td></td>
<td>25</td>
<td>25</td>
</tr>
<tr>
<td>Experimental Warnings</td>
<td>7</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Malicious code vulnerability Warnings</td>
<td>221</td>
<td>(disabled)</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Multithreaded correctness Warnings</td>
<td>90</td>
<td>175</td>
<td>165</td>
<td>198</td>
</tr>
<tr>
<td>Performance Warnings</td>
<td>459</td>
<td>15</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>Style / Dodgy Code Warnings</td>
<td>304</td>
<td>406</td>
<td>127</td>
<td>295</td>
</tr>
<tr>
<th>Total</th>
<th>1322</th>
<th>636</th>
<th>293</th>
<th>518</th>
</tr>
<tr>
<th>Medium Priority</th>
<th></th>
<th>199</th>
<th>79</th>
<th>427</th>
</tr>
<tr>
<th>Low Priority</th>
<th></th>
<th>437</th>
<th>214</th>
<th>128</th>
</tr>
<tr>
<th>@SuppressFBWarnings<br> Lines</th>
<th></th>
<th></th>
<th>868</th>
<th>873</th>
</tr>
</table>
<h4>JMRI Test Code</h4>
<table border="1">
<tr><th></th>
<th>JMRI 5.1.1<br>SB 4.7.0</th>
<th>JMRI 5.5.3<br>SB 4.7.3</th>
</tr>
<tr><td>High Priority Total</td>
<td>251</td>
<td>4</td>
</tr>
<tr><td>Medium Priority Total</td>
<td>4692</td>
<td>328</td>
</tr>
<tr><td>Low Priority Total</td>
<td>988</td>
<td>371</td>
</tr>
<tr><td><strong>Total Warnings</strong></td>
<td><strong>5931</strong></td>
<td><strong>703</strong></td>
</tr>
<tr><td></td><td></td><td></td></tr>
<tr><td>High Priority Density</td>
<td>0.95</td>
<td>0.01</td>
</tr>
<tr><td>Medium Priority Density</td>
<td>17.77</td>
<td>1.19</td>
</tr>
<tr><td>Low Priority Density</td>
<td>3.74</td>
<td>1.35</td>
</tr>
<tr><td><strong>Total Density</strong></td>
<td><strong>22.46</strong></td>
<td><strong>2.55</strong></td>
</tr>
</table>
<p>Density - Defects per Thousand lines of non-commenting source statements.</p>
<p>A lot of work has gone into JMRI cycle to bring the bug count down. The <a href=
"ContinuousIntegration.shtml">Continuous Integration server</a> runs SpotBugs twice a day,
which helps developers see the results of their coding without having to set up to run
SpotBugs themselves.</p>
<p>If you are checking code in to the JMRI repository, please check the CI server and make
sure that your changes do not introduce new errors.</p>
<!--#include virtual="/help/en/parts/Footer.shtml" -->
</div>
<!-- closes #mainContent-->
</div>
<!-- closes #mBody-->
<script src="/js/help.js"></script>
</body>
</html>