Skip to content

SONARJAVA-6464 Fix S2119 false positive for chained Random() in field initializers#5831

Open
sonar-nigel[bot] wants to merge 3 commits into
masterfrom
fix/SONARJAVA-6464-s2119-false-positive-on-statically-initialized-randomints-sonnet
Open

SONARJAVA-6464 Fix S2119 false positive for chained Random() in field initializers#5831
sonar-nigel[bot] wants to merge 3 commits into
masterfrom
fix/SONARJAVA-6464-s2119-false-positive-on-statically-initialized-randomints-sonnet

Conversation

@sonar-nigel

@sonar-nigel sonar-nigel Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Relates to SONARJAVA-6464

S2119 incorrectly flagged new Random() when it was used as the receiver of a chained method call in a field initializer (e.g., static IntStream s = new Random().ints(100)). The fix extends the initialization-context guard to treat any code with no enclosing method or constructor as a one-time class-level initialization, covering field initializers, static initializer blocks, and instance initializer blocks.

  • The renamed method isEffectivelyOneTimeInitialized() now returns true when getEnclosingMethod() is null, which previously short-circuited to false and triggered the false positive
  • Direct field initializers (e.g., static Random f = new Random()) were already exempt; only the chained form was affected
  • The rspec Exceptions section is updated to mention field initializers and class initializer blocks alongside constructors and static main

Vibe Bot added 2 commits July 22, 2026 14:20
Tests cover the scenario where new Random() is used as the receiver of a
chained method call in a static or instance field initializer (e.g.,
static IntStream s = new Random().ints(100)). The rule incorrectly flags
these cases because isInConstructorOrStaticMain() returns false when
getEnclosingMethod() is null (field initializer context).

The tests verify that field initializers with chained Random() calls are
treated as compliant, matching both static and instance field initializer
patterns.

Relates to SONARJAVA-6464
S2119 was incorrectly flagging `new Random()` used in field initializers
with chained method calls (e.g., `static IntStream s = new Random().ints(100)`).
The root cause was that `isInConstructorOrStaticMain()` returned `false` when
`getEnclosingMethod()` returned `null` (field initializer context), while
`isUsedOnlyLocally()` correctly returned `true` for the chained `MEMBER_SELECT`
parent, causing a false positive.

The fix renames the method to `isEffectivelyOneTimeInitialized()` and returns
`true` when `getEnclosingMethod()` is `null`, covering field initializers,
static initializer blocks, and instance initializer blocks — all contexts where
the `Random` is a one-time class-level initialization. The rspec exceptions
section is updated to reflect this behavior.

Relates to SONARJAVA-6464
@sonar-nigel

sonar-nigel Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Rule Profile

Field Value
Rule S2119 — "Random" objects should be reused
Severity / type Critical BUG
Sonar Way Yes
Scope Main

Genuine violation (a new Random is created on every invocation):

void doSomething() {
  new Random().ints(100).forEach(System.out::println); // Noncompliant
}

False Positive Pattern

When new Random() is the receiver of a chained method call in a field initializer, its AST parent is a MEMBER_SELECT node (the .ints accessor). isUsedOnlyLocally() returns true for MEMBER_SELECT parents without checking whether the chain is part of a field initializer. Meanwhile, the former isInConstructorOrStaticMain() returned false whenever getEnclosingMethod() was null — the condition for a field initializer context — treating it as a regular method body. The combination caused the rule to report an issue where none should exist.

public class Example {
  // Flagged before the fix — compliant, Random is created once at class-load time
  static IntStream s = new Random().ints(100);
}

Direct field assignment (non-chained) was already handled correctly: its AST parent is VARIABLE, so isLocalVariable() returned false and no issue was raised.

False Negative Risk

The fix suppresses issues for any new Random() that has no enclosing method or constructor. This scope is inherently bounded: field initializers and class initializer blocks execute once per class/instance load, so any Random created there is a one-time class-level initialization. There is no plausible scenario where a new Random() in a field initializer represents a genuine "re-created on every use" bug.

Fix Summary

isInConstructorOrStaticMain() is renamed to isEffectivelyOneTimeInitialized() and now returns true when getEnclosingMethod() is null, in addition to the existing constructor and static-main conditions. The rspec Exceptions section is updated to explicitly list field initializers and class initializer blocks alongside constructors and static main.

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

SONARJAVA-6464

@sonarqube-next

Copy link
Copy Markdown

@sonar-nigel
sonar-nigel Bot marked this pull request as ready for review July 23, 2026 05:23
@sonar-nigel
sonar-nigel Bot requested a review from asya-vorobeva July 23, 2026 05:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants