`UnnecessaryAsync` misses the split declaration/assignment form
- 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
Assessment
This issue has not been assessed yet.