Skip to content

Support static @Initializer methods assigning receiver parameter fields#1626

Open
Shankar-v27 wants to merge 2 commits into
uber:masterfrom
Shankar-v27:fix-static-initializer-enum-fields
Open

Support static @Initializer methods assigning receiver parameter fields#1626
Shankar-v27 wants to merge 2 commits into
uber:masterfrom
Shankar-v27:fix-static-initializer-enum-fields

Conversation

@Shankar-v27

@Shankar-v27 Shankar-v27 commented Jul 4, 2026

Copy link
Copy Markdown

Thank you for contributing to NullAway!

Please note that once you click "Create Pull Request" you will be asked to sign our Uber Contributor License Agreement via CLA assistant.

Before pressing the "Create Pull Request" button, please provide the following:

  • A description about what and why you are contributing, even if it's trivial.

Description

Adds support for static methods annotated with @Initializer that initialize fields through a receiver parameter.

Currently, NullAway recognizes initialization performed through instance initializer methods, but static helper methods such as:

@Initializer
static void init(MyEnum e, Object value) {
    e.field = value;
}

are not considered when checking field initialization.

This results in false positive @NonNull field not initialized warnings for cases such as enum constants that defer field assignment through shared static initialization logic.

This PR updates the initialization analysis to:

  • collect fields initialized through parameter access paths

  • support static @Initializer helper methods

  • reuse the existing AccessPath-based nullness tracking

  • preserve the existing instance initializer behavior

    • The issue number(s) or PR number(s) in the description if you are contributing in response to those.

Fixes #1625

  • If applicable, unit tests.

Added regression coverage with EnumWithStaticInitializer, verifying that enum fields assigned through a static @Initializer helper are recognized as initialized.

Testing

Ran successfully:

gradlew.bat spotlessApply
gradlew.bat :nullaway:test

Both completed with BUILD SUCCESSFUL.

Summary by CodeRabbit

  • Bug Fixes
    • Field initialization analysis now recognizes assignments made via static initializer methods, reducing false positives for fields that are initialized indirectly during class setup.
    • Initialization detection is more accurate when initializer logic derives non-null field values from method parameters.
  • Tests
    • Expanded negative-case coverage with an enum scenario that uses a static initializer plus an initializer method to assign field values.

@CLAassistant

CLAassistant commented Jul 4, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

This change extends NullAway's field initialization checking to recognize fields initialized via static initializer methods annotated with @Initializer. A new API, getNonnullFieldsOfParameterAtExit, was added to AccessPathNullnessAnalysis, backed by NullnessStore.getNonNullParameterFields, which filters non-null access paths rooted at a parameter down to field elements. NullAway.notAssignedInAnyInitializer now iterates static initializer methods, applies this analysis to method parameters, and includes the resulting fields as initialized. A test case with an enum using a static @Initializer method was added.

Suggested labels: run-benchmarks

Suggested reviewers: yuxincs, seemantasaha

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: supporting static @Initializer methods that assign fields through a receiver parameter.
Linked Issues check ✅ Passed The PR addresses #1625 by recognizing static @Initializer helper methods and counting receiver-parameter field assignments toward initialization.
Out of Scope Changes check ✅ Passed The changes stay focused on static initializer support, related dataflow helpers, and a regression test, with no unrelated scope added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nullaway/src/main/java/com/uber/nullaway/dataflow/NullnessStore.java`:
- Around line 292-315: The body of getNonNullParameterFields is duplicating the
same access-path scanning logic used by getReceiverFields,
getNonNullReceiverFields, and getNonNullStaticFields. Extract the shared
iteration/filtering into a private helper in NullnessStore that takes the
nullness value and a root-matching predicate (or equivalent root selector), then
have getNonNullParameterFields delegate to it with the parameter equality check.
Keep the FIELD-only element filtering and the existing result type unchanged.

In `@nullaway/src/main/java/com/uber/nullaway/NullAway.java`:
- Around line 2342-2352: The static initializer handling in NullAway.java only
analyzes the first parameter of each method, so later-parameter field
initialization paths are missed. Update the loop in the static initializer
processing to consider all parameters from each MethodTree returned by
entities.staticInitializerMethods(), or else clearly enforce and document a
first-parameter-only contract; use getNullnessAnalysis,
getNonnullFieldsOfParameterAtExit, and initInSomeInitializerBuilder as the key
locations to adjust.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 159d512d-fd90-4791-b621-e57b5f3fe1c4

📥 Commits

Reviewing files that changed from the base of the PR and between addfe41 and ccd04e9.

📒 Files selected for processing (4)
  • nullaway/src/main/java/com/uber/nullaway/NullAway.java
  • nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessAnalysis.java
  • nullaway/src/main/java/com/uber/nullaway/dataflow/NullnessStore.java
  • nullaway/src/test/resources/com/uber/nullaway/testdata/CheckFieldInitNegativeCases.java

Comment thread nullaway/src/main/java/com/uber/nullaway/NullAway.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nullaway/src/main/java/com/uber/nullaway/dataflow/NullnessStore.java (1)

292-302: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make getFieldsWithRootMatching accept a nullable root AccessPath.getRoot() is @Nullable, and NullnessStore is @NullMarked, so rootMatches.test(ap.getRoot()) should type the predicate as Predicate<@nullable Element> (or equivalent) to match the values it receives.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nullaway/src/main/java/com/uber/nullaway/dataflow/NullnessStore.java` around
lines 292 - 302, The root-matching helper in NullnessStore is currently typed
too narrowly for nullable roots. Update getFieldsWithRootMatching and its
rootMatches predicate usage so the predicate accepts a nullable Element (for
example Predicate<`@Nullable` Element>), since AccessPath.getRoot() can return
null under `@NullMarked`; adjust the method signature and any related local
declarations in NullnessStore to match the actual values passed into
rootMatches.test(...).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@nullaway/src/main/java/com/uber/nullaway/dataflow/NullnessStore.java`:
- Around line 292-302: The root-matching helper in NullnessStore is currently
typed too narrowly for nullable roots. Update getFieldsWithRootMatching and its
rootMatches predicate usage so the predicate accepts a nullable Element (for
example Predicate<`@Nullable` Element>), since AccessPath.getRoot() can return
null under `@NullMarked`; adjust the method signature and any related local
declarations in NullnessStore to match the actual values passed into
rootMatches.test(...).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9411cc5e-da5f-4211-93f7-3f2a41b2ae07

📥 Commits

Reviewing files that changed from the base of the PR and between ccd04e9 and f17130c.

📒 Files selected for processing (2)
  • nullaway/src/main/java/com/uber/nullaway/NullAway.java
  • nullaway/src/main/java/com/uber/nullaway/dataflow/NullnessStore.java

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.

False positive over @Initializer static method

2 participants