Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@haewiful
Copy link
Contributor

@haewiful haewiful commented Dec 5, 2025

This pull request adds method type parameter upper bound information to astubx files and updates the JSON-to-astubx generator accordingly.

The unit tests are located in NullAway/jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java and NullAway/jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java.

Note that the JarInfer code has not been updated; that needs to be done in a follow-up, but for now, our distributed JarInfer astubx files for the Android library are out of date and won't work.

Summary by CodeRabbit

  • New Features

    • Detects and records nullable upper bounds on generic type parameters.
  • Refactor

    • Method annotation records extended to include type-parameter nullable-upper-bound data (public API changed).
    • Serialization/deserialization and model generation updated to emit/load new per-method type-parameter metadata.
  • Tests

    • Added and updated unit and integration tests to cover type-parameter nullability and the new record shape.
  • Chores

    • Adjusted test dependency comments for integration tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@haewiful haewiful changed the title Include method type parameters in the astubx files. Include method type parameters in astubx files. Dec 5, 2025
@msridhar msridhar marked this pull request as ready for review December 6, 2025 18:25
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 6, 2025

Walkthrough

Adds tracking and emission of per-method nullable upper bounds for type parameters and extends MethodAnnotationsRecord with a new ImmutableSet typeParamNullableUpperbounds field and accessor. Updated usages to the new three-argument MethodAnnotationsRecord.create(...) everywhere (AstubxGenerator, LibraryModelGenerator, DefinitelyDerefedParamsDriver, tests). StubxWriter now emits an additional block of method-type-parameter nullable-upper-bound entries and StubxCacheUtil reads and caches that block. LibraryModelsHandler/ExternalStubxLibraryModels expose the cached mappings. New unit and integration tests cover nullable type-parameter upper-bound scenarios.

Possibly related PRs

Suggested reviewers

  • yuxincs
  • lazaroclapp

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.16% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately describes the main objective of the pull request: adding method type parameter information to astubx files.
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
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: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6165b8e and 23f1482.

📒 Files selected for processing (9)
  • jar-infer/jar-infer-lib/src/main/java/com/uber/nullaway/jarinfer/DefinitelyDerefedParamsDriver.java (1 hunks)
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (2 hunks)
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (10 hunks)
  • 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)
  • library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/MethodAnnotationsRecord.java (1 hunks)
  • library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/StubxWriter.java (2 hunks)
  • nullaway/src/main/java/com/uber/nullaway/handlers/StubxCacheUtil.java (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: msridhar
Repo: uber/NullAway PR: 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.
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.
Learnt from: msridhar
Repo: uber/NullAway PR: 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
Repo: uber/NullAway PR: 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.
📚 Learning: 2025-10-29T23:56:18.236Z
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.

Applied to files:

  • library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/MethodAnnotationsRecord.java
  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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:

  • library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/MethodAnnotationsRecord.java
  • jar-infer/jar-infer-lib/src/main/java/com/uber/nullaway/jarinfer/DefinitelyDerefedParamsDriver.java
  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java
⏰ 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). (1)
  • GitHub Check: Build and test on windows-latest
🔇 Additional comments (11)
jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)

51-55: LGTM!

The new generic test methods are appropriate for exercising type parameter handling in unannotated library code scenarios.

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

357-384: LGTM!

The test comprehensively exercises method type parameter handling with appropriate positive and negative test cases. The expected diagnostics align with the nullable and non-null type parameter semantics.

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

57-59: LGTM with observation.

The methods correctly test type parameter nullable upper bounds. Note that the @Nullable T t parameter annotation may be redundant since T extends @Nullable String already establishes T's nullability at the bound level. This could be intentional to test both bound-level and use-site annotations.

Based on learnings, type variable bounds with annotations are checked at their declaration sites separately from type uses.

jdk-annotations/astubx-generator/src/main/java/com/uber/nullaway/jdkannotations/AstubxGenerator.java (2)

207-215: LGTM!

