google / google/error-prone

`UnnecessaryAsync` misses the split declaration/assignment form

Open
#5,829 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
7.2k
Forks
820
Avg merge
5h 9m
Merged PRs (30d)
50

Description

### Error Prone version

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

### Check name

[UnnecessaryAsync](https://errorprone.info/bugpattern/UnnecessaryAsync)

### Description

`UnnecessaryAsync` flags atomic variables that are initialized and do not escape the current scope (so the atomicity is unnecessary). The matcher recognizes the combined declarator-with-initializer form `AtomicInteger a = new AtomicInteger(0)`. Splitting it into `AtomicInteger a; a = new AtomicInteger(0);` is a semantics-preserving rewrite — the variable is still initialized exactly once before use and still does not escape — yet the rule stops firing.

### Reproducer

```java
// BEFORE — 1 finding
import java.util.concurrent.atomic.AtomicInteger;
class C {
int f() {
AtomicInteger a = new AtomicInteger(0);
a.incrementAndGet();
return a.get();
}
}
```

```java
// AFTER — equivalent rewrite, 0 findings
import java.util.concurrent.atomic.AtomicInteger;
class C {
int f() {
AtomicInteger a;
a = new AtomicInteger(0);
a.incrementAndGet();
return a.get();
}
}
```

### Expected behavior

`UnnecessaryAsync` should report the same finding on BEFORE and AFTER, since the variable remains initialized-once and non-escaping in both forms.

### Actual behavior

- BEFORE: 1 finding (`Variables which are initialized and do not escape the current scope ...`).
- AFTER: 0 findings.

The matcher only recognizes the combined declarator-with-initializer form and misses the equivalent split-assignment form.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.