Skip to content

Conversation

@haewiful
Copy link
Contributor

@haewiful haewiful commented Aug 9, 2025

This support will be used to generate an astubx JDK model from the JSpecify annotated JDK. Includes extensive unit testing and integration testing of the support. We will likely need to add more features as we try to support all the annotations present in the JDK (on wildcards, etc.).

Summary by CodeRabbit

  • New Features

    • Added an ASTUBX generator and CLI to produce .astubx bundles capturing method return-type and nullability metadata; added annotated and unannotated test libraries and a service provider to supply generated stubx files.
  • Chores

    • Integrated .astubx generation into the build lifecycle, added new modules, migrated projects to Java 21 toolchains, and updated formatting guard to exclude the jdk-annotations module.
  • Tests

    • Added unit and integration tests validating JDK/JSPECIFY nullability scenarios and end-to-end .astubx generation.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 9, 2025

Walkthrough

Adds five jdk-annotations modules and includes them in settings.gradle: astubx-generator, astubx-generator-cli, test-annotated, test-unannotated, and jdk-integration-test. Introduces AstubxGenerator and AstubxGeneratorCLI to convert compiler-emitted JSON into .astubx files, wires a Gradle Exec task generateAstubx in test-annotated, and packages the generated astubx into test-unannotated with a TestProvider. Updates NullnessAnnotationSerializer (records made public; MethodInfo gains a returnType), adds unit and integration tests, enables Java 21 toolchains across modules, and updates the Spotless guard to skip jdk-annotations.

Possibly related PRs

  • Compile with JDK 24 #1276: Also modifies Gradle Java toolchain/sourceCompatibility and touches jdk-javac-plugin build configuration, overlapping with this PR's toolchain and build changes.
  • Compile with JDK 25 #1303: Adjusts Gradle Java toolchain languageVersion across modules; related to this PR's widespread Java 21 toolchain updates.

Suggested reviewers

  • yuxincs
  • lazaroclapp

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title “Jdk javac” is overly generic and does not summarize the primary changes in the pull request, which add multiple modules for generating JDK nullness JSON and producing astubx files via a javac plugin. It fails to convey the scope of new modules, build configurations, and integration tests introduced. A reviewer scanning PR history would not understand what functionality or modules were added from this title alone. Rename the title to clearly reflect the main changes, for example “Add JDK nullness JSON generator and astubx-generator CLI modules,” to succinctly describe the added functionality.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

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

Some high level comments; haven't reviewed the actual logic. Looking forward to seeing the integration test working, and then I'll review in more detail

@codecov
Copy link

codecov bot commented Aug 27, 2025

Codecov Report

❌ Patch coverage is 84.02367% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.39%. Comparing base (7e2a06f) to head (1640a9e).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
.../uber/nullaway/jdkannotations/AstubxGenerator.java 83.33% 17 Missing and 10 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1243      +/-   ##
============================================
- Coverage     88.49%   88.39%   -0.10%     
- Complexity     2479     2516      +37     
============================================
  Files            93       94       +1     
  Lines          8228     8393     +165     
  Branches       1623     1657      +34     
============================================
+ Hits           7281     7419     +138     
- Misses          474      491      +17     
- Partials        473      483      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

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

It's a good start! The comments below are a couple of initial minor comments. The major issue, though, is that I don't think the jdk-integration-test project is set up correctly. Right now, it depends on the test-annotated project. This means it sees the annotated code, which means we are not testing if the astubx file is being loaded correctly. Here is what should happen instead, at a high level.

  1. test-annotated should be compiled with the jdk-javac-plugin as a javac plugin, producing a JSON file in some location. Then this JSON should be converted to an astubx file output.astubx using the new CLI you've written here. I think this is already happening.
  2. test-unannotated should not be compiled with the jdk-javac-plugin; there is no reason to do so, as it has no annotations. Instead, after it gets compiled, the output.astubx generated from step 1 should be added to its jar file. There is logic to do something like this here using jar uf that you can base this on.
  3. jdk-integration-test should depend on test-unannotated. After step 2, the jar for test-unannotated should contain the unannotated class files plus the astubx with the annotation info. Then, the tests should only pass if the astubx is getting loaded correctly.

