Skip to content

Commit c06c7b8

Browse files
cpovirkError Prone Team
authored and
Error Prone Team
committed
Look for infinite recursion in the first statement of multi-statement methods.
PiperOrigin-RevId: 500163360
1 parent 0f5753f commit c06c7b8

File tree

2 files changed

+52
-3
lines changed

2 files changed

+52
-3
lines changed

core/src/main/java/com/google/errorprone/bugpatterns/InfiniteRecursion.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616

1717
package com.google.errorprone.bugpatterns;
1818

19-
import static com.google.common.collect.Iterables.getOnlyElement;
2019
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
2120
import static com.google.errorprone.matchers.Description.NO_MATCH;
2221

2322
import com.google.errorprone.BugPattern;
23+
import com.google.errorprone.ErrorProneFlags;
2424
import com.google.errorprone.VisitorState;
2525
import com.google.errorprone.matchers.Description;
2626
import com.google.errorprone.util.ASTHelpers;
@@ -37,19 +37,37 @@
3737
import com.sun.source.tree.TypeCastTree;
3838
import com.sun.source.util.SimpleTreeVisitor;
3939
import com.sun.tools.javac.code.Symbol.MethodSymbol;
40+
import javax.inject.Inject;
4041
import org.checkerframework.checker.nullness.qual.Nullable;
4142

4243
/** A {@link BugChecker}; see the associated {@link BugPattern} annotation for details. */
4344
@BugPattern(
4445
summary = "This method always recurses, and will cause a StackOverflowError",
4546
severity = ERROR)
4647
public class InfiniteRecursion extends BugChecker implements BugChecker.MethodTreeMatcher {
48+
private final boolean matchFirstOfMultipleStatements;
49+
50+
@Inject
51+
public InfiniteRecursion(ErrorProneFlags flags) {
52+
// TODO(b/264529494): Remove flag.
53+
matchFirstOfMultipleStatements =
54+
flags.getBoolean("InfiniteRecursion:MatchFirstOfMultipleStatements").orElse(true);
55+
}
56+
4757
@Override
4858
public Description matchMethod(MethodTree tree, VisitorState state) {
49-
if (tree.getBody() == null || tree.getBody().getStatements().size() != 1) {
59+
if (tree.getBody() == null || tree.getBody().getStatements().isEmpty()) {
60+
return NO_MATCH;
61+
}
62+
if (!matchFirstOfMultipleStatements && tree.getBody().getStatements().size() > 1) {
5063
return NO_MATCH;
5164
}
52-
Tree statement = getOnlyElement(tree.getBody().getStatements());
65+
/*
66+
* TODO(b/264529494): Match statements after the first as long as they're executed
67+
* unconditionally. And match more than just `return` and method-invocation expression
68+
* statements, too.
69+
*/
70+
Tree statement = tree.getBody().getStatements().get(0);
5371
MethodInvocationTree invocation =
5472
statement.accept(
5573
new SimpleTreeVisitor<MethodInvocationTree, Void>() {

core/src/test/java/com/google/errorprone/bugpatterns/InfiniteRecursionTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ public void positive() {
5050
.doTest();
5151
}
5252

53+
@Test
54+
public void positiveMultipleStatements() {
55+
compilationHelper
56+
.addSourceLines(
57+
"Test.java",
58+
"class Test {",
59+
" Test f() {",
60+
" // BUG: Diagnostic contains:",
61+
" f();",
62+
" return this;",
63+
" }",
64+
"}")
65+
.doTest();
66+
}
67+
5368
@Test
5469
public void positiveStatic() {
5570
compilationHelper
@@ -102,6 +117,22 @@ public void negative() {
102117
.doTest();
103118
}
104119

120+
@Test
121+
public void negativeForNowMultipleStatements() {
122+
compilationHelper
123+
.addSourceLines(
124+
"Test.java",
125+
"class Test {",
126+
" Test f() {",
127+
" new Test();",
128+
// TODO(b/264529494): Match this.
129+
" f();",
130+
" return this;",
131+
" }",
132+
"}")
133+
.doTest();
134+
}
135+
105136
@Test
106137
public void negativeDelegate() {
107138
compilationHelper

0 commit comments

Comments
 (0)