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: initial checks for generic type compatibility at assignments #715

Merged
merged 204 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
204 commits
Select commit Hold shift + click to select a range
f306db2
an initial test case
msridhar Sep 13, 2022
16654f7
sample code
msridhar Sep 27, 2022
7f9418b
add-generics-checks
NikitaAware Sep 29, 2022
441ab7c
first-case-of-generics-support
NikitaAware Sep 30, 2022
4a9c86d
first-case-of-generics-support
NikitaAware Sep 30, 2022
01377b6
restore bad deletion
msridhar Sep 30, 2022
359dcc8
add-generics-spport-task-1-updates
NikitaAware Sep 30, 2022
731bf3d
add-generics-spport-task-1-updates
NikitaAware Sep 30, 2022
b9b8239
add-generics-spport-task-1-updates
NikitaAware Sep 30, 2022
fb1beb9
add generics check for variables
NikitaAware Oct 2, 2022
1a23f8e
Merge branch 'master' into add-generics-checks
msridhar Oct 3, 2022
90d471d
tweak error message
msridhar Oct 3, 2022
98f7fa6
more tests
msridhar Oct 3, 2022
53ddad7
code cleanup.remove print statements
NikitaAware Oct 3, 2022
7daa52b
Add temporary check to consider unannoted base type
NikitaAware Oct 3, 2022
cd01ff9
minor code changes
NikitaAware Oct 4, 2022
202d5db
add generics check for new class - check if the class is generic
NikitaAware Oct 6, 2022
3998949
add generics check for new class - check for the type arguments
NikitaAware Oct 7, 2022
fc8b99f
generics check for new class
NikitaAware Oct 10, 2022
6425909
generics check for extended class
NikitaAware Oct 10, 2022
bae84dc
generics check for new class
NikitaAware Oct 10, 2022
4aba42c
generics check for new class - minor changes for error message
NikitaAware Oct 10, 2022
bca4f24
generics check for interface implementation
NikitaAware Oct 10, 2022
99e8bf9
minor identation changes
NikitaAware Oct 10, 2022
a0caac8
nullability checks for build failures
NikitaAware Oct 11, 2022
5b9d918
Add Test: generics check for subclasses
NikitaAware Oct 11, 2022
4cb51d9
restructure generics tests
NikitaAware Oct 11, 2022
2e4a2ef
restructure generics tests
NikitaAware Oct 11, 2022
b1668e9
Add tests
NikitaAware Oct 11, 2022
9c8b3ae
restructure code
NikitaAware Oct 11, 2022
97e088b
restructure code
NikitaAware Oct 11, 2022
036febf
restructure code
NikitaAware Oct 11, 2022
f065bd9
Merge branch 'master' into add-generics-checks
msridhar Oct 11, 2022
00ad702
add jspecify mode flag
msridhar Oct 11, 2022
31a0e4d
check jspecify flag
msridhar Oct 11, 2022
fe66b4a
add test case for nested type parameter instantiation
msridhar Oct 11, 2022
89ca225
nested type
NikitaAware Oct 13, 2022
8ba736b
nested generics type check for new class
NikitaAware Oct 13, 2022
fa627be
Add test - generics check for function return type
NikitaAware Oct 14, 2022
5745cf7
Add - generics check for function return type
NikitaAware Oct 14, 2022
1518629
code cleaning - generics check
NikitaAware Oct 18, 2022
b25ba39
code cleaning - generics check
NikitaAware Oct 18, 2022
bd96079
Merge branch 'master' into add-generics-checks
msridhar Oct 19, 2022
975dccd
Suggested changes
NikitaAware Oct 23, 2022
6d22b00
Suggested changes
NikitaAware Oct 23, 2022
2ddbea9
Suggested changes
NikitaAware Oct 25, 2022
b9cbbff
working changes
NikitaAware Oct 25, 2022
2002aec
working changes
NikitaAware Oct 25, 2022
5c11fbe
suggested changes
NikitaAware Oct 25, 2022
736a613
suggested changes
NikitaAware Oct 27, 2022
80ba7a5
suggested changes
NikitaAware Oct 27, 2022
e1864ee
suggested changes
NikitaAware Oct 27, 2022
8d5e193
extend test
msridhar Oct 27, 2022
91aab18
@Nullable annotation check for Paramterized typed tree
NikitaAware Oct 27, 2022
799f5dc
@Nullable annotation check for Paramterized typed tree
NikitaAware Oct 27, 2022
f042e60
added updates
NikitaAware Nov 1, 2022
852e4b1
added updates
NikitaAware Nov 1, 2022
8b484c2
extra test cases for other annotations
NikitaAware Nov 1, 2022
1727458
extra test cases for other annotations
NikitaAware Nov 1, 2022
7ecc2c4
Merge branch 'master' into add-generics-checks
NikitaAware Nov 1, 2022
f0a0b62
some comments and a todo
msridhar Nov 3, 2022
bc3c0be
reuse code
msridhar Nov 6, 2022
832f733
minor cleanup
msridhar Nov 6, 2022
0b5268c
fix
msridhar Nov 6, 2022
66a1068
Suggested changes
NikitaAware Nov 6, 2022
1bc1ea6
Suggested changes
NikitaAware Nov 6, 2022
cb1690a
Suggested changes
NikitaAware Nov 6, 2022
c523f2d
Javadoc tweaks
msridhar Nov 7, 2022
8cf3819
add private constructor
msridhar Nov 7, 2022
27d5488
suggested changes
NikitaAware Nov 10, 2022
263edac
suggested changes
NikitaAware Nov 10, 2022
2fa8569
suggested changes
NikitaAware Nov 10, 2022
f40a0da
suggested changes
NikitaAware Nov 10, 2022
8308cf6
code cleanup
msridhar Nov 10, 2022
af6404a
tests for error positions
msridhar Nov 10, 2022
ad67e01
Clean up by implementing ParameterizedTypeTreeMatcher
msridhar Nov 11, 2022
67994da
remove extra blank line
msridhar Nov 11, 2022
2631d63
add test
msridhar Nov 11, 2022
36a5c60
suggested changes
NikitaAware Nov 16, 2022
c8ebfe6
suggested changes
NikitaAware Nov 16, 2022
938c595
Merge branch 'master' into add-generics-checks
msridhar Nov 16, 2022
3fb6856
tweak error message
msridhar Nov 16, 2022
ee57c56
clarify test name and purpose
msridhar Nov 16, 2022
7349744
Assignment interface added
NikitaAware Nov 16, 2022
b5eb8a0
structure for assignments checks
NikitaAware Nov 16, 2022
eecc4c3
Assignment changes
NikitaAware Nov 18, 2022
063c580
supertype changes
NikitaAware Dec 7, 2022
696aa90
Merge branch 'master' into assigments1
NikitaAware Dec 9, 2022
823da48
multiple interface implementation
NikitaAware Dec 9, 2022
e26e088
multilevel inheritance
NikitaAware Dec 9, 2022
f7009ae
changes
NikitaAware Dec 9, 2022
3b2cdf1
changes
NikitaAware Dec 9, 2022
d957f56
nested checks
NikitaAware Dec 9, 2022
2d061fc
working changes
NikitaAware Dec 9, 2022
80f296e
working changes
NikitaAware Dec 9, 2022
c33fe3f
Working changes
NikitaAware Dec 9, 2022
31a3dca
updates
NikitaAware Dec 10, 2022
3eca5a3
failing 3 tests
NikitaAware Dec 15, 2022
3435f89
working tests
NikitaAware Dec 16, 2022
7972c05
npe for variable initialization changes
NikitaAware Dec 20, 2022
4924722
npe for variable initialization changes
NikitaAware Dec 20, 2022
35e2bc7
All tests pass including the existing tests
NikitaAware Dec 20, 2022
a59d223
Merge branch 'master' into assigments1
msridhar Dec 21, 2022
25b840b
Merge branch 'master' into assigments1
msridhar Dec 21, 2022
c8456f3
some cleanup
msridhar Dec 21, 2022
945df87
enable check for multiple top-level classes
msridhar Dec 21, 2022
a3fa869
suggested changes - enclosing class member fields
NikitaAware Jan 3, 2023
601f2b5
code cleanup
NikitaAware Jan 3, 2023
a0aacdb
suggested changes
NikitaAware Jan 3, 2023
7b39522
Merge branch 'master' into assigments1
msridhar Jan 4, 2023
84140d0
some suggested changes
NikitaAware Jan 4, 2023
20fae01
suggested changes with working normal typed tree
NikitaAware Jan 4, 2023
c517b4b
Changes with parameterized typed tree without the error generation
NikitaAware Jan 4, 2023
def7ce2
suggested changes with two test cases failing
NikitaAware Jan 4, 2023
75f36df
suggested changes with all passing tests
NikitaAware Jan 4, 2023
0125647
All working changes
NikitaAware Jan 4, 2023
8fc5d18
Continuous integration working
NikitaAware Jan 4, 2023
090ac0c
Continuous integration working
NikitaAware Jan 4, 2023
ba6b229
changes
NikitaAware Jan 4, 2023
bd6700f
minor changes
NikitaAware Jan 4, 2023
fd0b096
fix naming case
msridhar Jan 4, 2023
d5f34f2
failing test
msridhar Jan 4, 2023
01c5503
refactoring and comments
msridhar Jan 4, 2023
25599dd
add a case
msridhar Jan 4, 2023
bf80848
working code with class type
NikitaAware Jan 6, 2023
8ca41d0
minor refactoring
NikitaAware Jan 6, 2023
4a87b6c
minor changes
NikitaAware Jan 6, 2023
44bea9b
code cleaning
NikitaAware Jan 6, 2023
b9a2ed4
code cleaning
NikitaAware Jan 6, 2023
ac7f33a
fancy test works!
msridhar Jan 7, 2023
851fbfa
Merge branch 'master' into assigments1
msridhar Jan 7, 2023
b1119ab
only run check in JSpecify mode
msridhar Jan 7, 2023
92989ec
Fix NullAway errors
msridhar Jan 7, 2023
334d1e8
modified assignment message
NikitaAware Jan 7, 2023
79b474b
remove unneeded suppression
msridhar Jan 7, 2023
05292c0
remove change to build.gradle
msridhar Jan 7, 2023
d9e2689
remove local used only once
msridhar Jan 7, 2023
1cc72f2
cleanup supertypeMatchingLHS
msridhar Jan 7, 2023
85741be
restore private
msridhar Jan 7, 2023
c58d819
add private
msridhar Jan 7, 2023
8829bcd
changed the error message
NikitaAware Jan 8, 2023
fd16446
updated the error message text
NikitaAware Jan 8, 2023
c0fd28e
Type.ClassType suggestion changes
NikitaAware Jan 8, 2023
abe3ed9
changes
NikitaAware Jan 8, 2023
7a18462
changes
NikitaAware Jan 8, 2023
23c8fbb
changes
NikitaAware Jan 8, 2023
88accab
extra test case
NikitaAware Jan 8, 2023
124e7c8
add failing test
msridhar Jan 8, 2023
00802a1
updated logic
NikitaAware Jan 8, 2023
021435a
minor updates
NikitaAware Jan 8, 2023
a12d549
minor code cleaning
NikitaAware Jan 8, 2023
82d9e8d
minor code cleaning
NikitaAware Jan 8, 2023
f381d83
suggested changes
NikitaAware Jan 9, 2023
75234b8
suggested changes
NikitaAware Jan 9, 2023
1be7c40
replaced super type matching lhs with asSuper method
NikitaAware Jan 9, 2023
676e07d
updated assignment checks
NikitaAware Jan 10, 2023
a085443
changes
NikitaAware Jan 10, 2023
c8303d7
changes
NikitaAware Jan 10, 2023
4238a9a
suggested changes
NikitaAware Jan 10, 2023
f5ec4cd
suggested changes
NikitaAware Jan 10, 2023
257c42c
suggested changes
NikitaAware Jan 10, 2023
a9e4890
suggested changes
NikitaAware Jan 10, 2023
f3fd30d
suggested changes
NikitaAware Jan 10, 2023
c2f66e2
Merge branch 'master' into assigments1
msridhar Jan 11, 2023
aeddd14
fix nullaway error
msridhar Jan 11, 2023
c67db12
Merge branch 'master' into assigments1
msridhar Jan 11, 2023
e244e55
fixes
msridhar Jan 11, 2023
53f8546
more cleanup and javadoc
msridhar Jan 11, 2023
0fe788d
Merge branch 'master' into assigments1
msridhar Jan 14, 2023
3fb81c9
Merge branch 'master' into assigments1
msridhar Jan 20, 2023
0bfe208
Merge branch 'master' into assigments1
msridhar Jan 22, 2023
d6bc810
suggested changes in test nestedChecksForAssignmentsMultipleArguments
NikitaAware Jan 23, 2023
3127339
suggested changes in test - superTypeAssignmentChecksSingleInterface
NikitaAware Jan 23, 2023
4aacb5a
suggested indentation changes in test - superTypeAssignmentChecksMult…
NikitaAware Jan 23, 2023
f3e6b45
suggested changes
NikitaAware Jan 23, 2023
38f2ef6
suggested changes
NikitaAware Jan 23, 2023
5245c4d
suggested test for the lambda expressions
NikitaAware Jan 23, 2023
0344360
Java doc for type with preserved annotations
NikitaAware Jan 23, 2023
00cad6f
comments
NikitaAware Jan 23, 2023
d09c190
code cleaning for the tests
NikitaAware Jan 23, 2023
8bb552c
code cleaning for the tests
NikitaAware Jan 23, 2023
0cff82f
changes
NikitaAware Jan 23, 2023
9906f25
suggested changes
NikitaAware Jan 23, 2023
f6dcc69
negative tests
NikitaAware Jan 23, 2023
4323d48
fix formatting in tests
msridhar Jan 23, 2023
5ec8fa1
test cases for diamond operator and the lambdas
NikitaAware Jan 24, 2023
7c07d98
test cases for diamond operator and the lambdas
NikitaAware Jan 24, 2023
6755d66
private fields
NikitaAware Jan 25, 2023
41f45b1
check only for jspecify nullable annotation
NikitaAware Jan 25, 2023
322c3c2
move test method to more appropriate place
msridhar Jan 25, 2023
4a46a46
tweak method reference test
msridhar Jan 25, 2023
4a1ce05
re-add blank line
msridhar Jan 25, 2023
fce345b
clarify method ref test
msridhar Jan 25, 2023
02bbd9f
tweak lambda and diamond operator tests
msridhar Jan 25, 2023
514829d
make test Java 8 compatible
msridhar Jan 26, 2023
2270d8d
Merge branch 'master' into assigments1
msridhar Jan 31, 2023
924883b
Merge branch 'master' into assigments1
msridhar Feb 1, 2023
c070c22
Merge branch 'master' into assigments1
msridhar Feb 6, 2023
e37aa61
Merge branch 'master' into assigments1
msridhar Feb 6, 2023
7412439
suggested changes
NikitaAware Feb 7, 2023
228cf0f
suggested changes
NikitaAware Feb 7, 2023
b7bbf7c
suggested changes
NikitaAware Feb 7, 2023
edb4f86
suggested changes
NikitaAware Feb 7, 2023
18f3134
Merge branch 'master' into assigments1
lazaroclapp Feb 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nullaway/src/main/java/com/uber/nullaway/ErrorMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public enum MessageTypes {
WRONG_OVERRIDE_POSTCONDITION,
WRONG_OVERRIDE_PRECONDITION,
TYPE_PARAMETER_CANNOT_BE_NULLABLE,
ASSIGN_GENERIC_NULLABLE,
}