If something doesn't make sense let me know.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b799283 and 0891649.

📒 Files selected for processing (1)
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
📚 Learning: 2025-08-29T18:41:43.584Z
Learnt from: msridhar
PR: uber/NullAway#1259
File: jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java:318-321
Timestamp: 2025-08-29T18:41:43.584Z
Learning: Classes annotated with NullMarked are analyzed by NullAway even if they are not in packages specified by the AnnotatedPackages configuration. The NullMarked annotation guarantees NullAway analysis.

Applied to files:

  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
🧬 Code graph analysis (1)
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (2)
jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java (1)
  • NullnessAnnotationSerializer (35-187)
library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/StubxWriter.java (1)
  • StubxWriter (15-153)

Copy link
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

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

Let's look at this one more comment, but aim to get this landed soon

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0891649 and 90fb83c.

📒 Files selected for processing (2)
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (1 hunks)
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
🧰 Additional context used
🧬 Code graph analysis (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (1)
  • AstubxGenerator (34-309)
🔇 Additional comments (3)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (3)

23-44: LGTM!

The test setup is well-structured. The DummyChecker provides the necessary BugChecker instance for CompilationTestHelper, and the setup method correctly configures the NullnessAnnotationSerializer plugin with the temporary directory for JSON output.


46-371: Comprehensive test coverage.

The test suite covers a wide range of nullness annotation scenarios including nullable returns, parameters, upper bounds, generics, arrays, primitives, and void methods. The tests are well-structured with clear expectations and follow a consistent pattern that enhances readability.


311-314: Confirm asymmetric generic handling. AstubxGenerator intentionally strips generic parameters from method parameters (lines 212–231) but retains them in return types (lines 202–207). Verify this aligns with the astubx format specification.

msridhar and others added 3 commits October 14, 2025 12:37
…ay/jdkannotations/AstubxTest.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)

22-24: Redundant @NonNull annotation under @NullMarked.

Under @NullMarked, parameters are non-null by default, so the explicit @NonNull on line 22 is redundant. While not incorrect, removing it would align with JSpecify conventions where non-null is implicit within @NullMarked scopes.

Apply this diff to remove the redundant annotation:

-  public static void printObjectString(@NonNull Object object) {
+  public static void printObjectString(Object object) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90fb83c and d2121d8.

📒 Files selected for processing (3)
  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1 hunks)
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1 hunks)
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
🧬 Code graph analysis (2)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (2)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (1)
  • NullMarked (7-66)
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (2)
  • ParameterAnnotation (5-52)
  • Generic (32-41)
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)
  • Generic (36-45)
🔇 Additional comments (6)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (2)

7-8: LGTM!

The @NullMarked annotation correctly establishes non-null as the default for all type uses in this class, aligning with JSpecify semantics.


10-12: LGTM!

The method implementations correctly demonstrate various nullability scenarios for testing:

  • Default non-null parameters (add)
  • Explicit @Nullable parameters (getNewObjectIfNull, takesNullArray, getString)
  • Array type annotations (Object @Nullable [] vs Object[])
  • Generic type parameter nullability

All implementations align with the test expectations in JDKIntegrationTest.

Also applies to: 14-20, 27-34, 36-45, 48-55

jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (2)

3-3: Verify the intended purpose of annotations in the "unannotated" module.

