Skip to content

Better handling of method references passed to generic methods in JSpecify mode - #1499

Merged
msridhar merged 21 commits into
masterfrom
codex/add-test-and-fix-nullaway-issue-1431
Apr 3, 2026
Merged

msridhar merged 21 commits into
masterfrom
codex/add-test-and-fix-nullaway-issue-1431

Conversation

@msridhar

@msridhar msridhar commented Mar 25, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #1431
Fixes #1474

We make two key changes. First, we store the inferred types of method reference expressions in the inferredPolyExpressionTypes cache, similar to what we did for lambdas, so that some later checks that require the type of the method reference get the inferred type.

Second, when a method reference is passed to a generic method, sometimes the type for the method reference computed by javac does not include nullability annotations on nested types (see #1474 and a new test case here). So, we need to do explicit checks using the return and parameter types of the referenced method, to see if nested nullability annotations are consistent with the target functional-interface method type.

For code reuse and clarity, we extract a utility method for doing type checks related to method references to a new class GenericsUtils, and rewrite one use of the original logic to use the new utility.

Summary by CodeRabbit

  • New Features

    • Added nullability checks for method references when used with generic type inference.
  • Improvements

    • Brought method-reference type handling in line with lambda expressions, improving detection of parameter and return nullability mismatches, including varargs and explicit type-argument scenarios.
  • Tests

    • Added tests covering method-reference interactions with generic inference and expected nullability diagnostics.

@codecov

codecov Bot commented Mar 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.59649% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.45%. Comparing base (f4dcfc9) to head (9c5c926).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...java/com/uber/nullaway/generics/GenericsUtils.java 85.93% 3 Missing and 6 partials ⚠️
...ava/com/uber/nullaway/generics/GenericsChecks.java 92.00% 0 Missing and 4 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1499      +/-   ##
============================================
+ Coverage     88.43%   88.45%   +0.01%     
- Complexity     2813     2823      +10     
============================================
  Files            99      100       +1     
  Lines          9410     9446      +36     
  Branches       1888     1893       +5     
============================================
+ Hits           8322     8355      +33     
- Misses          529      530       +1     
- Partials        559      561       +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 March 26, 2026 22:09
@msridhar msridhar changed the title Handle method references in poly-expression nullability inference and add tests Better handling of method references passed to generic methods in JSpecify mode Mar 26, 2026
@coderabbitai

coderabbitai Bot commented Mar 26, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5d78670c-b615-4e72-a656-74118c48cb1e

📥 Commits

Reviewing files that changed from the base of the PR and between b2ada24 and 9c5c926.

📒 Files selected for processing (2)
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsUtils.java

Walkthrough

Adds method-reference support to generic-method inference and assignment-compatibility checks by treating MemberReferenceTree as a poly-expression, generalizing target-type caching, and delegating method-reference return/parameter relation generation to a new utility class GenericsUtils. Exposes two GenericsChecks helpers as package-visible, introduces method-reference-specific error reporters, substitutes explicit type arguments for referenced methods, and adds/updates tests covering method-reference inference, nested nullability, and array/varargs cases.

Possibly related PRs

Suggested labels

jspecify

Suggested reviewers

  • yuxincs
  • lazaroclapp
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% 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 'Better handling of method references passed to generic methods in JSpecify mode' directly describes the main change of integrating method-reference types with generic method type inference.
Linked Issues check ✅ Passed All coding requirements from #1431 and #1474 are met: method-reference types are cached for type inference, explicit return/parameter nullability checks detect nested annotation mismatches, and varargs array contents nullability is properly handled.
Out of Scope Changes check ✅ Passed All code changes are within scope: modifications to GenericsChecks handle method references, new GenericsUtils class encapsulates method-reference logic, and tests validate the fixes for issues #1431 and #1474.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/add-test-and-fix-nullaway-issue-1431

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nullaway/src/main/java/com/uber/nullaway/generics/GenericsUtils.java`:
- Around line 66-69: The code currently force-casts the result of
genericsChecks.getMemberReferenceMethodType(...) via castToNonNull into
referencedMethodType, which can be null outside JSpecify mode; update the logic
in GenericsUtils to first call
genericsChecks.getMemberReferenceMethodType(memberReferenceTree,
referencedMethod, state), check for null and return early (or handle the null
case appropriately) before calling castToNonNull, so castToNonNull is only
invoked on a guaranteed non-null value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f9889036-37ff-4dbb-addc-87c86891f3d1

📥 Commits

Reviewing files that changed from the base of the PR and between e78dc89 and 63ab186.

📒 Files selected for processing (3)
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsUtils.java
  • nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodLambdaOrMethodRefArgTests.java

if (typeArgumentTrees != null
&& !typeArgumentTrees.isEmpty()
&& overridingMethod.asType() instanceof Type.ForAll forAllType) {
// handle explicit type arguments in method reference, e.g., x::<Foo>method

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The other changes in this PR exposed this issue; apparently we weren't doing the appropriate substitution before when a method reference has explicit type arguments

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a case where a method reference has some type arguments given explicitly and some inferred? (Not sure if this is possible in Java, just checking)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No, it either needs to be all explicit or all inferred

@lazaroclapp lazaroclapp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Did my best to check this. LGMT. Biggest thing bothering me is the location of the annotation in the error messages, but I think that's just our existing pretty-printing logic instead?

return;
}
Type.MethodType referencedMethodType =
castToNonNull(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is this cast safe? And why is it preferred to an if check and return? I suspect it might be both, just noting that it might benefit from a comment as to why :)

Edit: There was a null check and an alternative default for referencedMethodType in the non-refactored version, no?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

You're right. A null check does no harm here (though I don't think we should ever see null). Will add.


private final NullAway analysis;
private final Config config;
/* package-private */ final Config config;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why? Can't get pass it around or create a package-private accessor?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, added a package-private getter

solver.addSubtypeConstraint(fiParamTypes.get(fiStartIndex + i), varargsElementType, false);
}
state,
(subtype, supertype, relationKind) -> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I guess relationKind is used only for reporting and then only when checking call-sites, right?

Does java support something like: (subtype, supertype, _) -> { here for clarity?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Java does support _ but only in a more recent version than we are using. I renamed to unused here; you're right this is only used in the other case for error reporting.

if (typeArgumentTrees != null
&& !typeArgumentTrees.isEmpty()
&& overridingMethod.asType() instanceof Type.ForAll forAllType) {
// handle explicit type arguments in method reference, e.g., x::<Foo>method

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a case where a method reference has some type arguments given explicitly and some inferred? (Not sure if this is possible in Java, just checking)

msridhar added a commit that referenced this pull request Apr 3, 2026
See #1499 (comment).
This should make our error messages more consistent and less confusing.
We now only include `@Nullable` type use annotations.

Eventually, we may want to adopt [JSpecify
shorthand](https://github.com/jspecify/jspecify/wiki/notation#shorthand-notation)
when printing types, but we'd have to think more carefully about that.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Diagnostic type annotations are printed more concisely: most inline
annotations (e.g., @nonnull) are suppressed while @nullable remains
visible once; array spacing normalized for clearer output.

* **Tests**
* Updated expected diagnostic texts in several tests to match the
slimmer annotation formatting.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSpecify: bug with array contents annotations being copied by javac JSpecify: Integrate method reference types with generic method type inference

2 participants