Skip to content

Commit ee74b10

Browse files
gtoisonPatrikScully
authored andcommitted
Fix for false positive MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR when referencing but not calling an overridable method (spotbugs#2900)
* test: issue spotbugs#2837 reproducer * fix: do not consider a method reference assigned to a field * test: added false positive check for issue spotbugs#1867 It looks like the problem for spotbugs#1867 was fixed elsewhere * test: added a true positive test for a lambda called in constructor * doc: added changelog entry * test: fixed tests * fix: removed unused variable * test: added a 2nd overridable method for the true positive case Overridable methods are only reported once, add a 2nd one to make sure the unit test shows the problem when assigning a method reference to a field
1 parent 2ff7cda commit ee74b10

File tree

6 files changed

+103
-0
lines changed

6 files changed

+103
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Currently the versioning policy of this project follows [Semantic Versioning v2.
2828
- Improved the bug description for VA_FORMAT_STRING_USES_NEWLINE when using text blocks, check the usage of String.formatted() ([#2881](https://github.com/spotbugs/spotbugs/pull/2881))
2929
- Fixed crash in ValueRangeAnalysisFactory when looking for redundant conditions used in assertions [#2887](https://github.com/spotbugs/spotbugs/pull/2887))
3030
- Revert again commons-text from 1.11.0 to 1.10.0 to resolve a version conflict ([#2686](https://github.com/spotbugs/spotbugs/issues/2686))
31+
- Fixed false positive MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR when referencing but not calling an overridable method [#2837](https://github.com/spotbugs/spotbugs/pull/2837))
3132

3233
### Added
3334
- New detector `MultipleInstantiationsOfSingletons` and introduced new bug types:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package edu.umd.cs.findbugs.detect;
2+
3+
import edu.umd.cs.findbugs.AbstractIntegrationTest;
4+
import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcher;
5+
import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcherBuilder;
6+
import org.junit.jupiter.api.Test;
7+
8+
import static edu.umd.cs.findbugs.test.CountMatcher.containsExactly;
9+
import static org.hamcrest.MatcherAssert.assertThat;
10+
11+
12+
class Issue1867Test extends AbstractIntegrationTest {
13+
14+
@Test
15+
void testIssue() {
16+
performAnalysis("ghIssues/Issue1867.class");
17+
BugInstanceMatcher matcher = new BugInstanceMatcherBuilder()
18+
.bugType("MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR")
19+
.build();
20+
21+
assertThat(getBugCollection(), containsExactly(0, matcher));
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package edu.umd.cs.findbugs.detect;
2+
3+
import edu.umd.cs.findbugs.AbstractIntegrationTest;
4+
import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcher;
5+
import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcherBuilder;
6+
import org.junit.jupiter.api.Test;
7+
8+
import static edu.umd.cs.findbugs.test.CountMatcher.containsExactly;
9+
import static org.hamcrest.MatcherAssert.assertThat;
10+
11+
12+
class Issue2837Test extends AbstractIntegrationTest {
13+
14+
@Test
15+
void testIssue() {
16+
performAnalysis("ghIssues/Issue2837.class");
17+
18+
assertBugCount("MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", 1);
19+
assertBugAtLine("MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", 19);
20+
}
21+
22+
private void assertBugCount(String type, int expectedCount) {
23+
BugInstanceMatcher bugMatcher = new BugInstanceMatcherBuilder()
24+
.bugType(type)
25+
.build();
26+
27+
assertThat(getBugCollection(), containsExactly(expectedCount, bugMatcher));
28+
}
29+
30+
private void assertBugAtLine(String type, int line) {
31+
BugInstanceMatcher bugMatcher = new BugInstanceMatcherBuilder()
32+
.bugType(type)
33+
.atLine(line)
34+
.build();
35+
36+
assertThat(getBugCollection(), containsExactly(1, bugMatcher));
37+
}
38+
}

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

+6
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ public void sawOpcode(int seen) {
146146
}
147147
OpcodeStack.Item item = stack.getStackItem(0);
148148

149+
if (getNextOpcode() == Const.PUTFIELD) {
150+
// INVOKEDYNAMIC followed by PUTFIELD means that we assigned the method reference to a field
151+
// But we might be missing calls to that field afterwards, this would be a false negative
152+
return;
153+
}
154+
149155
if (item.getRegisterNumber() == 0 && Const.CONSTRUCTOR_NAME.equals(getMethodName())) {
150156
refCallerConstructors.put(constDyn.getBootstrapMethodAttrIndex(),
151157
new CallerInfo(getXMethod(), SourceLineAnnotation.fromVisitedInstruction(this)));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package ghIssues;
2+
3+
import java.util.function.Function;
4+
5+
public class Issue1867 {
6+
private final Function<String, String> function;
7+
8+
public Issue1867() {
9+
// MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR false positive: Claims "append" is overridable
10+
function = v -> v + "!";
11+
12+
// just to ignore warnings about unused member
13+
System.out.println(function.apply("hello"));
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package ghIssues;
2+
3+
import java.util.function.Predicate;
4+
import java.util.stream.Stream;
5+
6+
public class Issue2837 {
7+
public boolean overridable1(String s) {
8+
return true;
9+
}
10+
11+
public boolean overridable2(String s) {
12+
return true;
13+
}
14+
15+
// overridable method referenced but not used
16+
public final Predicate<String> predicate1 = this::overridable1;
17+
18+
// overridable method used
19+
public final long matchesCount = Stream.of("Test").filter(this::overridable2).count();
20+
}

0 commit comments

Comments
 (0)