The test-unannotated module imports org.jspecify.annotations.Nullable and uses it on line 44, which seems inconsistent with the module's "unannotated" designation. Please confirm whether:

  1. The module should be completely unannotated (in which case line 44's @Nullable should be removed)
  2. The module intentionally includes minimal annotations for specific test scenarios (in which case the name might be misleading)

Looking at the test expectations in JDKIntegrationTest.nullableGenericArrayTest() (lines 265-274), the test expects takesNullGenericArray(null) to succeed and takesNonNullGenericArray(null) to fail, suggesting the annotation on line 44 may be intentional for testing.


6-8: LGTM!

The method implementations correctly mirror the annotated version's API surface, providing a baseline for testing NullAway's behavior with and without explicit annotations.

Also applies to: 10-16, 18-20, 23-30, 32-41, 44-51

jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (2)

17-20: LGTM!

The setup correctly initializes CompilationTestHelper with NullAway.class for each test, ensuring proper test isolation.


22-301: Enforce test failure on unsupported JDK versions
The build config currently disables the testJdk17 task via onlyIf { false }. Per our JSpecify testing guidelines, unsupported JDKs should produce test failures (not be skipped) to catch regressions. Please replace the skip with a version check that fails on JDK < 21.
(jdk-annotations/jdk-integration-test/build.gradle)

⛔ Skipped due to learnings
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.
Learnt from: msridhar
PR: uber/NullAway#1243
File: jdk-annotations/astubx-generator/build.gradle:22-22
Timestamp: 2025-10-09T19:59:16.521Z
Learning: When disabling testJdk17 tasks for modules requiring JDK 21, use `onlyIf { false }` to skip the task:
```gradle
tasks.named("testJdk17").configure {
    onlyIf { false }
}
```
Do not use `doFirst { throw new GradleException(...) }` as it will cause CI failures when the task is executed.

ImmutableMap.of(
"ParameterizedTypeArray: ParameterizedTypeArray()",
MethodAnnotationsRecord.create(ImmutableSet.of(), ImmutableMap.of()),
"ParameterizedTypeArray:java.util.List<java.lang.String>[] getList()",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering, why do we need java.util.List<java.lang.String>[] for the return type, but java.util.List[] without the generic for the parameter type? Is this some weirdness in astubx format?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is weird. I'll look into this.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)

388-393: Unresolved: clarify or remove the fallback condition.

This concern was raised in a previous review and remains unaddressed. The fallback logic allows the test to pass when the astubx file doesn't exist if all expectations are empty, which could mask file creation failures. Since AstubxGenerator.writeToAstubxFile always creates the file and no current test exercises the fallback condition (all tests have non-empty expectedMethodRecords), the fallback is effectively dead code that may hide bugs.

Consider one of the following approaches:

Option 1: Always expect file creation (recommended)

     Assert.assertTrue(
         "astubx file was not created",
-        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx"))
-            || (expectedMethodRecords.isEmpty()
-                && expectedNullableUpperBounds.isEmpty()
-                && expectedNullMarkedClasses.isEmpty()));
+        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx")));

Option 2: Explicitly document and test the empty case

-    Assert.assertTrue(
-        "astubx file was not created",
-        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx"))
-            || (expectedMethodRecords.isEmpty()
-                && expectedNullableUpperBounds.isEmpty()
-                && expectedNullMarkedClasses.isEmpty()));
+    if (expectedMethodRecords.isEmpty()
+        && expectedNullableUpperBounds.isEmpty()
+        && expectedNullMarkedClasses.isEmpty()) {
+      // Document: for empty data, file creation is optional (or should not happen)
+      // Add explicit assertion here based on intended behavior
+      return;
+    }
+    Assert.assertTrue(
+        "astubx file was not created",
+        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx")));
🧹 Nitpick comments (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)

377-377: Simplify redundant path construction.

The Paths.get(...).toString() wrapper is unnecessary since getAbsolutePath() already returns a String.

Apply this diff to simplify:

-    String astubxOutputDirPath = Paths.get(astubxFolder.getRoot().getAbsolutePath()).toString();
+    String astubxOutputDirPath = astubxFolder.getRoot().getAbsolutePath();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2121d8 and b043e1c.

📒 Files selected for processing (1)
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (1)
  • AstubxGenerator (34-309)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build and test on ubuntu-latest
  • GitHub Check: Build caffeine with snapshot
  • GitHub Check: Build and test on macos-latest
  • GitHub Check: Build spring-framework with snapshot
  • GitHub Check: Build and test on windows-latest
🔇 Additional comments (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)

1-371: Comprehensive test coverage.

The test suite thoroughly covers various nullness annotation scenarios including nullable returns, upper bounds, parameters, generics, arrays, primitives, and void methods. The test structure using CompilationTestHelper with a DummyChecker to trigger the NullnessAnnotationSerializer plugin is appropriate and well-organized.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)

388-393: Address the unresolved fallback condition in file existence assertion.

The fallback logic allowing the test to pass when the astubx file doesn't exist (if all expectations are empty) was flagged in a previous review but remains unaddressed. This condition could mask file creation failures.

As previously recommended, consider one of these approaches:

Option 1: Always require file creation

 Assert.assertTrue(
     "astubx file was not created",
-    Files.exists(Paths.get(astubxOutputDirPath, "output.astubx"))
-        || (expectedMethodRecords.isEmpty()
-            && expectedNullableUpperBounds.isEmpty()
-            && expectedNullMarkedClasses.isEmpty()));
+    Files.exists(Paths.get(astubxOutputDirPath, "output.astubx")));

Option 2: Explicitly document the empty-data behavior

+// When all data collections are empty, file creation is optional
 Assert.assertTrue(
     "astubx file was not created",
     Files.exists(Paths.get(astubxOutputDirPath, "output.astubx"))
         || (expectedMethodRecords.isEmpty()
             && expectedNullableUpperBounds.isEmpty()
             && expectedNullMarkedClasses.isEmpty()));
🧹 Nitpick comments (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)

291-316: Document or align generics handling for parameters
getArgumentsAsArray deliberately strips generic type arguments from parameters (e.g. java.util.List[]), while return types keep generics (e.g. java.util.List<java.lang.String>[]). If this asymmetry is part of the Astubx format, add a note in the spec; otherwise update getArgumentsAsArray to preserve parameter generics.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b043e1c and bd29e7d.

📒 Files selected for processing (1)
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (1)
  • AstubxGenerator (34-309)
🔇 Additional comments (4)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (4)

1-35: LGTM!

The test class structure, imports, and DummyChecker implementation are well-organized. The use of TemporaryFolder rules ensures proper test isolation and cleanup.


37-44: LGTM!

The setup method correctly initializes the CompilationTestHelper with appropriate compiler arguments for testing the NullnessAnnotationSerializer plugin.


46-289: LGTM!

The test methods provide comprehensive coverage of various nullness annotation scenarios including nullable returns, parameters, upper bounds, and different type contexts. The consistent test structure enhances maintainability.


318-371: LGTM!

The tests for primitive return types and void returns correctly validate the handling of different return type categories.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (1)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (1)

18-28: Return may be null; add return-level @nullable.

Under @NullMarked, the method returns null for size <= 0 (Line 20) but the return type isn’t annotated @nullable. Annotate the method return in addition to the array element type.

-  public Integer @Nullable [] generateIntArray(int size) {
+  public @Nullable Integer @Nullable [] generateIntArray(int size) {
🧹 Nitpick comments (1)
jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1)

17-21: Optional: factor out repeated setArgs logic.

Reduce duplication and make the flag matrix clearer with a small helper.

   @Before
   public void setup() {
     compilationHelper = CompilationTestHelper.newInstance(NullAway.class, getClass());
   }
+
+  private static java.util.List<String> args(
+      org.junit.rules.TemporaryFolder tmp, boolean jspecify, boolean jarInfer) {
+    java.util.List<String> base =
+        new java.util.ArrayList<>(
+            java.util.Arrays.asList(
+                "-d", tmp.getRoot().getAbsolutePath(), "-XepOpt:NullAway:AnnotatedPackages=com.uber"));
+    if (jspecify) base.add("-XepOpt:NullAway:JSpecifyMode=true");
+    if (jarInfer) base.add("-XepOpt:NullAway:JarInferEnabled=true");
+    return base;
+  }

Example usage (elsewhere in this class):

  • setArgs(args(temporaryFolder, /* jspecify= / false, / jarInfer= */ true))
  • setArgs(args(temporaryFolder, true, false))
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd29e7d and 3c0dd32.

📒 Files selected for processing (4)
  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1 hunks)
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (1 hunks)
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1 hunks)
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
🧬 Code graph analysis (2)
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)
  • Generic (36-45)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (2)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)
  • NullMarked (7-56)
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (3)
  • ReturnAnnotation (5-61)
  • InnerExample (31-35)
  • UpperBoundExample (37-44)
