Product/version: SonarQube Community Build 26.9.0.129388 (sonar-java 8.41.0.47177); also 26.7.0.124771 (sonar-java 8.34.0.44906), sonar-scanner 8.1.0.6389
Language: Java
Rule: java:S3824
Issue:
S3824 wants a Map.get() null test followed by a put replaced with computeIfAbsent. seed/Cache.java tests value == null and is reported. variant/Cache.java tests !(value != null), which is the same test on the same value, and isn’t reported. !(!(value == null)) isn’t reported either.
MapComputeIfAbsentOrPresentCheck (symbolic-execution plugin 8.16.4.1912, shipped with this release) matches the null test by the syntax of the if condition, == or != against the null literal, and doesn’t look through !. The same spelling also gets past S2168’s double-checked locking check (linked below). PMD’s DoubleCheckedLocking had the same negated-guard gap and fixed it in 7.27.0 (pmd#6900).
Reproducer:
seed/Cache.java:
import java.util.Map;
public class Cache {
String lookup(Map<String, String> map, String key) {
String value = map.get(key);
if (value == null) { // S3824 reported
value = key.toUpperCase();
map.put(key, value);
}
return value;
}
}
variant/Cache.java:
import java.util.Map;
public class Cache {
String lookup(Map<String, String> map, String key) {
String value = map.get(key);
if (!(value != null)) { // the same test; nothing reported
value = key.toUpperCase();
map.put(key, value);
}
return value;
}
}
Expected behavior:
S3824 on the if in variant/Cache.java (line 6), as on the one in seed/Cache.java.
How to reproduce:
# one project per file
sonar-scanner -Dsonar.projectKey=<key> -Dsonar.sources=. \
-Dsonar.java.binaries=. -Dsonar.scm.disabled=true
See also False negative java:S2168: not reported when the outer null check is written `!(helper != null)`.
Output of the commands above:
### SonarQube Community Build 26.9.0.129388 (sonar-java 8.41.0.47177), server reports 26.9.0.129388, default Sonar way profile rule: S3824
--- SEED -- expected: reported (seed/)
java:S1220 line - Move this file to a named package.
java:S3824 line 5 Replace this "Map.get()" and condition with a call to "Map.c <== S3824
[S3824: 1]
--- VARIANT -- expected: reported; actual: not reported (variant/)
java:S1220 line - Move this file to a named package.
java:S1940 line 6 Use the opposite operator ("==") instead.
[S3824: 0]