Skip to content

LoopConditionChecker fails to resolve compile-time constant loop bounds, missing genuine infinite loops #5826

Description

@Chordrain

Error Prone version

Error Prone 2.49.0 (javac plugin) — javac 21.0.8, source/target 21.

Check name

LoopConditionChecker

Description

LoopConditionChecker flags loops whose condition variable(s) are never modified in the loop body (a no-op or infinite loop). The check is sensitive to the syntactic form of the loop bound rather than its compile-time-constant value: replacing the literal 1 with a static final int ONE = 1 — which javac inlines as the constant 1 — silences the diagnostic, even though the loop is byte-for-byte identical at runtime and is still an infinite loop.

Reproducer

// BEFORE — LoopConditionChecker fires on the second loop
class C {
  void foo() {
    for (int i = 0; i < 1; i++) {}      // i is modified -> not flagged
    for (int i = 0; i < 1; foo()) {}    // i never modified -> FLAGGED
  }
}
// AFTER — semantically identical (ONE is the constant 1), rule no longer fires
class C { static final int ONE = 1;
  void foo() {
    for (int i = 0; i < ONE; i++) {}
    for (int i = 0; i < ONE; foo()) {}  // still an infinite loop, NOT flagged
  }
}

Expected behavior

LoopConditionChecker should report the same finding on BEFORE and AFTER. ONE is a compile-time constant equal to 1, so i < ONE and i < 1 are identical, and the second loop is an infinite loop in both forms.

Actual behavior

  • BEFORE: 1 finding — condition variable(s) never modified in loop body: i on the for (int i = 0; i < 1; foo()) loop.
  • AFTER: 0 findings. The checker bails out once the bound is a named constant instead of a literal, failing to constant-fold the condition operand.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions