java:S2189 Inconsistent behavior on infinite loops with method calls in update expressions

Product: SonarQube Community Build 26.5.0.122743
sonar-java version: sonar-java 8.29 (build 43460)
sonar-java SE version: sonar-java-symbolic-execution 8.16.4 (build 1912)
Java source level: 21 (javac 21, source/target 21)

Rule

S2189 - Loops should not be infinite

Description

There is an inconsistency in how rule java:S2189 handles infinite for loops.

When a for loop has no condition expression (which implicitly evaluates to true), the rule correctly raises an issue. However, when the condition is explicitly set to true, and the update expression contains a method call, the rule fails to detect the infinite loop (False Negative).

Logically, both cases are identical infinite loops without any break, return, or throw statements, so they should exhibit the same analysis results.

Reproducer

public class LoopInconsistency {

  // Case A: NO finding (False Negative)
  void loopWithExplicitTrue() {
    for (Object o = getNext(); true; o = getNext()) {
      String s = (String) o;
    }
  }

  // Case B: S2189 fired (Correct)
  void loopWithEmptyCondition() {
    for (Object o = getNext(); ; o = getNext()) {
      String s = (String) o;
    }
  }

  Object getNext() { 
    return null; 
  }
}

Expected behavior

Both loopWithExplicitTrue and loopWithEmptyCondition should trigger a java:S2189 warning because both are infinite loops that cannot terminate normally.

Actual behavior

  • loopWithEmptyCondition triggers java:S2189 as expected.
  • loopWithExplicitTrue does not trigger any issue. It seems the Symbolic Execution engine becomes overly conservative when an explicit true condition is paired with a method call in the loop update expression, potentially fearing side-effects or exceptions that are not actually handling the loop termination.