public String getMessage() {
Expand Down
220 changes: 216 additions & 4 deletions nullaway/src/main/java/com/uber/nullaway/GenericsChecks.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
package com.uber.nullaway;

import static com.uber.nullaway.NullabilityUtil.castToNonNull;

import com.google.common.base.Preconditions;
import com.google.errorprone.VisitorState;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.AnnotatedTypeTree;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.TypeMetadata;
import com.sun.tools.javac.code.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** Methods for performing checks related to generic types and nullability. */
public final class GenericsChecks {

private GenericsChecks() {
// just utility methods
private static final String NULLABLE_NAME = "org.jspecify.annotations.Nullable";
msridhar marked this conversation as resolved.
Show resolved Hide resolved

private static final Supplier<Type> NULLABLE_TYPE_SUPPLIER =
Suppliers.typeFromString(NULLABLE_NAME);
private VisitorState state;
private Config config;
private NullAway analysis;

public GenericsChecks(VisitorState state, Config config, NullAway analysis) {
this.state = state;
this.config = config;
this.analysis = analysis;
}

/**
Expand Down Expand Up @@ -70,14 +92,14 @@ public static void checkInstantiationForParameterizedTypedTree(
// if base type argument does not have @Nullable annotation then the instantiation is
// invalid
if (!hasNullableAnnotation) {
invalidInstantiationError(
reportInvalidInstantiationError(
nullableTypeArguments.get(i), baseType, typeVariable, state, analysis);
}
}
}
}

private static void invalidInstantiationError(
private static void reportInvalidInstantiationError(
Tree tree, Type baseType, Type baseTypeVariable, VisitorState state, NullAway analysis) {
ErrorBuilder errorBuilder = analysis.getErrorBuilder();
ErrorMessage errorMessage =
Expand All @@ -90,4 +112,194 @@ private static void invalidInstantiationError(
errorBuilder.createErrorDescription(
errorMessage, analysis.buildDescription(tree), state, null));
}

private static void reportInvalidAssignmentInstantiationError(
Tree tree, Type lhsType, Type rhsType, VisitorState state, NullAway analysis) {
ErrorBuilder errorBuilder = analysis.getErrorBuilder();
ErrorMessage errorMessage =
new ErrorMessage(
ErrorMessage.MessageTypes.ASSIGN_GENERIC_NULLABLE,
String.format(
"Cannot assign from type "
+ rhsType
+ " to type "
+ lhsType
+ " due to mismatched nullability of type parameters"));
state.reportMatch(
errorBuilder.createErrorDescription(
errorMessage, analysis.buildDescription(tree), state, null));
}

/**
* For a tree representing an assignment, ensures that from the perspective of type parameter
* nullability, the type of the right-hand side is assignable to (a subtype of) the type of the
* left-hand side. This check ensures that for every parameterized type nested in each of the
* types, the type parameters have identical nullability.
*
* @param tree the tree to check, which must be either an {@link AssignmentTree} or a {@link
* VariableTree}
*/
public void checkTypeParameterNullnessForAssignability(Tree tree) {
if (!config.isJSpecifyMode()) {
return;
}
Tree lhsTree;
Tree rhsTree;
if (tree instanceof VariableTree) {
VariableTree varTree = (VariableTree) tree;
lhsTree = varTree.getType();
rhsTree = varTree.getInitializer();
} else {
AssignmentTree assignmentTree = (AssignmentTree) tree;
lhsTree = assignmentTree.getVariable();
rhsTree = assignmentTree.getExpression();
}
// rhsTree can be null for a VariableTree. Also, we don't need to do a check
// if rhsTree is the null literal
if (rhsTree == null || rhsTree.getKind().equals(Tree.Kind.NULL_LITERAL)) {
return;
}
Type lhsType = ASTHelpers.getType(lhsTree);
Type rhsType = ASTHelpers.getType(rhsTree);
// For NewClassTrees with annotated type parameters, javac does not preserve the annotations in
// its computed type for the expression. As a workaround, we construct a replacement Type
// object with the appropriate annotations.
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 Nice trick and comment 🙂

if (rhsTree instanceof NewClassTree
&& ((NewClassTree) rhsTree).getIdentifier() instanceof ParameterizedTypeTree) {
ParameterizedTypeTree paramTypedTree =
(ParameterizedTypeTree) ((NewClassTree) rhsTree).getIdentifier();
if (paramTypedTree.getTypeArguments().isEmpty()) {
// no explicit type parameters
return;
}
rhsType = typeWithPreservedAnnotations(paramTypedTree);
}
if (lhsType instanceof Type.ClassType && rhsType instanceof Type.ClassType) {
compareNullabilityAnnotations((Type.ClassType) lhsType, (Type.ClassType) rhsType, tree);
msridhar marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
* Compare two types from an assignment for identical type parameter nullability, recursively
* checking nested generic types. See <a
* href="https://jspecify.dev/docs/spec/#nullness-delegating-subtyping">the JSpecify
* specification</a> and <a
* href="https://docs.oracle.com/javase/specs/jls/se14/html/jls-4.html#jls-4.10.2">the JLS
* subtyping rules for class and interface types</a>.
*
* @param lhsType type for the lhs of the assignment
* @param rhsType type for the rhs of the assignment
* @param tree tree representing the assignment
*/
private void compareNullabilityAnnotations(
Type.ClassType lhsType, Type.ClassType rhsType, Tree tree) {
Types types = state.getTypes();
// The base type of rhsType may be a subtype of lhsType's base type. In such cases, we must
// compare lhsType against the supertype of rhsType with a matching base type.
rhsType = (Type.ClassType) types.asSuper(rhsType, lhsType.tsym);
// This is impossible, considering the fact that standard Java subtyping succeeds before running
// NullAway
if (rhsType == null) {
throw new RuntimeException("Did not find supertype of " + rhsType + " matching " + lhsType);
}
List<Type> lhsTypeArguments = lhsType.getTypeArguments();
List<Type> rhsTypeArguments = rhsType.getTypeArguments();
// This is impossible, considering the fact that standard Java subtyping succeeds before running
// NullAway
if (lhsTypeArguments.size() != rhsTypeArguments.size()) {
throw new RuntimeException(
"Number of types arguments in " + rhsType + " does not match " + lhsType);
}
for (int i = 0; i < lhsTypeArguments.size(); i++) {
Type lhsTypeArgument = lhsTypeArguments.get(i);
Type rhsTypeArgument = rhsTypeArguments.get(i);
boolean isLHSNullableAnnotated = false;
List<Attribute.TypeCompound> lhsAnnotations = lhsTypeArgument.getAnnotationMirrors();
// To ensure that we are checking only jspecify nullable annotations
for (Attribute.TypeCompound annotation : lhsAnnotations) {
if (annotation.getAnnotationType().toString().equals(NULLABLE_NAME)) {
isLHSNullableAnnotated = true;
break;
}
}
boolean isRHSNullableAnnotated = false;
List<Attribute.TypeCompound> rhsAnnotations = rhsTypeArgument.getAnnotationMirrors();
// To ensure that we are checking only jspecify nullable annotations
for (Attribute.TypeCompound annotation : rhsAnnotations) {
if (annotation.getAnnotationType().toString().equals(NULLABLE_NAME)) {
isRHSNullableAnnotated = true;
break;
}
}
if (isLHSNullableAnnotated != isRHSNullableAnnotated) {
reportInvalidAssignmentInstantiationError(tree, lhsType, rhsType, state, analysis);
return;
}
// nested generics
if (lhsTypeArgument.getTypeArguments().length() > 0) {
compareNullabilityAnnotations(
(Type.ClassType) lhsTypeArgument, (Type.ClassType) rhsTypeArgument, tree);
}
}
}

/**
NikitaAware marked this conversation as resolved.
Show resolved Hide resolved
* For the Parameterized typed trees, ASTHelpers.getType(tree) does not return a Type with
* preserved annotations. This method takes a Parameterized typed tree as an input and returns the
* Type of the tree with the annotations.
*
* @param tree A parameterized typed tree for which we need class type with preserved annotations.
* @return A Type with preserved annotations.
*/
private Type.ClassType typeWithPreservedAnnotations(ParameterizedTypeTree tree) {
Type.ClassType type = (Type.ClassType) ASTHelpers.getType(tree);
Preconditions.checkNotNull(type);
Type nullableType = NULLABLE_TYPE_SUPPLIER.get(state);
List<? extends Tree> typeArguments = tree.getTypeArguments();
List<Type> newTypeArgs = new ArrayList<>();
boolean hasNullableAnnotation = false;
for (int i = 0; i < typeArguments.size(); i++) {
AnnotatedTypeTree annotatedType = null;
Tree curTypeArg = typeArguments.get(i);
// If the type argument has an annotation, it will either be an AnnotatedTypeTree, or a
// ParameterizedTypeTree in the case of a nested generic type
if (curTypeArg instanceof AnnotatedTypeTree) {
annotatedType = (AnnotatedTypeTree) curTypeArg;
} else if (curTypeArg instanceof ParameterizedTypeTree
&& ((ParameterizedTypeTree) curTypeArg).getType() instanceof AnnotatedTypeTree) {
annotatedType = (AnnotatedTypeTree) ((ParameterizedTypeTree) curTypeArg).getType();
}
List<? extends AnnotationTree> annotations =
annotatedType != null ? annotatedType.getAnnotations() : Collections.emptyList();
for (AnnotationTree annotation : annotations) {
if (ASTHelpers.isSameType(
nullableType, ASTHelpers.getType(annotation.getAnnotationType()), state)) {
hasNullableAnnotation = true;
break;
}
}
// construct a TypeMetadata object containing a nullability annotation if needed
com.sun.tools.javac.util.List<Attribute.TypeCompound> nullableAnnotationCompound =
hasNullableAnnotation
? com.sun.tools.javac.util.List.from(
Collections.singletonList(
new Attribute.TypeCompound(
nullableType, com.sun.tools.javac.util.List.nil(), null)))
: com.sun.tools.javac.util.List.nil();
TypeMetadata typeMetadata =
new TypeMetadata(new TypeMetadata.Annotations(nullableAnnotationCompound));
msridhar marked this conversation as resolved.
Show resolved Hide resolved
Type currentTypeArgType = castToNonNull(ASTHelpers.getType(curTypeArg));
if (currentTypeArgType.getTypeArguments().size() > 0) {
// nested generic type; recursively preserve its nullability type argument annotations
currentTypeArgType = typeWithPreservedAnnotations((ParameterizedTypeTree) curTypeArg);
}
Type.ClassType newTypeArgType =
(Type.ClassType) currentTypeArgType.cloneWithMetadata(typeMetadata);
newTypeArgs.add(newTypeArgType);
}
Type.ClassType finalType =
new Type.ClassType(
type.getEnclosingType(), com.sun.tools.javac.util.List.from(newTypeArgs), type.tsym);
return finalType;
}
}
9 changes: 9 additions & 0 deletions nullaway/src/main/java/com/uber/nullaway/NullAway.java
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@ public Description matchAssignment(AssignmentTree tree, VisitorState state) {
if (lhsType != null && lhsType.isPrimitive()) {
doUnboxingCheck(state, tree.getExpression());
}
// generics check
if (lhsType != null && lhsType.getTypeArguments().length() > 0) {
new GenericsChecks(state, config, this).checkTypeParameterNullnessForAssignability(tree);
}

Symbol assigned = ASTHelpers.getSymbol(tree.getVariable());
if (assigned == null || assigned.getKind() != ElementKind.FIELD) {
// not a field of nullable type
Expand Down Expand Up @@ -1333,6 +1338,10 @@ public Description matchVariable(VariableTree tree, VisitorState state) {
return Description.NO_MATCH;
}
VarSymbol symbol = ASTHelpers.getSymbol(tree);
if (tree.getInitializer() != null) {
new GenericsChecks(state, config, this).checkTypeParameterNullnessForAssignability(tree);
}

if (symbol.type.isPrimitive() && tree.getInitializer() != null) {
doUnboxingCheck(state, tree.getInitializer());
}
Expand Down
Loading