Skip to content

Commit 720af6c

Browse files
authored
Fix RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE in try-with-resources in presence of debug symbols (spotbugs#1932)
* Fix RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE in try-with-resources in presence of debug symbols Resolves spotbugs#1931 by improving on spotbugs#1575 Probably also spotbugs#868, spotbugs#1338, spotbugs#1694 Not entirely sure about what exactly is happening but since 4.4.0 on javac11+ (openjdk) findbugs was reporting redundant null check if all of following holds - code in try block always dereferences the object - try-with-resources is applied to an object via interface reference - debug symbols are present (LocalVariableTable, via javac -g) javac11+ generates bytecode along the lines of ``` try-body if (obj != null) obj.close() on_any_exceptions_in_try_body: if (obj != null) obj.close() ``` This does always close obj if it is not null Javac could've deduced that if try-body always throws on `obj==null` then null check isn't needed but isn't obliged to do so, for simplicity for example. Fix by treating invokeinterface same way as invokevirtual in FindNullDeref Adding a minimal test for interface case. Note that test suite is compiled with default gradle settings that have debug symbols enabled. * Add changelog entry * Update Issue600Test assertions to catch the issue with interfaces * fix indentation
1 parent 4ec2a2a commit 720af6c

File tree

5 files changed

+35
-1
lines changed

5 files changed

+35
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ This is the changelog for SpotBugs. This follows [Keep a Changelog v1.0.0](http:
55
Currently the versioning policy of this project follows [Semantic Versioning v2.0.0](http://semver.org/spec/v2.0.0.html).
66

77
## Unreleased - 2022-??-??
8+
### Fixed
9+
- Fixed False positives for `RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE` on try-with-resources with interface references ([#1931](https://github.com/spotbugs/spotbugs/issues/1931))
810

911
## 4.7.0 - 2022-04-14
1012
### Changed

spotbugs-tests/src/test/java/edu/umd/cs/findbugs/nullness/Issue600Test.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,20 @@ public void testExplicitNulls() {
3737

3838
private void assertBugCount(String clazz, int count) {
3939
BugCollection bugCollection = analyse(clazz);
40+
4041
final BugInstanceMatcher bugTypeMatcher = new BugInstanceMatcherBuilder()
4142
.bugType("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE").build();
4243
assertThat(bugCollection, containsExactly(count, bugTypeMatcher));
44+
45+
final BugInstanceMatcher bugTypeMatcher2 = new BugInstanceMatcherBuilder()
46+
.bugType("RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE").build();
47+
assertThat(bugCollection, containsExactly(0, bugTypeMatcher2));
4348
}
4449

4550
private BugCollection analyse(String clazz) {
4651
return spotbugs.performAnalysis(
4752
Paths.get("../spotbugsTestCases/build/classes/java/main/ghIssues/issue600/" + clazz + ".class"),
48-
Paths.get("../spotbugsTestCases/build/classes/java/main/ghIssues/issue600/MyAutocloseable.class"));
53+
Paths.get("../spotbugsTestCases/build/classes/java/main/ghIssues/issue600/MyAutocloseable.class"),
54+
Paths.get("../spotbugsTestCases/build/classes/java/main/ghIssues/issue600/IMyAutocloseable.class"));
4955
}
5056
}

spotbugs/src/main/java/edu/umd/cs/findbugs/detect/FindNullDeref.java

+16
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.apache.bcel.generic.IFNONNULL;
5050
import org.apache.bcel.generic.IFNULL;
5151
import org.apache.bcel.generic.INVOKEDYNAMIC;
52+
import org.apache.bcel.generic.INVOKEINTERFACE;
5253
import org.apache.bcel.generic.INVOKEVIRTUAL;
5354
import org.apache.bcel.generic.Instruction;
5455
import org.apache.bcel.generic.InstructionHandle;
@@ -1919,6 +1920,21 @@ private boolean isGeneratedCodeInCatchBlockViaLineNumber(@NonNull ConstantPool c
19191920
}
19201921
}
19211922
break;
1923+
case Const.INVOKEINTERFACE:
1924+
if (handle.getInstruction() instanceof INVOKEINTERFACE) {
1925+
String methodName = ((INVOKEINTERFACE) handle.getInstruction()).getMethodName(cpg);
1926+
if ("close".equals(methodName)) {
1927+
// the close methods get the line number of the end of the try block assigned
1928+
if (throwables.stream().anyMatch(x -> lineNumberTable.getSourceLine(x.getEndPC()) == currentLine)) {
1929+
closeCounter++;
1930+
}
1931+
} else if ("addSuppressed".equals(methodName)) {
1932+
if (relevantLineNumbers.contains(currentLine)) {
1933+
addSuppressedPresent = true;
1934+
}
1935+
}
1936+
}
1937+
break;
19221938
case Const.ATHROW:
19231939
if (relevantLineNumbers.contains(currentLine) && handle.getInstruction() instanceof ATHROW) {
19241940
Class<?>[] exceptions = ((ATHROW) handle.getInstruction()).getExceptions();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package ghIssues.issue600;
2+
3+
public interface IMyAutocloseable extends AutoCloseable {
4+
}

spotbugsTestCases/src/java/ghIssues/issue600/TryWithResourcesSimple.java

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package ghIssues.issue600;
22

33
public class TryWithResourcesSimple {
4+
public int nocatchInterface(IMyAutocloseable x) throws Exception {
5+
try (IMyAutocloseable closeable = x) {
6+
return closeable.hashCode();
7+
}
8+
}
9+
410
public void nocatch() throws Exception {
511
try (MyAutocloseable closeable = MyAutocloseable.create()) {
612
closeable.exampleMethod();

0 commit comments

Comments
 (0)