Skip to content

JSpecify: infer generic method type arguments for assignments#1131

Merged
yuxincs merged 93 commits intouber:masterfrom
haewiful:infer-generics
Mar 13, 2025
Merged

JSpecify: infer generic method type arguments for assignments#1131
yuxincs merged 93 commits intouber:masterfrom
haewiful:infer-generics

Conversation

@haewiful
Copy link
Contributor

@haewiful haewiful commented Jan 15, 2025

This PR infers nullability of generic method type arguments for the case where the result of the call is assigned to a variable, e.g.:

  class Foo<T extends @Nullable Object> {
    Foo(T t) {}
    static <U extends @Nullable Object> Foo<U> make(U u) { return new Foo<>(u); }
    void test() {
      Foo<@Nullable Object> f1 = Foo.make(null); // legal
      Foo<Object> f2 = Foo.make(null); // error reported; arg cannot be @Nullable
    }
  }

See the tests for (many) more examples.

@msridhar
Copy link
Collaborator

@haewiful thanks for this! It looks like CI fails on the :nullaway:buildWithNullAway task:

https://github.com/uber/NullAway/actions/runs/12978381939/job/36192901792?pr=1131#step:5:606

I suggest looking at the code it is crashing on and trying to write a unit test that causes the same failure, which will make it easier to debug.

@codecov
Copy link

codecov bot commented Feb 3, 2025

Codecov Report

Attention: Patch coverage is 91.13924% with 7 lines in your changes missing coverage. Please review.

Project coverage is 88.16%. Comparing base (baf8f77) to head (fe8702d).
Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
.../InferSubstitutionViaAssignmentContextVisitor.java 79.31% 3 Missing and 3 partials ⚠️
...ava/com/uber/nullaway/generics/GenericsChecks.java 96.87% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             master    #1131   +/-   ##
=========================================
  Coverage     88.16%   88.16%           
- Complexity     2281     2300   +19     
=========================================
  Files            87       88    +1     
  Lines          7461     7531   +70     
  Branches       1491     1503   +12     
=========================================
+ Hits           6578     6640   +62     
- Misses          445      448    +3     
- Partials        438      443    +5     

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

Comment on lines +1089 to +1091
new GenericsChecks()
.getGenericReturnNullnessAtInvocation(
ASTHelpers.getSymbol(tree), tree, state, config);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hrm, this seems suspect. If you create a new GenericsChecks object here, then the inferred types will immediately disappear. Do we need to instead somehow pass in the GenericsChecks object from the NullAway instance into this class?

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, I do think the GenericsChecks object should be passed from NullAway because getGenericReturnNullnessAtInvocation() method call uses what is in inferred types but doesn't add to it. If the returned type contains some type variable in the inferredTypes, it should be used here.
In my examination, the AccessPathNullnessPropagation class is used in AccessPathNullnessAnalysis which is used in the NullAway file. I think we should pass the GenericsChecks instance from NullAway to AccessPathNullnessAnalysis to AccessPathNullnessPropagation.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok done in bfccfad

if (typeVarHasNullableUpperBound) { // can just use the lhs type nullability
genericNullness.put(typeVar, lhsType);
} else { // rhs can't be nullable, use upperbound
// this is a bit weird. the nullability might be right, but the base type may be wrong?
Copy link
Collaborator

@msridhar msridhar Mar 11, 2025

Choose a reason for hiding this comment

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

@haewiful this logic here is incorrect. We shouldn't put the upper bound in the map; we should put lhsType, except that if lhsType is @Nullable, we should strip that annotation off of it. (I think you can do that by calling stripMetadata().) See the new test inferNestedNonNullUpperBound() that I added; we should get an error there, but we do not right now.

@msridhar msridhar changed the title Infer generics for assignments Infer generic method type arguments for assignments Mar 11, 2025
@msridhar msridhar changed the title Infer generic method type arguments for assignments JSpecify: infer generic method type arguments for assignments Mar 11, 2025
@msridhar msridhar enabled auto-merge (squash) March 11, 2025 19:37
@msridhar msridhar closed this Mar 12, 2025
auto-merge was automatically disabled March 12, 2025 00:06

Pull request was closed

@msridhar msridhar reopened this Mar 12, 2025
@yuxincs yuxincs merged commit 8786e88 into uber:master Mar 13, 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.

3 participants