🔇 Additional comments (5)
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (4)

8-14: LGTM!

The null check is correctly implemented and the method safely handles null inputs.


32-34: LGTM!

The method correctly handles null input with an explicit check and provides a safe fallback.


1-50: Verify the intent of missing null safety in test-unannotated module.

This test-unannotated module lacks null checks in most methods, which contrasts with the test-annotated counterpart that uses @Nullable annotations. If these NPE-prone methods are intentionally designed to test nullness checker behavior, consider adding documentation (class-level or method-level comments) to clarify this intent and prevent confusion.


41-49: NPE risk: generic array parameters can be null.

Both methods will throw NullPointerException if the objects array is null. Add null checks to handle this case safely.

 @SuppressWarnings("ArrayToString")
 public static void takesNullGenericArray(Generic<String>[] objects) {
+  if (objects == null) {
+    System.out.println("null array");
+    return;
+  }
   System.out.println(objects);
 }

 @SuppressWarnings("ArrayToString")
 public static void takesNonNullGenericArray(Generic<String>[] objects) {
+  if (objects == null) {
+    throw new IllegalArgumentException("objects cannot be null");
+  }
   System.out.println(objects);
 }

Likely an incorrect or invalid review comment.

jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1)

1-305: Solid coverage and correct use of NullAway options.

