Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSpecify mode: initial support for generic methods (with explicit type arguments at calls) #1053

Merged
merged 27 commits into from
Oct 30, 2024

Conversation

haewiful
Copy link
Contributor

@haewiful haewiful commented Oct 8, 2024

Thank you for contributing to NullAway!

Please note that once you click "Create Pull Request" you will be asked to sign our Uber Contributor License Agreement via CLA assistant.

Before pressing the "Create Pull Request" button, please provide the following:

@msridhar msridhar marked this pull request as draft October 9, 2024 16:19
Copy link

codecov bot commented Oct 14, 2024

Codecov Report

Attention: Patch coverage is 93.24324% with 5 lines in your changes missing coverage. Please review.

Project coverage is 87.65%. Comparing base (238363a) to head (641b9ee).
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...ava/com/uber/nullaway/generics/GenericsChecks.java 93.05% 0 Missing and 5 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1053      +/-   ##
============================================
+ Coverage     87.60%   87.65%   +0.05%     
- Complexity     2204     2222      +18     
============================================
  Files            85       85              
  Lines          7195     7269      +74     
  Branches       1416     1436      +20     
============================================
+ Hits           6303     6372      +69     
  Misses          456      456              
- Partials        436      441       +5     

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

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.

This is a great start! I have a few comments. Also, can we add some tests for a method like this:

    public static <T, U> void twoTypeVars(T first, U second) {
    }

Maybe write some variants where only one of the type variables has a @Nullable upper bound, and some calls where the wrong type argument is made @Nullable. This is just to check that our indexing logic is matching type arguments to type variables correctly.


// base type that is being instantiated
Type baseType = methodSymbol.asType();
List<Type> baseTypeArgs = baseType.getTypeArguments();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's rename:

Suggested change
List<Type> baseTypeArgs = baseType.getTypeArguments();
List<Type> baseTypeVariables = baseType.getTypeArguments();

@@ -715,6 +785,40 @@ public static Nullness getGenericReturnNullnessAtInvocation(
MethodInvocationTree tree,
VisitorState state,
Config config) {

Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to update the Javadoc of getGenericReturnNullnessAtInvocation to indicate it also handles generic methods, in addition to instance methods of generic classes (what it handled before)

@@ -715,6 +785,40 @@ public static Nullness getGenericReturnNullnessAtInvocation(
MethodInvocationTree tree,
VisitorState state,
Config config) {

List<? extends Tree> typeArgumentTrees = tree.getTypeArguments();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this new logic only apply to generic methods? If so, can we only run it for that case, by checking if the invoked method has any type variables, !invokedMethodSymbol.getTypeParameters().isEmpty()?

Comment on lines 807 to 815
// If it's not a ForAll type, handle it as a normal MethodType
Type.MethodType methodTypeElse = (Type.MethodType) invokedMethodSymbol.type;
substitutedReturnType =
state
.getTypes()
.subst(
methodTypeElse.restype,
invokedMethodSymbol.type.getTypeArguments(),
explicitTypeArgs);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I feel like if we only apply the new logic to generic methods, we will always get a ForAll type and won't need this else case

@@ -768,6 +884,51 @@ public static Nullness getGenericParameterNullnessAtInvocation(
MethodInvocationTree tree,
VisitorState state,
Config config) {
List<? extends Tree> typeArgumentTrees = tree.getTypeArguments();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment as above regarding only running the new logic when a generic method is being invoked

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.

Looking good! A few more minor comments

@@ -145,6 +148,83 @@ private static boolean[] getTypeParamsWithNullableUpperBound(
return result;
}

/**
* Checks instantiated generic arguments of generic method calls. {@code @Nullable} types are only
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
* Checks instantiated generic arguments of generic method calls. {@code @Nullable} types are only
* Checks instantiated generic arguments of generic method calls. {@code @Nullable} types can only be

boolean hasNullableAnnotation =
Nullness.hasNullableAnnotation(annotationMirrors.stream(), config)
|| handler.onOverrideTypeParameterUpperBound(baseType.tsym.toString(), i);
// if type argument does not have @Nullable annotation then the instantiation is invalid
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// if type argument does not have @Nullable annotation then the instantiation is invalid
// if type variable's upper bound does not have @Nullable annotation then the instantiation is invalid

Comment on lines 923 to 924
// problem is that it returns nullable from the declaration even though the parameter itself is
// nonnull
Copy link
Collaborator

Choose a reason for hiding this comment

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

do we still want this new comment?

@msridhar msridhar changed the title Task2 JSpecify mode: initial support for generic methods (with explicit type arguments at calls) Oct 27, 2024
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.

This mostly looks good! Once we add a test case as discussed in the comment and see if it works, then we can land

@msridhar msridhar marked this pull request as ready for review October 30, 2024 23:47
@msridhar
Copy link
Collaborator

This support is incomplete (even for cases with explicit type arguments), but let's go ahead and land it and do things in pieces. Thanks!

@msridhar msridhar enabled auto-merge (squash) October 30, 2024 23:50
@msridhar msridhar merged commit e9541e3 into uber:master Oct 30, 2024
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