Please review the following code:
~~~~~~~~~~~~~~
import java.io.BufferedReader;
import java.io.FileReader;
public class CheckSomething {
public static void withoutLinebreak() throws Exception {
try (BufferedReader reader = new BufferedReader(new FileReader(
"whatever"))) {
String sth = checkNotNull(reader.readLine(), "File Empty");
}
}
public static void witLinebreak() throws Exception {
try (BufferedReader reader = new BufferedReader(new FileReader(
"whatever"))) {
String sth = checkNotNull(reader.readLine(),
"File Empty");
}
}
public static <T> T checkNotNull(T object, String message) throws Exception {
if (object != null) {
return object;
}
throw new Exception(message);
}
}
~~~~~~~~~~~
Correctly, there are no bugs in withoutLinebreak(). However, findbugs (in eclipse, if that's important) finds the following bug:
[pathtofile]/CheckSomething.java:18 Dereference of the result of readLine() without nullcheck in CheckSomething.witLinebreak() [Of Concern(15), Normal confidence]
Note that it is vital that there is a line break in the source code after reader.readLine(), for the bug to be found. The only difference between withoutLinebreak() and witLinebreak() is that one line break in the source code. The generics in the checkNotNull method are also required for the bug to be found. If T is replaced with String, the bug disappears.
The xml output is the following:
~~~~~~~~~
<buginstance type="NP_DEREFERENCE_OF_READLINE_VALUE" priority="2" abbrev="NP" category="STYLE" first="1">
<class classname="CheckSomething">
<sourceline classname="CheckSomething" sourcefile="CheckSomething.java" sourcepath="CheckSomething.java">
</sourceline></class>
<method classname="CheckSomething" name="witLinebreak" signature="()V" isstatic="true">
<sourceline classname="CheckSomething" start="16" end="21" startbytecode="0" endbytecode="203" sourcefile="CheckSomething.java" sourcepath="CheckSomething.java">
</sourceline></method>
<localvariable name="?" register="-1" pc="27" role="LOCAL_VARIABLE_UNKNOWN">
<sourceline classname="CheckSomething" start="18" end="18" startbytecode="27" endbytecode="27" sourcefile="CheckSomething.java" sourcepath="CheckSomething.java">
<sourceline classname="CheckSomething" start="18" end="18" startbytecode="27" endbytecode="27" sourcefile="CheckSomething.java" sourcepath="CheckSomething.java">
</sourceline></sourceline></localvariable></buginstance>
~~~~~~~~~~`
Note that this is not an exotic scenario, since the Preconditions.checkNotNull() method in guava produces the same bug.
I also have this issue, but it only appears for me if the first argument of checkNotNull is a function return argument. If it's just a member variable, I don't have any bug, with or without line-breaks (e.g. if I just say checkNotNull(student.age, "....") it's ok)