End-to-end scenarios look good: returns (incl. arrays), inner classes, and generic upper bounds are exercised with/without JarInfer and JSpecify. No version-gating here, which aligns with our preference to fail rather than skip tests on unsupported JDKs. Based on learnings.

Comment on lines +67 to +69
public static UpperBoundExample<String> @Nullable [] returnNullableGenericArray() {
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Also mark the array-returning method’s return as @nullable.

This method returns null but only the array element type is annotated. Add return-level @nullable.

-  public static UpperBoundExample<String> @Nullable [] returnNullableGenericArray() {
+  public static @Nullable UpperBoundExample<String> @Nullable [] returnNullableGenericArray() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static UpperBoundExample<String> @Nullable [] returnNullableGenericArray() {
return null;
}
public static @Nullable UpperBoundExample<String> @Nullable [] returnNullableGenericArray() {
return null;
}
🤖 Prompt for AI Agents
In
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java
around lines 67 to 69, the method currently only annotates the array element
type as @Nullable but the method return itself can be null; add a return-level
@Nullable before the element type so the return type is annotated as nullable in
addition to the array element (i.e., apply @Nullable to the return type, not
just the component type).

Comment on lines +4 to +6
public static Integer add(Integer a, Integer b) {
return a + b;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

NPE risk: unboxing null Integer parameters.

If either a or b is null, unboxing during the addition operation will throw a NullPointerException. Add null checks or use primitive int parameters if null inputs are not expected.

 public static Integer add(Integer a, Integer b) {
+  if (a == null || b == null) {
+    throw new IllegalArgumentException("Parameters cannot be null");
+  }
   return a + b;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static Integer add(Integer a, Integer b) {
return a + b;
}
public static Integer add(Integer a, Integer b) {
if (a == null || b == null) {
throw new IllegalArgumentException("Parameters cannot be null");
}
return a + b;
}

Comment on lines +16 to +18
public static void printObjectString(Object object) {
System.out.println(object.toString());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

NPE risk: calling toString() on potentially null parameter.

If object is null, calling object.toString() will throw a NullPointerException. Add a null check before dereferencing.

 public static void printObjectString(Object object) {
+  if (object == null) {
+    throw new IllegalArgumentException("object cannot be null");
+  }
   System.out.println(object.toString());
 }
🤖 Prompt for AI Agents
In
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
around lines 16 to 18, the method dereferences the parameter and may NPE;
replace the direct object.toString() call with a null-safe approach (either
check if object == null and handle/print a placeholder, or use
String.valueOf(object) which returns "null" for null inputs) so the method does
not throw when passed null.

Comment on lines +20 to +28
@SuppressWarnings("ArrayToString")
public static void takesNullArray(Object[] objects) {
System.out.println(objects);
}

@SuppressWarnings("ArrayToString")
public static void takesNonNullArray(Object[] objects) {
String unused = objects.toString();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

NPE risk: array parameter can be null.

Both methods will throw NullPointerException if the objects array itself is null (not just its elements). Despite the method names suggesting null-handling intent, there are no parameter annotations or null checks.

 @SuppressWarnings("ArrayToString")
 public static void takesNullArray(Object[] objects) {
+  if (objects == null) {
+    System.out.println("null array");
+    return;
+  }
   System.out.println(objects);
 }

 @SuppressWarnings("ArrayToString")
 public static void takesNonNullArray(Object[] objects) {
+  if (objects == null) {
+    throw new IllegalArgumentException("objects cannot be null");
+  }
   String unused = objects.toString();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@SuppressWarnings("ArrayToString")
public static void takesNullArray(Object[] objects) {
System.out.println(objects);
}
@SuppressWarnings("ArrayToString")
public static void takesNonNullArray(Object[] objects) {
String unused = objects.toString();
}
@SuppressWarnings("ArrayToString")
public static void takesNullArray(Object[] objects) {
if (objects == null) {
System.out.println("null array");
return;
}
System.out.println(objects);
}
@SuppressWarnings("ArrayToString")
public static void takesNonNullArray(Object[] objects) {
if (objects == null) {
throw new IllegalArgumentException("objects cannot be null");
}
String unused = objects.toString();
}
🤖 Prompt for AI Agents
In
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation
around lines 20 to 28, the methods take an Object[] parameter that can be null
and will cause an NPE when used; annotate the parameter as @Nullable and add an
explicit null check before dereferencing (e.g., return or handle the null case)
so the method safely handles a null array, or if the intent is to require
non-null, annotate as @NonNull and add a defensive
Objects.requireNonNull(objects, "...") at the start to fail fast; pick one
approach and apply it consistently to both methods.

Comment on lines +36 to +38
public void printObjectString(T t) {
System.out.println(t.toString());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

NPE risk: inconsistent null handling in Generic class.

Unlike getString (which safely checks t != null), this method calls t.toString() without a null check and will throw NullPointerException if t is null. Consider adding a null check for consistency.

 public void printObjectString(T t) {
+  if (t == null) {
+    throw new IllegalArgumentException("t cannot be null");
+  }
   System.out.println(t.toString());
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void printObjectString(T t) {
System.out.println(t.toString());
}
public void printObjectString(T t) {
if (t == null) {
throw new IllegalArgumentException("t cannot be null");
}
System.out.println(t.toString());
}
🤖 Prompt for AI Agents
In
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
around lines 36 to 38, printObjectString calls t.toString() without a null check
causing an NPE if t is null; modify the method to mirror getString by checking
if t == null and handling that case (e.g., return or print "null") before
calling toString(), ensuring consistent null handling in the generic class.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)

391-397: Avoid masking failures with the empty-expectations fallback

Allowing the test to pass when the file is missing and expectations are empty can hide regressions. Prefer a clear policy (always expect file, or explicitly handle empty case).

Option 1 (always expect file):

-    Assert.assertTrue(
-        "astubx file was not created",
-        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx"))
-            || (expectedMethodRecords.isEmpty()
-                && expectedNullableUpperBounds.isEmpty()
-                && expectedNullMarkedClasses.isEmpty()));
+    Assert.assertTrue(
+        "astubx file was not created",
+        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx")));

Option 2 (explicitly return for empty case):

-    Assert.assertTrue(
-        "astubx file was not created",
-        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx"))
-            || (expectedMethodRecords.isEmpty()
-                && expectedNullableUpperBounds.isEmpty()
-                && expectedNullMarkedClasses.isEmpty()));
+    if (expectedMethodRecords.isEmpty()
+        && expectedNullableUpperBounds.isEmpty()
+        && expectedNullMarkedClasses.isEmpty()) {
+      return;
+    }
+    Assert.assertTrue(
+        "astubx file was not created",
+        Files.exists(Paths.get(astubxOutputDirPath, "output.astubx")));
🧹 Nitpick comments (3)
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (3)

201-206: Handle unqualified @nullable on return types and normalize stripping

Return-type nullable detection only matches the fully-qualified annotation. If javac ever emits unqualified @Nullable, we’ll miss it and leave the token in the signature. Detect both forms, and strip both consistently.

-      // check if return type has Nullable annotation
-      if (returnType.contains("@org.jspecify.annotations.Nullable")) {
-        returnType = returnType.replace("@org.jspecify.annotations.Nullable ", "");
-        returnType = returnType.replaceAll("@Nullable\\s*", "");
-        returnType = returnType.replace(" []", "[]"); // remove whitespace in Array types
-        returnTypeNullness = ImmutableSet.of("Nullable");
-      }
+      // check if return type has Nullable annotation (qualified or unqualified)
+      boolean hasNullableReturn =
+          returnType.contains("@org.jspecify.annotations.Nullable")
+              || returnType.matches(".*@Nullable\\b.*");
+      if (hasNullableReturn) {
+        returnType = returnType.replace("@org.jspecify.annotations.Nullable ", "");
+        returnType = returnType.replaceAll("@Nullable\\s*", "");
+        returnType = returnType.replace(" []", "[]"); // normalize array spacing
+        returnTypeNullness = ImmutableSet.of("Nullable");
+      }

219-226: Match unqualified @nullable in parameter types (consistency with return types)

Param annotation detection only checks FQ name. Support unqualified @Nullable too to avoid missing annotations.

-          for (String token : signatureTokens) {
+          for (String token : signatureTokens) {
             if (token.contains("@")) {
-              if (token.contains("@org.jspecify.annotations.Nullable")) {
+              if (token.contains("@org.jspecify.annotations.Nullable")
+                  || token.matches(".*@Nullable\\b.*")) {
                 argAnnotation.put(i, ImmutableSet.of("Nullable"));
               }
               typeSignature += token.substring(0, token.indexOf('@'));
             } else {
               typeSignature += token;
             }
           }

141-142: Fix error message: not writing JSON here

The message says “JSON file” but this block writes a .astubx binary.

-      System.err.println("Error writing JSON file: " + outputFile.getAbsolutePath());
+      System.err.println("Error writing astubx file: " + outputFile.getAbsolutePath());
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c0dd32 and 148cae4.

📒 Files selected for processing (2)
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (1 hunks)
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
📚 Learning: 2025-08-29T18:41:43.584Z
Learnt from: msridhar
PR: uber/NullAway#1259
File: jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java:318-321
Timestamp: 2025-08-29T18:41:43.584Z
Learning: Classes annotated with NullMarked are analyzed by NullAway even if they are not in packages specified by the AnnotatedPackages configuration. The NullMarked annotation guarantees NullAway analysis.

Applied to files:

  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
🧬 Code graph analysis (2)
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (2)
jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java (1)
  • NullnessAnnotationSerializer (35-187)
library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/StubxWriter.java (1)
  • StubxWriter (15-153)
jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (1)
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (1)
  • AstubxGenerator (34-322)

Comment on lines +170 to +181
// parse JSON file
Map<String, List<ClassInfo>> parsed = new HashMap<>();
for (File jsonFile : jsonFiles) {
try {
String jsonContent = Files.readString(jsonFile.toPath());
Map<String, List<ClassInfo>> parsedShard = gson.fromJson(jsonContent, parsedType);
for (Map.Entry<String, List<ClassInfo>> shardEntry : parsedShard.entrySet()) {
parsed
// if the key didn't exist, create a new list
.computeIfAbsent(shardEntry.getKey(), __ -> new ArrayList<>())
.addAll(shardEntry.getValue()); // add values to the key
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Defensive null handling when merging JSON shards

gson.fromJson(...) or shardEntry.getValue() can be null; current loop would NPE. Guard before merging.

-      try {
+      try {
         String jsonContent = Files.readString(jsonFile.toPath());
-        Map<String, List<ClassInfo>> parsedShard = gson.fromJson(jsonContent, parsedType);
-        for (Map.Entry<String, List<ClassInfo>> shardEntry : parsedShard.entrySet()) {
+        Map<String, List<ClassInfo>> parsedShard = gson.fromJson(jsonContent, parsedType);
+        if (parsedShard == null) {
+          continue;
+        }
+        for (Map.Entry<String, List<ClassInfo>> shardEntry : parsedShard.entrySet()) {
+          if (shardEntry.getValue() == null) {
+            continue;
+          }
           parsed
               // if the key didn't exist, create a new list
               .computeIfAbsent(shardEntry.getKey(), __ -> new ArrayList<>())
               .addAll(shardEntry.getValue()); // add values to the key
         }
       } catch (IOException e) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// parse JSON file
Map<String, List<ClassInfo>> parsed = new HashMap<>();
for (File jsonFile : jsonFiles) {
try {
String jsonContent = Files.readString(jsonFile.toPath());
Map<String, List<ClassInfo>> parsedShard = gson.fromJson(jsonContent, parsedType);
for (Map.Entry<String, List<ClassInfo>> shardEntry : parsedShard.entrySet()) {
parsed
// if the key didn't exist, create a new list
.computeIfAbsent(shardEntry.getKey(), __ -> new ArrayList<>())
.addAll(shardEntry.getValue()); // add values to the key
}
// parse JSON file
Map<String, List<ClassInfo>> parsed = new HashMap<>();
for (File jsonFile : jsonFiles) {
try {
String jsonContent = Files.readString(jsonFile.toPath());
Map<String, List<ClassInfo>> parsedShard = gson.fromJson(jsonContent, parsedType);
if (parsedShard == null) {
continue;
}
for (Map.Entry<String, List<ClassInfo>> shardEntry : parsedShard.entrySet()) {
if (shardEntry.getValue() == null) {
continue;
}
parsed
// if the key didn't exist, create a new list
.computeIfAbsent(shardEntry.getKey(), __ -> new ArrayList<>())
.addAll(shardEntry.getValue()); // add values to the key
}
} catch (IOException e) {
// existing error handling...
}
}
🤖 Prompt for AI Agents
In
jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
around lines 170 to 181, the code assumes gson.fromJson(...) returns a non-null
parsedShard and that shardEntry.getValue() is non-null, which can cause NPEs;
modify the loop to first check if parsedShard is null and skip that file if so,
and then for each shardEntry skip entries whose value is null (or treat them as
empty lists) before calling computeIfAbsent(...).addAll(...), ensuring you only
call addAll when the value is non-null.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1)

22-330: LGTM! Comprehensive integration test coverage.

The test suite provides excellent coverage of JDK annotation scenarios including nullable returns, parameters, arrays, generics, and inner classes. The tests correctly verify both positive cases (expecting errors) and negative cases (no errors expected), and include validation of flag behavior (JarInferEnabled, JSpecifyMode).

Optional: Consider extracting common argument setup to reduce duplication.

Many tests share similar argument lists. Consider extracting a helper method to reduce duplication:

private List<String> standardArgs(boolean jarInferEnabled, boolean jspecifyMode) {
  List<String> args = new ArrayList<>(Arrays.asList(
      "-d",
      temporaryFolder.getRoot().getAbsolutePath(),
      "-XepOpt:NullAway:AnnotatedPackages=com.uber"));
  if (jarInferEnabled) {
    args.add("-XepOpt:NullAway:JarInferEnabled=true");
  }
  if (jspecifyMode) {
    args.add("-XepOpt:NullAway:JSpecifyMode=true");
  }
  return args;
}

This would make tests more concise and easier to maintain:

compilationHelper
    .setArgs(standardArgs(true, false))
    .addSourceLines(...)
    .doTest();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 148cae4 and 1640a9e.

📒 Files selected for processing (3)
  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1 hunks)
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (1 hunks)
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ReturnAnnotation.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
🔇 Additional comments (1)
jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1)

1-20: LGTM! Test setup is properly configured.

The test class is correctly configured with CompilationTestHelper and uses a TemporaryFolder for compilation artifacts. The setup follows standard patterns for Error Prone compilation tests.

@msridhar msridhar changed the title Jdk javac Generate astubx from JSON output of jdk-javac-plugin Oct 15, 2025
@msridhar msridhar merged commit b4b272b into uber:master Oct 15, 2025
9 of 11 checks passed
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.

2 participants