The logic correctly detects nullable upper bounds for method type parameters by checking for @Nullable annotations in the bounds. This mirrors the pattern used for class-level type parameters (lines 97-109).


250-253: LGTM!

The MethodAnnotationsRecord.create call is correctly updated to include the nullable type parameter upper bounds as the second argument.

jar-infer/jar-infer-lib/src/main/java/com/uber/nullaway/jarinfer/DefinitelyDerefedParamsDriver.java (2)

454-460: LGTM!

The MethodAnnotationsRecord.create call is correctly updated to include an empty set for type parameter nullable upper bounds (second argument). This is appropriate since jar-infer doesn't infer type parameter nullability.


463-466: LGTM!

The MethodAnnotationsRecord.create call for nullable return methods is correctly updated with the empty set parameter.

library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/MethodAnnotationsRecord.java (1)

11-23: Breaking API change: verify all callers are updated.

The create method signature changes from 2 parameters to 3 parameters by adding typeParamNullableUpperbounds. This is a breaking change that requires all callers to be updated. Ensure all usages of MethodAnnotationsRecord.create() pass the new typeParamNullableUpperbounds parameter.

library-model/library-model-generator/src/main/java/com/uber/nullaway/libmodel/StubxWriter.java (1)

107-129: Verify read/write order in StubxCacheUtil matches StubxWriter before merging.

The logic correctly aggregates and emits method type parameter nullable upper bound information in the astubx format. The implementation follows the established pattern for other metadata sections.

The new section is written between method annotations and argument records. Confirm that StubxCacheUtil reads these sections in the same order: method annotations first, then type parameter nullable bounds, then argument records. A mismatch in read/write order would cause data corruption.

jdk-annotations/astubx-generator/src/test/java/com/uber/nullaway/jdkannotations/AstubxTest.java (2)

68-69: LGTM: Consistent API migration across all existing tests.

The updates to use the new 3-argument MethodAnnotationsRecord.create signature are consistent and correct. The empty ImmutableSet.of() as the second parameter is appropriate for tests that don't involve method-level type parameters with nullable upper bounds.

Also applies to: 94-95, 125-126, 182-184, 209-211, 238-240, 262-264, 290-291, 316-318, 342-344


348-366: Well-structured test for method-level type parameters.

The test correctly validates:

  • The new method signature format with type parameters: void <K,T>nullableTypeVar()
  • Type parameter index tracking: ImmutableSet.of(1) correctly identifies T (0-indexed) as having the nullable upper bound
  • Only the method with nullable type parameter bounds is recorded; nonNullTypeVar is correctly excluded

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.

A few comments

System.out.println(objects);
}

public static <K, T extends @Nullable String> T nullableTypeParam(K k, @Nullable T t) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You shouldn't need to write @Nullable T t, you should just write T t. If a @Nullable argument is passed, that should be allowed, and NullAway should figure out that then the return type is also @Nullable.

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: 4

♻️ Duplicate comments (2)
jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java (1)

376-376: Missing error expectation based on maintainer feedback.

According to the previous review comment, NullAway should report an error on this line because the return type of nullableTypeParam(1, null) is inferred to be @Nullable, and then toString() is called on it. The test should expect this error.

Apply this diff to add the expected error:

+    // BUG: Diagnostic contains: dereferenced expression
     ParameterAnnotation.nullableTypeParam(1, null).toString();
nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (1)

1388-1388: Fix typo in variable name.

The variable name methodTypeParamNullableUpperBoundCache contains the same typo as in StubxCacheUtil.java. Once the typo is fixed in the cache utility, update the reference here accordingly.

This will be fixed when the corresponding change is made in StubxCacheUtil.java.

Also applies to: 1415-1416

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7fd7ee and 3c12c19.

