Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ private Description check(ExpressionTree condition, ImmutableList<Tree> loopBody
.build();
}

/** Scan for loop conditions that are determined entirely by the state of local variables. */
/** Scan for loop conditions determined entirely by local variables and compile-time constants. */
private static class LoopConditionVisitor extends SimpleTreeVisitor<Boolean, Void> {

static ImmutableSet<Symbol.VarSymbol> scan(Tree tree) {
Expand All @@ -112,6 +112,9 @@ static ImmutableSet<Symbol.VarSymbol> scan(Tree tree) {

@Override
public Boolean visitIdentifier(IdentifierTree tree, Void unused) {
if (ASTHelpers.constValue(tree) != null) {
return true;
}
Symbol sym = ASTHelpers.getSymbol(tree);
if (sym instanceof Symbol.VarSymbol varSymbol) {
switch (sym.getKind()) {
Expand All @@ -130,6 +133,11 @@ public Boolean visitLiteral(LiteralTree tree, Void unused) {
return true;
}

@Override
protected Boolean defaultAction(Tree node, Void unused) {
return ASTHelpers.constValue(node) != null;
}

@Override
public Boolean visitUnary(UnaryTree node, Void unused) {
return node.getExpression().accept(this, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,112 @@ void f() {
.doTest();
}

@Test
public void compileTimeConstantBounds_areEquivalentToLiterals() {
compilationTestHelper
.expectErrorMessage(
"ONLY_I",
message ->
message.contains("condition variable(s) never modified in loop body: i")
&& !message.contains("i,"))
.addSourceLines(
"Test.java",
"""
class Test {
static final int ZERO = 0;
static final int ONE = 1;
static final boolean TRUE = true;
static final boolean FALSE = false;

void sink() {}

void f() {
final int localOne = 1;
// BUG: Diagnostic matches: ONLY_I
for (int i = 0; i < 1; sink()) {}
// BUG: Diagnostic matches: ONLY_I
for (int i = 0; i < localOne; sink()) {}
// BUG: Diagnostic matches: ONLY_I
for (int i = 0; i < ONE; sink()) {}
// BUG: Diagnostic matches: ONLY_I
for (int i = 0; i < ZERO; sink()) {}
// BUG: Diagnostic matches: ONLY_I
for (int i = 0; TRUE && i < ONE; sink()) {}
// BUG: Diagnostic matches: ONLY_I
for (int i = 0; FALSE || i < ONE; sink()) {}
}
}
""")
.doTest();
}

@Test
public void compileTimeConstantExpressions() {
compilationTestHelper
.addSourceLines(
"Test.java",
"""
class Bounds {
static final int ZERO = 0;
static final int ONE = 1;
static final int TWO = ONE + 1;
}

class Test {
static final boolean TRUE = true;

void sink() {}

void f() {
// BUG: Diagnostic contains:
for (int i = 0; i < Bounds.ONE; sink()) {}
// BUG: Diagnostic contains:
for (int i = 0; i < Bounds.TWO; sink()) {}
// BUG: Diagnostic contains:
for (int i = 0; i < Bounds.ONE + 1; sink()) {}
// BUG: Diagnostic contains:
for (int i = 0; i < (int) Bounds.ONE; sink()) {}
// BUG: Diagnostic contains:
for (int i = 0; i < (TRUE ? Bounds.ONE : Bounds.ZERO); sink()) {}
}
}
""")
.doTest();
}

@Test
public void nonConstantFieldsRemainOutOfScope() {
compilationTestHelper
.addSourceLines(
"Test.java",
"""
class Test {
static final Integer BOXED_ONE = 1;
static final int RUNTIME_ONE = Integer.parseInt("1");
static final Boolean BOXED_TRUE = true;
static final boolean RUNTIME_TRUE = Boolean.parseBoolean("true");
static final boolean RUNTIME_FALSE = Boolean.parseBoolean("false");
static int mutableOne = 1;
static boolean mutableTrue = true;
static boolean mutableFalse = false;

void sink() {}

void f() {
for (int i = 0; i < BOXED_ONE; sink()) {}
for (int i = 0; i < RUNTIME_ONE; sink()) {}
for (int i = 0; i < mutableOne; sink()) {}
for (int i = 0; BOXED_TRUE && i < 1; sink()) {}
for (int i = 0; RUNTIME_TRUE && i < 1; sink()) {}
for (int i = 0; RUNTIME_FALSE || i < 1; sink()) {}
for (int i = 0; mutableTrue && i < 1; sink()) {}
for (int i = 0; mutableFalse || i < 1; sink()) {}
}
}
""")
.doTest();
}

@Test
public void negative_field() {
compilationTestHelper
Expand Down