java:S1481 false positive on compound-assignment form (`+=`) in `while` condition

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

java:S1481 — Unused local variables should be removed

Description

S1481 incorrectly reports a local variable as unused when its only update site is a compound assignment (+=) embedded in a while condition, even though the assignment’s result is what drives the loop. When the same code is rewritten with an explicit x = x + y assignment in the same position, S1481 correctly recognizes the variable as used. This is a false positive on the compound-assignment form: per JLS §15.26.2, x += y reads x, computes x + y, and writes the result back to x, so the variable is read on every iteration. The two forms are observationally indistinguishable but produce different verdicts.

Reproducer

public class S1481Reproducer {

    // BEFORE — S1481 incorrectly reports `read` as unused.
    public void compoundForm() {
        int read = 0;
        while ((read += step()) < 100) {   // S1481 raised on `read`
            doWork();
        }
    }

    // AFTER — semantically identical, S1481 does NOT fire.
    public void explicitForm() {
        int read = 0;
        while ((read = read + step()) < 100) {
            doWork();
        }
    }

    private int step()   { return 7; }
    private void doWork() { /* no-op */ }
}

Expected behavior

S1481 should treat read += step() exactly like read = read + step(). Both forms read the current value of read on the right-hand side (implicitly for +=), compute a new value, and assign it back; the new value then flows into the surrounding while condition, so read is unambiguously used. S1481 should NOT raise on either method.

Actual behavior

  • compoundForm(): java:S1481 (MINOR) raised on the declaration int read = 0; with message
    “Remove this unused ‘read’ local variable.”
  • explicitForm(): no S1481 violation, although the only structural difference is = + + instead of +=.

Hi @Chordrain!

Thank you for reporting this issue and providing such a clear reproducer!

I was able to successfully reproduce the behavior on my end. It is indeed a false positive, as the compound assignment implicitly reads the variable on each iteration.

I have created a ticket for our development team to look into this and fix it. You can track its progress here: SONARJAVA-6424.

Thank you again for helping us improve SonarQube!

Best regards,

Romain