Skip to content

JSpecify: Improve handling of method references - #1430

Merged
msridhar merged 23 commits into
masterfrom
method-ref-improve
Jan 14, 2026
Merged

msridhar merged 23 commits into
masterfrom
method-ref-improve

Conversation

@msridhar

@msridhar msridhar commented Jan 7, 2026 •

Copy link
Copy Markdown
Collaborator

We now infer the type of a method reference passed to a generic method based on the results of our generic method inference, similar to lambda expressions, and use this inferred type in appropriate places. Rewrite some of the code that previously referenced lambdas to now more generically reference poly expressions instead.

This is not full support, as we don't use method reference expressions in the inference process yet; see #1431. But that will be handled in a follow up.

Fixes #1128
Fixes #1307

Summary by CodeRabbit

  • New Features

    • Improved generic type inference for poly expressions (lambdas and method references), yielding more accurate nullness checks for parameters and returns.
  • Tests

    • Added tests for explicitly annotated lambda arguments and multiple method-reference scenarios.
    • Renamed and expanded test coverage for generic lambdas and method-reference cases.

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

@codecov

codecov Bot commented Jan 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.37%. Comparing base (0b4156b) to head (6abdacf).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...ava/com/uber/nullaway/generics/GenericsChecks.java 85.71% 0 Missing and 3 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1430      +/-   ##
============================================
- Coverage     88.38%   88.37%   -0.01%     
- Complexity     2674     2680       +6     
============================================
  Files            97       97              
  Lines          8866     8878      +12     
  Branches       1773     1777       +4     
============================================
+ Hits           7836     7846      +10     
  Misses          511      511              
- Partials        519      521       +2     

☔ 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.

@msridhar
msridhar marked this pull request as ready for review January 8, 2026 02:13
@coderabbitai

coderabbitai Bot commented Jan 8, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

This change generalizes generics inference for poly-expressions by replacing lambda-only inference with a unified approach for LambdaExpressionTree and MemberReferenceTree. GenericsChecks renames getInferredLambdaType(...) to getInferredPolyExpressionType(...) and replaces the inferredLambdaTypes map with inferredPolyExpressionTypes. Call sites in NullAway and CoreNullnessStoreInitializer now prefer GenericsChecks.getInferredPolyExpressionType(...) (with ASTHelpers.getType fallback) when resolving generic nullness for lambda and method-reference parameter and return checks. Tests were added/updated for method references and annotated lambdas.

Possibly related PRs

  • PR 1348: Modifies GenericsChecks to obtain and use compiler-inferred types for lambdas; related to this change’s generalization to poly-expressions.
  • PR 1428: Adjusts GenericsChecks’ lambda/type-inference caching and storage/restoration; overlaps with the new inferredPolyExpressionTypes cache changes.
  • PR 1312: Evolves inferred-lambda caching and updates call sites such as CoreNullnessStoreInitializer; closely related to the API/usage adjustments here.

Suggested reviewers

  • yuxincs
  • lazaroclapp
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'JSpecify: Improve handling of method references' accurately and concisely describes the main change in the pull request, which focuses on improving how method references are handled in generic contexts.
Linked Issues check ✅ Passed The PR successfully addresses both linked issues by inferring method reference types from generic method type inference results and using those inferred types in nullness checks, matching lambda expression treatment.
Out of Scope Changes check ✅ Passed All changes are directly related to improving method reference handling in generic contexts as specified in the linked issues; no unrelated modifications were identified.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent 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 6afc36b and 6abdacf.

📒 Files selected for processing (1)
  • nullaway/src/main/java/com/uber/nullaway/NullAway.java
🧰 Additional context used
🧠 Learnings (7)
📓 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.
📚 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/NullAway.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/NullAway.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:

  • nullaway/src/main/java/com/uber/nullaway/NullAway.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/NullAway.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:

  • nullaway/src/main/java/com/uber/nullaway/NullAway.java
📚 Learning: 2025-12-31T23:59:20.009Z
Learnt from: msridhar
Repo: uber/NullAway PR: 1421
File: nullaway/src/main/java/com/uber/nullaway/NullAway.java:339-340
Timestamp: 2025-12-31T23:59:20.009Z
Learning: In Error Prone's ASTHelpers class, the getSymbol(MethodInvocationTree) overload is guaranteed to return a non-null Symbol.MethodSymbol. This differs from other overloads of getSymbol() that may return null. Therefore, no null check is required after calling ASTHelpers.getSymbol() with a MethodInvocationTree parameter.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/NullAway.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). (5)
  • GitHub Check: Build caffeine with snapshot
  • GitHub Check: Build and test on ubuntu-latest
  • GitHub Check: Build and test on macos-latest
  • GitHub Check: Build and test on windows-latest
  • GitHub Check: Build spring-framework with snapshot
🔇 Additional comments (3)
nullaway/src/main/java/com/uber/nullaway/NullAway.java (3)

835-847: LGTM! Unified handling for lambda and method reference parameter nullness.

The change correctly extends the existing lambda type inference pattern to method references. The castToNonNull is safe since the enclosing condition at line 834 guarantees at least one of memberReferenceTree or lambdaExpressionTree is non-null. The fallback to ASTHelpers.getType when the inferred type is unavailable maintains backward compatibility.


1010-1014: LGTM! API rename for unified poly expression type inference.

The change from getInferredLambdaType to getInferredPolyExpressionType aligns with the new unified approach while preserving existing behavior for lambda return type checking.


1180-1190: LGTM! Key improvement for method reference return type nullness.

This is the core change that addresses issues #1128 and #1307. By using getInferredPolyExpressionType for method references (with fallback to ASTHelpers.getType), the code now properly preserves @Nullable annotations on method-reference-derived functional types. This mirrors the treatment already used for lambda expressions and ensures consistent nullness inference for both poly expression types.


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.

@msridhar msridhar changed the title [draft] JSpecify: Improve handling of method references JSpecify: Improve handling of method references Jan 8, 2026
@github-actions

github-actions Bot commented Jan 8, 2026

Copy link
Copy Markdown

Main Branch:

Benchmark                          Mode  Cnt   Score   Error  Units
AutodisposeBenchmark.compile      thrpt   25  10.594 ± 0.068  ops/s
CaffeineBenchmark.compile         thrpt   25   2.026 ± 0.012  ops/s
DFlowMicroBenchmark.compile       thrpt   25  41.523 ± 0.482  ops/s
NullawayReleaseBenchmark.compile  thrpt   25   1.667 ± 0.017  ops/s

With This PR:

Benchmark                          Mode  Cnt   Score   Error  Units
AutodisposeBenchmark.compile      thrpt   25  10.585 ± 0.058  ops/s
CaffeineBenchmark.compile         thrpt   25   2.014 ± 0.011  ops/s
DFlowMicroBenchmark.compile       thrpt   25  41.778 ± 0.415  ops/s
NullawayReleaseBenchmark.compile  thrpt   25   1.676 ± 0.025  ops/s

@msridhar
msridhar requested a review from yuxincs January 8, 2026 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Better support method reference parameters in generic method inference Undetected @Nullable annotation in a method reference

2 participants