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

External Library Models: Adding support for Nullable upper bounds of Generic Type parameters #949

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

akulk022
Copy link
Collaborator

@akulk022 akulk022 commented Apr 13, 2024

With these changes we are able to read and create library models for Nullable upper bounds of generic type parameters from externally annotated source code as shown in the below example.

Externally annotated source code example:

public static class GenericExample<T extends @Nullable Object> {
        public T getNull() {
            return null;
        }
 }
public static class GenericExample<T> {
    public T getNull() {
      return null;
    }
}
static GenericExample<@Nullable Object> genericExample = new GenericExample<@Nullable Object>();
static void test(Object value){}
// BUG: Diagnostic contains: passing @Nullable parameter 'genericExample.getNull()'
test(genericExample.getNull());

@akulk022 akulk022 marked this pull request as draft April 13, 2024 14:55
Copy link

codecov bot commented Apr 13, 2024

Codecov Report

Attention: Patch coverage is 72.72727% with 30 lines in your changes are missing coverage. Please review.

Project coverage is 85.98%. Comparing base (09db47a) to head (d351cc9).

Files Patch % Lines
.../uber/nullaway/libmodel/LibraryModelGenerator.java 0.00% 15 Missing ⚠️
...ava/com/uber/nullaway/handlers/StubxCacheUtil.java 78.12% 9 Missing and 5 partials ⚠️
...er/nullaway/handlers/InferredJARModelsHandler.java 66.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #949      +/-   ##
============================================
- Coverage     86.08%   85.98%   -0.11%     
- Complexity     2018     2023       +5     
============================================
  Files            79       80       +1     
  Lines          6612     6663      +51     
  Branches       1280     1288       +8     
============================================
+ Hits           5692     5729      +37     
- Misses          510      524      +14     
  Partials        410      410              

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

I have a few initial comments. I got lost reviewing the changes under nullaway/src/main/java/com/uber/nullaway/handlers. Can you describe the purpose of the new StubxCacheUtil class and how the refactored code works, for both old JarInfer stubs and newly generated ones?

@@ -45,4 +45,11 @@ public String returnNull() {
return null;
}
}

public static class GenericExample<T extends @Nullable Object> {
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 call this UpperBoundExample

"package com.uber;",
"import org.jspecify.annotations.Nullable;",
"import com.uber.nullaway.libmodel.AnnotationExample;",
"class Test {",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would suggest we also update the libraryModelWithoutJarInferEnabledTest test. But I think we will get errors there even without the modeling due to #872

Comment on lines +211 to +213
methodRecords.put(
parentName + ":" + GENERIC_TYPE_IDENTIFIER,
MethodAnnotationsRecord.create(ImmutableSet.of(), genericParamAnnotationsMap));
Copy link
Collaborator

Choose a reason for hiding this comment

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

At first glance this seems rather hacky. Why are we reusing the methodRecords map and the MethodAnnotationsRecord class when the upper bound is on a type variable? At least for this case, the type variable is declared on a class, not a method. I'd prefer we just create a separate data structure for class or type variable records

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree, it is very hacky. I will use a separate data structure and work on a way to integrate it within the cache.

We insert a specialized MethodAnnotationsRecord object to store the generic type parameter nullability
information for the class by using an identifier that can never be an actual method name.
*/
if (this.isNullMarked && !genericParamAnnotationsMap.isEmpty()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we be checking if the class is null marked before getting the upper bounds at line 204, for efficiency?

* @param cid ClassOrInterfaceDeclaration instance.
* @return Map of annotations for generic type parameters with Nullable upper bounds.
*/
private ImmutableMap<Integer, ImmutableSet<String>> getGenericTypeParameterNullableUpperBounds(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Returning a map seems overly complex. If we are only looking for @Nullable upper bounds why not just return an ImmutableSet<Integer> containing those type variable indices with such a bound?

import java.util.ServiceLoader;
import java.util.Set;

public class StubxCacheUtil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the purpose of this class? Please add Javadoc

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I created it to reuse the logic for caching the annotation information from the stubx file from InferredJARModelsHandler

private static final int RETURN = -1;
private final Map<String, Map<String, Map<Integer, Set<String>>>> argAnnotCache;

public StubxCacheUtil(String logCaller) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add Javadoc, including the purpose of the parameter

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.

None yet

2 participants