📒 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/ParameterAnnotation.java (1 hunks)
  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (3 hunks)
  • nullaway/src/main/java/com/uber/nullaway/handlers/StubxCacheUtil.java (4 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: msridhar
Repo: uber/NullAway PR: 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.
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.
Learnt from: msridhar
Repo: uber/NullAway PR: 1276
File: nullaway/src/main/java/com/uber/nullaway/generics/TypeMetadataBuilder.java:235-241
Timestamp: 2025-09-03T19:40:11.118Z
Learning: In JDK 21+, ListBuffer can be passed directly to TypeMetadata.Annotations constructor without needing conversion to List via toList() method. The APIs were updated to make this compatible.
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • nullaway/src/main/java/com/uber/nullaway/handlers/StubxCacheUtil.java
  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
📚 Learning: 2025-10-29T23:56:18.236Z
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.

Applied to files:

  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
📚 Learning: 2025-08-29T18:41:43.584Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
📚 Learning: 2025-11-25T22:43:06.446Z
Learnt from: CR
Repo: uber/NullAway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T22:43:06.446Z
Learning: Run only the tests for the main NullAway module using `./gradlew :nullaway:test` unless specifically asked to run tests in a different module

Applied to files:

  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
🧬 Code graph analysis (1)
nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (1)
nullaway/src/main/java/com/uber/nullaway/LibraryModels.java (1)
  • MethodRef (211-314)
🔇 Additional comments (2)
jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java (1)

57-61: LGTM! Type parameter declarations are correct.

The method type parameter bounds are properly declared. The nullable upper bound on T extends @Nullable String in nullableTypeParam enables testing of the new nullable type parameter tracking functionality.

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

357-386: The test covers basic type parameter scenarios but may lack comprehensive validation of nullable upper bound return value handling.

The test validates that nullableTypeParam<T extends @Nullable String> correctly accepts null/non-null values for the parameter and that nonNullTypeParam<T> rejects null. However, test cases 1-2 dereference the return value (.toString()) without expecting errors despite the return type being nullable-bounded. Verify whether dereferencing these nullable return values should trigger errors per JSpecify semantics, or if the current test expectations are correct per the type parameter bound rules being implemented.

Comment on lines 1434 to 1446
@Override
public ImmutableSetMultimap<MethodRef, Integer> methodTypeVariablesWithNullableUpperBounds() {
ImmutableSetMultimap.Builder<MethodRef, Integer> mapBuilder =
new ImmutableSetMultimap.Builder<>();
for (Map.Entry<String, Integer> entry : methodTypeParamNullableUpperBoundCache.entrySet()) {
// String methodSig = entry.getKey();
String className = entry.getKey().split(":")[0].replace('$', '.');
String methodSig = getMethodNameAndSignature(entry.getKey());

mapBuilder.put(MethodRef.methodRef(className, methodSig), entry.getValue());
}
return mapBuilder.build();
}
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

Update implementation to handle multiple nullable type parameters per method.

This implementation assumes the cache is Map<String, Integer>, which can only store one nullable type parameter index per method. Once StubxCacheUtil.java is updated to use Map<String, Set<Integer>> (to support methods with multiple nullable type parameters), this code must be updated to iterate over the set of indices.

After fixing the cache structure in StubxCacheUtil.java, update this method:

 @Override
 public ImmutableSetMultimap<MethodRef, Integer> methodTypeVariablesWithNullableUpperBounds() {
   ImmutableSetMultimap.Builder<MethodRef, Integer> mapBuilder =
       new ImmutableSetMultimap.Builder<>();
   for (Map.Entry<String, Integer> entry : methodTypeParamNullableUpperBoundCache.entrySet()) {
-    //        String methodSig = entry.getKey();
     String className = entry.getKey().split(":")[0].replace('$', '.');
     String methodSig = getMethodNameAndSignature(entry.getKey());
-
-    mapBuilder.put(MethodRef.methodRef(className, methodSig), entry.getValue());
+    
+    MethodRef methodRef = MethodRef.methodRef(className, methodSig);
+    for (Integer idx : entry.getValue()) {
+      mapBuilder.put(methodRef, idx);
+    }
   }
   return mapBuilder.build();
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
around lines 1434 to 1446, the current code assumes
methodTypeParamNullableUpperBoundCache is Map<String, Integer> and only adds a
single index per method; after changing the cache to Map<String, Set<Integer>>
you must iterate the Set<Integer> for each entry and call
mapBuilder.put(MethodRef.methodRef(className, methodSig), index) for each index
in the set (instead of using entry.getValue() directly), ensuring all nullable
type parameter indices for a method are added to the ImmutableSetMultimap.

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 (3)
nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (3)

1388-1388: Cache structure must support multiple nullable type parameter indices per method.

The cache is declared as Map<String, Integer>, which can only store one nullable type parameter index per method. Methods can have multiple type parameters with nullable upper bounds (e.g., <T extends @Nullable Object, U extends @Nullable Object>), so the cache must be changed to Map<String, Set<Integer>>.

This limitation affects the methodTypeVariablesWithNullableUpperBounds() implementation at lines 1434-1446, which must also be updated to iterate over the set of indices.

🔎 Apply this diff to fix the cache structure:
-    private final Map<String, Integer> methodTypeParamNullableUpperBoundCache;
+    private final Map<String, Set<Integer>> methodTypeParamNullableUpperBoundCache;

1439-1439: Remove commented-out code.

Line 1439 contains commented-out code that should be removed to keep the codebase clean.

🔎 Apply this diff:
   for (Map.Entry<String, Integer> entry : methodTypeParamNullableUpperBoundCache.entrySet()) {
-    //        String methodSig = entry.getKey();
     String className = entry.getKey().split(":")[0].replace('$', '.');

1434-1446: Update implementation to iterate over multiple nullable type parameter indices.

Once the cache structure is changed from Map<String, Integer> to Map<String, Set<Integer>> (as noted in the comment on line 1388), this method must be updated to iterate over each set of indices and add them individually to the builder.

🔎 After fixing the cache structure, apply this diff:
 @Override
 public ImmutableSetMultimap<MethodRef, Integer> methodTypeVariablesWithNullableUpperBounds() {
   ImmutableSetMultimap.Builder<MethodRef, Integer> mapBuilder =
       new ImmutableSetMultimap.Builder<>();
-  for (Map.Entry<String, Integer> entry : methodTypeParamNullableUpperBoundCache.entrySet()) {
+  for (Map.Entry<String, Set<Integer>> entry : methodTypeParamNullableUpperBoundCache.entrySet()) {
     String className = entry.getKey().split(":")[0].replace('$', '.');
     String methodSig = getMethodNameAndSignature(entry.getKey());
-
-    mapBuilder.put(MethodRef.methodRef(className, methodSig), entry.getValue());
+    
+    MethodRef methodRef = MethodRef.methodRef(className, methodSig);
+    for (Integer idx : entry.getValue()) {
+      mapBuilder.put(methodRef, idx);
+    }
   }
   return mapBuilder.build();
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c12c19 and af14e4a.

📒 Files selected for processing (1)
  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (3 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: msridhar
Repo: uber/NullAway PR: 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.
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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:

  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
📚 Learning: 2025-10-29T23:56:18.236Z
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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:

  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java

@codecov
Copy link

codecov bot commented Dec 18, 2025

Codecov Report

❌ Patch coverage is 96.29630% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.47%. Comparing base (03833a5) to head (a7b9c58).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
.../uber/nullaway/jdkannotations/AstubxGenerator.java 90.90% 0 Missing and 1 partial ⚠️
.../uber/nullaway/libmodel/LibraryModelGenerator.java 92.85% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             master    #1370   +/-   ##
=========================================
  Coverage     88.46%   88.47%           
- Complexity     2619     2626    +7     
=========================================
  Files            97       97           
  Lines          8774     8822   +48     
  Branches       1751     1762   +11     
=========================================
+ Hits           7762     7805   +43     
- Misses          504      509    +5     
  Partials        508      508           

☔ 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
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)
nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (1)

1440-1440: Remove commented-out code.

The commented-out line should be removed to keep the codebase clean, as noted in previous reviews.

Apply this diff
   for (Map.Entry<String, Integer> entry : methodTypeParamNullableUpperBoundCache.entries()) {
-    //        String methodSig = entry.getKey();
     String className = entry.getKey().split(":")[0].replace('$', '.');
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf540b and a4ab247.

📒 Files selected for processing (5)
  • 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)
  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (4 hunks)
  • nullaway/src/main/java/com/uber/nullaway/handlers/StubxCacheUtil.java (5 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: msridhar
Repo: uber/NullAway PR: 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.
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
📚 Learning: 2025-08-29T18:41:43.584Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
📚 Learning: 2025-11-25T22:43:06.446Z
Learnt from: CR
Repo: uber/NullAway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T22:43:06.446Z
Learning: Run only the tests for the main NullAway module using `./gradlew :nullaway:test` unless specifically asked to run tests in a different module

Applied to files:

  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
📚 Learning: 2025-10-29T23:56:18.236Z
Learnt from: msridhar
Repo: uber/NullAway PR: 1316
File: jdk-javac-plugin/src/main/java/com/uber/nullaway/javacplugin/NullnessAnnotationSerializer.java:261-293
Timestamp: 2025-10-29T23:56:18.236Z
Learning: In NullAway's jdk-javac-plugin NullnessAnnotationSerializer, type variable bounds with annotations (e.g., `T extends Nullable Object`) are checked at their declaration sites by the typeParamHasAnnotation method for both class-level and method-level type parameters. The hasJSpecifyAnnotationDeep method is designed to check type uses (return types, parameters, etc.) and does not need a TYPEVAR case because type variable declaration bounds are already handled separately.

Applied to files:

  • jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
  • jdk-annotations/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
Repo: uber/NullAway PR: 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/test-annotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • jdk-annotations/test-unannotated/src/main/java/com/uber/nullaway/jdkannotations/ParameterAnnotation.java
  • nullaway/src/main/java/com/uber/nullaway/handlers/StubxCacheUtil.java
  • nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java
🔇 Additional comments (10)
nullaway/src/main/java/com/uber/nullaway/handlers/StubxCacheUtil.java (4)

25-27: LGTM! Data structure correctly supports multiple nullable type parameters per method.

The use of SetMultimap<String, Integer> properly addresses the requirement to track multiple nullable type parameter indices per method, as noted in previous reviews. The SetMultimap.put() operation accumulates values rather than overwriting, ensuring no data loss when a method has multiple type parameters with nullable upper bounds.

Also applies to: 73-73


87-87: LGTM! Initialization is correct.

The HashMultimap.create() initialization is appropriate for the declared SetMultimap type.


100-102: LGTM! Getter correctly exposes the cache.

The getter returns Multimap<String, Integer>, which is consumed by LibraryModelsHandler at line 1417 to populate method-level nullable upper-bound mappings.


171-177: LGTM! Parsing logic correctly populates the cache for multiple type parameters.

The loop reads each (methodSig, idx) pair and uses SetMultimap.put(), which accumulates multiple indices per method signature without overwriting. This correctly handles methods with multiple type parameters that have nullable upper bounds (e.g., <T extends @Nullable Object, U extends @Nullable Object>).

nullaway/src/main/java/com/uber/nullaway/handlers/LibraryModelsHandler.java (4)

35-35: LGTM! Import added for Multimap usage.


1389-1389: LGTM! Field type matches the cache getter.

The Multimap<String, Integer> type correctly matches the return type of StubxCacheUtil.getMethodTypeParamNullableUpperBoundCache().


1416-1417: LGTM! Initialization correctly retrieves the cache.


1435-1447: LGTM! Implementation correctly handles multiple nullable type parameters per method.

The code iterates over methodTypeParamNullableUpperBoundCache.entries(), which returns all key-value pairs from the Multimap. When a method has multiple nullable type parameter indices, each (methodSig, idx) pair appears as a separate entry, and all are correctly added to the resulting ImmutableSetMultimap. This addresses the concern raised in previous reviews about supporting methods with multiple nullable type parameters.

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

57-66: LGTM! Method signatures correctly use nullable upper bounds.

The three methods properly demonstrate the new functionality:

  1. nullableTypeParam (lines 57-59): Tests nullable upper bound on single type parameter
  2. twoNullableTypeParam (lines 61-64): Tests multiple nullable upper bounds
  3. nonNullTypeParam (line 66): Provides contrast with non-nullable bound

The implementations correctly place @Nullable on the type parameter bounds (T extends @Nullable String) rather than on the parameter declarations themselves (T t not @Nullable T t), which aligns with the past review feedback and JSpecify semantics.

Based on learnings from previous reviews, these method-level type parameter bounds will be captured by the typeParamHasAnnotation logic during stub generation.

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

51-59: LGTM! Test fixtures for method type parameter handling in astubx generation.

These generic methods appropriately test different type parameter scenarios (bounded vs. unbounded, single vs. multiple type parameters) for the astubx generation feature. The unused parameters and simple implementations are typical and expected for test utility methods. All three methods are actively used in JDKIntegrationTest.java to validate nullability inference on method type parameters and their bounds.

Comment on lines +357 to +390
@Test
public void loadLibraryModuleMethodTypeParam() {
compilationHelper
.setArgs(
Arrays.asList(
"-d",
temporaryFolder.getRoot().getAbsolutePath(),
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
"-XepOpt:NullAway:JarInferEnabled=true",
"-XepOpt:NullAway:JSpecifyMode=true",
"-XDaddTypeAnnotationsToSymbol=true"))
.addSourceLines(
"Test.java",
"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"import com.uber.nullaway.jdkannotations.ParameterAnnotation;",
"import java.util.List;",
"class Test {",
" void testCall() {",
" // BUG: Diagnostic contains: dereferenced expression ParameterAnnotation.nullableTypeParam(1, null) is @Nullable",
" ParameterAnnotation.nullableTypeParam(1, null).toString();",
" ParameterAnnotation.nullableTypeParam(1, \"string\").toString();",
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
" ParameterAnnotation.nullableTypeParam(null, \"string\");",
" ParameterAnnotation.nonNullTypeParam(1);",
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
" ParameterAnnotation.nonNullTypeParam(null);",
" Object x = ParameterAnnotation.twoNullableTypeParam(null, \"string\");",
" // BUG: Diagnostic contains: dereferenced expression x is @Nullable",
" x.toString();",
" }",
"}")
.doTest();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Well-structured test for method type parameter nullable bounds.

The test comprehensively covers the new functionality:

  • Line 377: correctly expects error when dereferencing nullable return (null argument passed)
  • Line 378: correctly expects no error with non-null argument (flow-sensitive inference)
  • Lines 380, 383: verify null-checking for non-nullable type parameters
  • Lines 384-386: test multiple nullable type parameters

The test correctly demonstrates that NullAway infers return-type nullability based on the actual arguments passed to methods with nullable type parameter bounds.

Optional: Add clarifying comment for line 378

Consider adding a brief comment for line 378 to document the expected behavior:

 ParameterAnnotation.nullableTypeParam(1, null).toString();
+// No error expected: non-null argument passed, so return type inferred as non-null
 ParameterAnnotation.nullableTypeParam(1, "string").toString();

This helps readers understand why line 377 errors but line 378 doesn't.

📝 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
@Test
public void loadLibraryModuleMethodTypeParam() {
compilationHelper
.setArgs(
Arrays.asList(
"-d",
temporaryFolder.getRoot().getAbsolutePath(),
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
"-XepOpt:NullAway:JarInferEnabled=true",
"-XepOpt:NullAway:JSpecifyMode=true",
"-XDaddTypeAnnotationsToSymbol=true"))
.addSourceLines(
"Test.java",
"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"import com.uber.nullaway.jdkannotations.ParameterAnnotation;",
"import java.util.List;",
"class Test {",
" void testCall() {",
" // BUG: Diagnostic contains: dereferenced expression ParameterAnnotation.nullableTypeParam(1, null) is @Nullable",
" ParameterAnnotation.nullableTypeParam(1, null).toString();",
" ParameterAnnotation.nullableTypeParam(1, \"string\").toString();",
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
" ParameterAnnotation.nullableTypeParam(null, \"string\");",
" ParameterAnnotation.nonNullTypeParam(1);",
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
" ParameterAnnotation.nonNullTypeParam(null);",
" Object x = ParameterAnnotation.twoNullableTypeParam(null, \"string\");",
" // BUG: Diagnostic contains: dereferenced expression x is @Nullable",
" x.toString();",
" }",
"}")
.doTest();
}
@Test
public void loadLibraryModuleMethodTypeParam() {
compilationHelper
.setArgs(
Arrays.asList(
"-d",
temporaryFolder.getRoot().getAbsolutePath(),
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
"-XepOpt:NullAway:JarInferEnabled=true",
"-XepOpt:NullAway:JSpecifyMode=true",
"-XDaddTypeAnnotationsToSymbol=true"))
.addSourceLines(
"Test.java",
"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"import com.uber.nullaway.jdkannotations.ParameterAnnotation;",
"import java.util.List;",
"class Test {",
" void testCall() {",
" // BUG: Diagnostic contains: dereferenced expression ParameterAnnotation.nullableTypeParam(1, null) is @Nullable",
" ParameterAnnotation.nullableTypeParam(1, null).toString();",
" // No error expected: non-null argument passed, so return type inferred as non-null",
" ParameterAnnotation.nullableTypeParam(1, \"string\").toString();",
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
" ParameterAnnotation.nullableTypeParam(null, \"string\");",
" ParameterAnnotation.nonNullTypeParam(1);",
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
" ParameterAnnotation.nonNullTypeParam(null);",
" Object x = ParameterAnnotation.twoNullableTypeParam(null, \"string\");",
" // BUG: Diagnostic contains: dereferenced expression x is @Nullable",
" x.toString();",
" }",
"}")
.doTest();
}
🤖 Prompt for AI Agents
In
jdk-annotations/jdk-integration-test/src/test/java/com/uber/nullaway/jdkannotations/JDKIntegrationTest.java
around lines 357 to 390, add a brief clarifying inline comment on the line with
ParameterAnnotation.nullableTypeParam(1, "string").toString(); (currently line
~378) explaining that this call should produce no diagnostic because the
non-null actual argument lets NullAway infer a non-null return (flow-sensitive
inference), so readers understand why the previous line errors but this one does
not.

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.

Thanks!

Comment on lines +385 to +399
private ImmutableSet<Integer> getMethodTypeVariableNullableUpperBounds(MethodDeclaration md) {
ImmutableSet.Builder<Integer> setBuilder = ImmutableSet.builder();
List<TypeParameter> typeParameterList = md.getTypeParameters();
for (int i = 0; i < typeParameterList.size(); i++) {
TypeParameter param = typeParameterList.get(i);
for (ClassOrInterfaceType type : param.getTypeBound()) {
Optional<AnnotationExpr> nullableAnnotation = type.getAnnotationByName(NULLABLE);
if (nullableAnnotation.isPresent() && isAnnotationNullable(nullableAnnotation.get())) {
setBuilder.add(i);
}
}
}
return setBuilder.build();
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok to add this for now, but in a follow-up PR we should just delete this class. I think we're going with the new javac plugin approach at this point

@msridhar msridhar enabled auto-merge (squash) December 18, 2025 23:21
@msridhar msridhar merged commit 824353e into uber:master Dec 18, 2025
10 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