• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.uber.nullaway.generics;
2 
3 import static com.google.common.base.Verify.verify;
4 import static com.uber.nullaway.NullabilityUtil.castToNonNull;
5 
6 import com.google.errorprone.VisitorState;
7 import com.google.errorprone.suppliers.Supplier;
8 import com.google.errorprone.suppliers.Suppliers;
9 import com.google.errorprone.util.ASTHelpers;
10 import com.sun.source.tree.AnnotatedTypeTree;
11 import com.sun.source.tree.AnnotationTree;
12 import com.sun.source.tree.AssignmentTree;
13 import com.sun.source.tree.ConditionalExpressionTree;
14 import com.sun.source.tree.ExpressionTree;
15 import com.sun.source.tree.MemberSelectTree;
16 import com.sun.source.tree.MethodInvocationTree;
17 import com.sun.source.tree.MethodTree;
18 import com.sun.source.tree.NewClassTree;
19 import com.sun.source.tree.ParameterizedTypeTree;
20 import com.sun.source.tree.Tree;
21 import com.sun.source.tree.VariableTree;
22 import com.sun.source.util.TreePath;
23 import com.sun.tools.javac.code.Attribute;
24 import com.sun.tools.javac.code.Symbol;
25 import com.sun.tools.javac.code.Type;
26 import com.uber.nullaway.Config;
27 import com.uber.nullaway.ErrorBuilder;
28 import com.uber.nullaway.ErrorMessage;
29 import com.uber.nullaway.NullAway;
30 import com.uber.nullaway.Nullness;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import javax.annotation.Nullable;
35 import javax.lang.model.type.ExecutableType;
36 
37 /** Methods for performing checks related to generic types and nullability. */
38 public final class GenericsChecks {
39 
40   /**
41    * Supplier for the JSpecify {@code @Nullable} annotation. Required since for now, certain checks
42    * related to generics specifically look for {@code @org.jspecify.annotations.Nullable}
43    * annotations and do not apply to other {@code @Nullable} annotations.
44    */
45   static final Supplier<Type> JSPECIFY_NULLABLE_TYPE_SUPPLIER =
46       Suppliers.typeFromString("org.jspecify.annotations.Nullable");
47 
48   /** Do not instantiate; all methods should be static */
GenericsChecks()49   private GenericsChecks() {}
50 
51   /**
52    * Checks that for an instantiated generic type, {@code @Nullable} types are only used for type
53    * variables that have a {@code @Nullable} upper bound.
54    *
55    * @param tree the tree representing the instantiated type
56    * @param state visitor state
57    * @param analysis the analysis object
58    * @param config the analysis config
59    */
checkInstantiationForParameterizedTypedTree( ParameterizedTypeTree tree, VisitorState state, NullAway analysis, Config config)60   public static void checkInstantiationForParameterizedTypedTree(
61       ParameterizedTypeTree tree, VisitorState state, NullAway analysis, Config config) {
62     if (!config.isJSpecifyMode()) {
63       return;
64     }
65     List<? extends Tree> typeArguments = tree.getTypeArguments();
66     if (typeArguments.isEmpty()) {
67       return;
68     }
69     Map<Integer, Tree> nullableTypeArguments = new HashMap<>();
70     for (int i = 0; i < typeArguments.size(); i++) {
71       Tree curTypeArg = typeArguments.get(i);
72       if (curTypeArg instanceof AnnotatedTypeTree) {
73         AnnotatedTypeTree annotatedType = (AnnotatedTypeTree) curTypeArg;
74         for (AnnotationTree annotation : annotatedType.getAnnotations()) {
75           Type annotationType = ASTHelpers.getType(annotation);
76           if (annotationType != null
77               && Nullness.isNullableAnnotation(annotationType.toString(), config)) {
78             nullableTypeArguments.put(i, curTypeArg);
79             break;
80           }
81         }
82       }
83     }
84     // base type that is being instantiated
85     Type baseType = ASTHelpers.getType(tree);
86     if (baseType == null) {
87       return;
88     }
89     com.sun.tools.javac.util.List<Type> baseTypeArgs = baseType.tsym.type.getTypeArguments();
90     for (int i = 0; i < baseTypeArgs.size(); i++) {
91       if (nullableTypeArguments.containsKey(i)) {
92 
93         Type typeVariable = baseTypeArgs.get(i);
94         Type upperBound = typeVariable.getUpperBound();
95         com.sun.tools.javac.util.List<Attribute.TypeCompound> annotationMirrors =
96             upperBound.getAnnotationMirrors();
97         boolean hasNullableAnnotation =
98             Nullness.hasNullableAnnotation(annotationMirrors.stream(), config);
99         // if base type argument does not have @Nullable annotation then the instantiation is
100         // invalid
101         if (!hasNullableAnnotation) {
102           reportInvalidInstantiationError(
103               nullableTypeArguments.get(i), baseType, typeVariable, state, analysis);
104         }
105       }
106     }
107   }
108 
reportInvalidInstantiationError( Tree tree, Type baseType, Type baseTypeVariable, VisitorState state, NullAway analysis)109   private static void reportInvalidInstantiationError(
110       Tree tree, Type baseType, Type baseTypeVariable, VisitorState state, NullAway analysis) {
111     ErrorBuilder errorBuilder = analysis.getErrorBuilder();
112     ErrorMessage errorMessage =
113         new ErrorMessage(
114             ErrorMessage.MessageTypes.TYPE_PARAMETER_CANNOT_BE_NULLABLE,
115             String.format(
116                 "Generic type parameter cannot be @Nullable, as type variable %s of type %s does not have a @Nullable upper bound",
117                 baseTypeVariable.tsym.toString(), baseType.tsym.toString()));
118     state.reportMatch(
119         errorBuilder.createErrorDescription(
120             errorMessage, analysis.buildDescription(tree), state, null));
121   }
122 
reportInvalidAssignmentInstantiationError( Tree tree, Type lhsType, Type rhsType, VisitorState state, NullAway analysis)123   private static void reportInvalidAssignmentInstantiationError(
124       Tree tree, Type lhsType, Type rhsType, VisitorState state, NullAway analysis) {
125     ErrorBuilder errorBuilder = analysis.getErrorBuilder();
126     ErrorMessage errorMessage =
127         new ErrorMessage(
128             ErrorMessage.MessageTypes.ASSIGN_GENERIC_NULLABLE,
129             String.format(
130                 "Cannot assign from type "
131                     + prettyTypeForError(rhsType, state)
132                     + " to type "
133                     + prettyTypeForError(lhsType, state)
134                     + " due to mismatched nullability of type parameters"));
135     state.reportMatch(
136         errorBuilder.createErrorDescription(
137             errorMessage, analysis.buildDescription(tree), state, null));
138   }
139 
reportInvalidReturnTypeError( Tree tree, Type methodType, Type returnType, VisitorState state, NullAway analysis)140   private static void reportInvalidReturnTypeError(
141       Tree tree, Type methodType, Type returnType, VisitorState state, NullAway analysis) {
142     ErrorBuilder errorBuilder = analysis.getErrorBuilder();
143     ErrorMessage errorMessage =
144         new ErrorMessage(
145             ErrorMessage.MessageTypes.RETURN_NULLABLE_GENERIC,
146             String.format(
147                 "Cannot return expression of type "
148                     + prettyTypeForError(returnType, state)
149                     + " from method with return type "
150                     + prettyTypeForError(methodType, state)
151                     + " due to mismatched nullability of type parameters"));
152     state.reportMatch(
153         errorBuilder.createErrorDescription(
154             errorMessage, analysis.buildDescription(tree), state, null));
155   }
156 
reportMismatchedTypeForTernaryOperator( Tree tree, Type expressionType, Type subPartType, VisitorState state, NullAway analysis)157   private static void reportMismatchedTypeForTernaryOperator(
158       Tree tree, Type expressionType, Type subPartType, VisitorState state, NullAway analysis) {
159     ErrorBuilder errorBuilder = analysis.getErrorBuilder();
160     ErrorMessage errorMessage =
161         new ErrorMessage(
162             ErrorMessage.MessageTypes.ASSIGN_GENERIC_NULLABLE,
163             String.format(
164                 "Conditional expression must have type "
165                     + prettyTypeForError(expressionType, state)
166                     + " but the sub-expression has type "
167                     + prettyTypeForError(subPartType, state)
168                     + ", which has mismatched nullability of type parameters"));
169     state.reportMatch(
170         errorBuilder.createErrorDescription(
171             errorMessage, analysis.buildDescription(tree), state, null));
172   }
173 
reportInvalidParametersNullabilityError( Type formalParameterType, Type actualParameterType, ExpressionTree paramExpression, VisitorState state, NullAway analysis)174   private static void reportInvalidParametersNullabilityError(
175       Type formalParameterType,
176       Type actualParameterType,
177       ExpressionTree paramExpression,
178       VisitorState state,
179       NullAway analysis) {
180     ErrorBuilder errorBuilder = analysis.getErrorBuilder();
181     ErrorMessage errorMessage =
182         new ErrorMessage(
183             ErrorMessage.MessageTypes.PASS_NULLABLE_GENERIC,
184             "Cannot pass parameter of type "
185                 + prettyTypeForError(actualParameterType, state)
186                 + ", as formal parameter has type "
187                 + prettyTypeForError(formalParameterType, state)
188                 + ", which has mismatched type parameter nullability");
189     state.reportMatch(
190         errorBuilder.createErrorDescription(
191             errorMessage, analysis.buildDescription(paramExpression), state, null));
192   }
193 
reportInvalidOverridingMethodReturnTypeError( Tree methodTree, Type overriddenMethodReturnType, Type overridingMethodReturnType, NullAway analysis, VisitorState state)194   private static void reportInvalidOverridingMethodReturnTypeError(
195       Tree methodTree,
196       Type overriddenMethodReturnType,
197       Type overridingMethodReturnType,
198       NullAway analysis,
199       VisitorState state) {
200     ErrorBuilder errorBuilder = analysis.getErrorBuilder();
201     ErrorMessage errorMessage =
202         new ErrorMessage(
203             ErrorMessage.MessageTypes.WRONG_OVERRIDE_RETURN_GENERIC,
204             "Method returns "
205                 + prettyTypeForError(overridingMethodReturnType, state)
206                 + ", but overridden method returns "
207                 + prettyTypeForError(overriddenMethodReturnType, state)
208                 + ", which has mismatched type parameter nullability");
209     state.reportMatch(
210         errorBuilder.createErrorDescription(
211             errorMessage, analysis.buildDescription(methodTree), state, null));
212   }
213 
reportInvalidOverridingMethodParamTypeError( Tree formalParameterTree, Type typeParameterType, Type methodParamType, NullAway analysis, VisitorState state)214   private static void reportInvalidOverridingMethodParamTypeError(
215       Tree formalParameterTree,
216       Type typeParameterType,
217       Type methodParamType,
218       NullAway analysis,
219       VisitorState state) {
220     ErrorBuilder errorBuilder = analysis.getErrorBuilder();
221     ErrorMessage errorMessage =
222         new ErrorMessage(
223             ErrorMessage.MessageTypes.WRONG_OVERRIDE_PARAM_GENERIC,
224             "Parameter has type "
225                 + prettyTypeForError(methodParamType, state)
226                 + ", but overridden method has parameter type "
227                 + prettyTypeForError(typeParameterType, state)
228                 + ", which has mismatched type parameter nullability");
229     state.reportMatch(
230         errorBuilder.createErrorDescription(
231             errorMessage, analysis.buildDescription(formalParameterTree), state, null));
232   }
233 
234   /**
235    * This method returns the type of the given tree, including any type use annotations.
236    *
237    * <p>This method is required because in some cases, the type returned by {@link
238    * com.google.errorprone.util.ASTHelpers#getType(Tree)} fails to preserve type use annotations,
239    * particularly when dealing with {@link com.sun.source.tree.NewClassTree} (e.g., {@code new
240    * Foo<@Nullable A>}).
241    *
242    * @param tree A tree for which we need the type with preserved annotations.
243    * @param state the visitor state
244    * @return Type of the tree with preserved annotations.
245    */
246   @Nullable
getTreeType(Tree tree, VisitorState state)247   private static Type getTreeType(Tree tree, VisitorState state) {
248     if (tree instanceof NewClassTree
249         && ((NewClassTree) tree).getIdentifier() instanceof ParameterizedTypeTree) {
250       ParameterizedTypeTree paramTypedTree =
251           (ParameterizedTypeTree) ((NewClassTree) tree).getIdentifier();
252       if (paramTypedTree.getTypeArguments().isEmpty()) {
253         // diamond operator, which we do not yet support; for now, return null
254         // TODO: support diamond operators
255         return null;
256       }
257       return typeWithPreservedAnnotations(paramTypedTree, state);
258     } else {
259       Type result = ASTHelpers.getType(tree);
260       if (result != null && result.isRaw()) {
261         // bail out of any checking involving raw types for now
262         return null;
263       }
264       return result;
265     }
266   }
267 
268   /**
269    * For a tree representing an assignment, ensures that from the perspective of type parameter
270    * nullability, the type of the right-hand side is assignable to (a subtype of) the type of the
271    * left-hand side. This check ensures that for every parameterized type nested in each of the
272    * types, the type parameters have identical nullability.
273    *
274    * @param tree the tree to check, which must be either an {@link AssignmentTree} or a {@link
275    *     VariableTree}
276    * @param analysis the analysis object
277    * @param state the visitor state
278    */
checkTypeParameterNullnessForAssignability( Tree tree, NullAway analysis, VisitorState state)279   public static void checkTypeParameterNullnessForAssignability(
280       Tree tree, NullAway analysis, VisitorState state) {
281     if (!analysis.getConfig().isJSpecifyMode()) {
282       return;
283     }
284     Tree lhsTree;
285     Tree rhsTree;
286     if (tree instanceof VariableTree) {
287       VariableTree varTree = (VariableTree) tree;
288       lhsTree = varTree.getType();
289       rhsTree = varTree.getInitializer();
290     } else {
291       AssignmentTree assignmentTree = (AssignmentTree) tree;
292       lhsTree = assignmentTree.getVariable();
293       rhsTree = assignmentTree.getExpression();
294     }
295     // rhsTree can be null for a VariableTree.  Also, we don't need to do a check
296     // if rhsTree is the null literal
297     if (rhsTree == null || rhsTree.getKind().equals(Tree.Kind.NULL_LITERAL)) {
298       return;
299     }
300     Type lhsType = getTreeType(lhsTree, state);
301     Type rhsType = getTreeType(rhsTree, state);
302 
303     if (lhsType != null && rhsType != null) {
304       boolean isAssignmentValid = compareNullabilityAnnotations(lhsType, rhsType, state);
305       if (!isAssignmentValid) {
306         reportInvalidAssignmentInstantiationError(tree, lhsType, rhsType, state, analysis);
307       }
308     }
309   }
310 
311   /**
312    * Checks that the nullability of type parameters for a returned expression matches that of the
313    * type parameters of the enclosing method's return type.
314    *
315    * @param retExpr the returned expression
316    * @param methodSymbol symbol for enclosing method
317    * @param analysis the analysis object
318    * @param state the visitor state
319    */
checkTypeParameterNullnessForFunctionReturnType( ExpressionTree retExpr, Symbol.MethodSymbol methodSymbol, NullAway analysis, VisitorState state)320   public static void checkTypeParameterNullnessForFunctionReturnType(
321       ExpressionTree retExpr,
322       Symbol.MethodSymbol methodSymbol,
323       NullAway analysis,
324       VisitorState state) {
325     if (!analysis.getConfig().isJSpecifyMode()) {
326       return;
327     }
328 
329     Type formalReturnType = methodSymbol.getReturnType();
330     // check nullability of parameters only for generics
331     if (formalReturnType.getTypeArguments().isEmpty()) {
332       return;
333     }
334     Type returnExpressionType = getTreeType(retExpr, state);
335     if (formalReturnType != null && returnExpressionType != null) {
336       boolean isReturnTypeValid =
337           compareNullabilityAnnotations(formalReturnType, returnExpressionType, state);
338       if (!isReturnTypeValid) {
339         reportInvalidReturnTypeError(
340             retExpr, formalReturnType, returnExpressionType, state, analysis);
341       }
342     }
343   }
344 
345   /**
346    * Compare two types from an assignment for identical type parameter nullability, recursively
347    * checking nested generic types. See <a
348    * href="https://jspecify.dev/docs/spec/#nullness-delegating-subtyping">the JSpecify
349    * specification</a> and <a
350    * href="https://docs.oracle.com/javase/specs/jls/se14/html/jls-4.html#jls-4.10.2">the JLS
351    * subtyping rules for class and interface types</a>.
352    *
353    * @param lhsType type for the lhs of the assignment
354    * @param rhsType type for the rhs of the assignment
355    * @param state the visitor state
356    */
compareNullabilityAnnotations( Type lhsType, Type rhsType, VisitorState state)357   private static boolean compareNullabilityAnnotations(
358       Type lhsType, Type rhsType, VisitorState state) {
359     // it is fair to assume rhyType should be the same as lhsType as the Java compiler has passed
360     // before NullAway.
361     return lhsType.accept(new CompareNullabilityVisitor(state), rhsType);
362   }
363 
364   /**
365    * For the Parameterized typed trees, ASTHelpers.getType(tree) does not return a Type with
366    * preserved annotations. This method takes a Parameterized typed tree as an input and returns the
367    * Type of the tree with the annotations.
368    *
369    * @param tree A parameterized typed tree for which we need class type with preserved annotations.
370    * @param state the visitor state
371    * @return A Type with preserved annotations.
372    */
typeWithPreservedAnnotations( ParameterizedTypeTree tree, VisitorState state)373   private static Type.ClassType typeWithPreservedAnnotations(
374       ParameterizedTypeTree tree, VisitorState state) {
375     return (Type.ClassType) tree.accept(new PreservedAnnotationTreeVisitor(state), null);
376   }
377 
378   /**
379    * For a conditional expression <em>c</em>, check whether the type parameter nullability for each
380    * sub-expression of <em>c</em> matches the type parameter nullability of <em>c</em> itself.
381    *
382    * <p>Note that the type parameter nullability for <em>c</em> is computed by javac and reflects
383    * what is required of the surrounding context (an assignment, parameter pass, etc.). It is
384    * possible that both sub-expressions of <em>c</em> will have identical type parameter
385    * nullability, but will still not match the type parameter nullability of <em>c</em> itself, due
386    * to requirements from the surrounding context. In such a case, our error messages may be
387    * somewhat confusing; we may want to improve this in the future.
388    *
389    * @param tree A conditional expression tree to check
390    * @param analysis the analysis object
391    * @param state the visitor state
392    */
checkTypeParameterNullnessForConditionalExpression( ConditionalExpressionTree tree, NullAway analysis, VisitorState state)393   public static void checkTypeParameterNullnessForConditionalExpression(
394       ConditionalExpressionTree tree, NullAway analysis, VisitorState state) {
395     if (!analysis.getConfig().isJSpecifyMode()) {
396       return;
397     }
398 
399     Tree truePartTree = tree.getTrueExpression();
400     Tree falsePartTree = tree.getFalseExpression();
401 
402     Type condExprType = getTreeType(tree, state);
403     Type truePartType = getTreeType(truePartTree, state);
404     Type falsePartType = getTreeType(falsePartTree, state);
405     // The condExpr type should be the least-upper bound of the true and false part types.  To check
406     // the nullability annotations, we check that the true and false parts are assignable to the
407     // type of the whole expression
408     if (condExprType != null) {
409       if (truePartType != null) {
410         if (!compareNullabilityAnnotations(condExprType, truePartType, state)) {
411           reportMismatchedTypeForTernaryOperator(
412               truePartTree, condExprType, truePartType, state, analysis);
413         }
414       }
415       if (falsePartType != null) {
416         if (!compareNullabilityAnnotations(condExprType, falsePartType, state)) {
417           reportMismatchedTypeForTernaryOperator(
418               falsePartTree, condExprType, falsePartType, state, analysis);
419         }
420       }
421     }
422   }
423 
424   /**
425    * Checks that for each parameter p at a call, the type parameter nullability for p's type matches
426    * that of the corresponding formal parameter. If a mismatch is found, report an error.
427    *
428    * @param formalParams the formal parameters
429    * @param actualParams the actual parameters
430    * @param isVarArgs true if the call is to a varargs method
431    * @param analysis the analysis object
432    * @param state the visitor state
433    */
compareGenericTypeParameterNullabilityForCall( List<Symbol.VarSymbol> formalParams, List<? extends ExpressionTree> actualParams, boolean isVarArgs, NullAway analysis, VisitorState state)434   public static void compareGenericTypeParameterNullabilityForCall(
435       List<Symbol.VarSymbol> formalParams,
436       List<? extends ExpressionTree> actualParams,
437       boolean isVarArgs,
438       NullAway analysis,
439       VisitorState state) {
440     if (!analysis.getConfig().isJSpecifyMode()) {
441       return;
442     }
443     int n = formalParams.size();
444     if (isVarArgs) {
445       // If the last argument is var args, don't check it now, it will be checked against
446       // all remaining actual arguments in the next loop.
447       n = n - 1;
448     }
449     for (int i = 0; i < n; i++) {
450       Type formalParameter = formalParams.get(i).type;
451       if (!formalParameter.getTypeArguments().isEmpty()) {
452         Type actualParameter = getTreeType(actualParams.get(i), state);
453         if (actualParameter != null) {
454           if (!compareNullabilityAnnotations(formalParameter, actualParameter, state)) {
455             reportInvalidParametersNullabilityError(
456                 formalParameter, actualParameter, actualParams.get(i), state, analysis);
457           }
458         }
459       }
460     }
461     if (isVarArgs && !formalParams.isEmpty()) {
462       Type.ArrayType varargsArrayType =
463           (Type.ArrayType) formalParams.get(formalParams.size() - 1).type;
464       Type varargsElementType = varargsArrayType.elemtype;
465       if (!varargsElementType.getTypeArguments().isEmpty()) {
466         for (int i = formalParams.size() - 1; i < actualParams.size(); i++) {
467           Type actualParameter = getTreeType(actualParams.get(i), state);
468           if (actualParameter != null) {
469             if (!compareNullabilityAnnotations(varargsElementType, actualParameter, state)) {
470               reportInvalidParametersNullabilityError(
471                   varargsElementType, actualParameter, actualParams.get(i), state, analysis);
472             }
473           }
474         }
475       }
476     }
477   }
478 
479   /**
480    * Checks that type parameter nullability is consistent between an overriding method and the
481    * corresponding overridden method.
482    *
483    * @param tree A method tree to check
484    * @param overridingMethod A symbol of the overriding method
485    * @param overriddenMethod A symbol of the overridden method
486    * @param analysis the analysis object
487    * @param state the visitor state
488    */
checkTypeParameterNullnessForMethodOverriding( MethodTree tree, Symbol.MethodSymbol overridingMethod, Symbol.MethodSymbol overriddenMethod, NullAway analysis, VisitorState state)489   public static void checkTypeParameterNullnessForMethodOverriding(
490       MethodTree tree,
491       Symbol.MethodSymbol overridingMethod,
492       Symbol.MethodSymbol overriddenMethod,
493       NullAway analysis,
494       VisitorState state) {
495     if (!analysis.getConfig().isJSpecifyMode()) {
496       return;
497     }
498     // Obtain type parameters for the overridden method within the context of the overriding
499     // method's class
500     Type methodWithTypeParams =
501         state.getTypes().memberType(overridingMethod.owner.type, overriddenMethod);
502 
503     checkTypeParameterNullnessForOverridingMethodReturnType(
504         tree, methodWithTypeParams, analysis, state);
505     checkTypeParameterNullnessForOverridingMethodParameterType(
506         tree, methodWithTypeParams, analysis, state);
507   }
508 
509   /**
510    * Computes the nullability of the return type of some generic method when seen as a member of
511    * some class {@code C}, based on type parameter nullability within {@code C}.
512    *
513    * <p>Consider the following example:
514    *
515    * <pre>
516    *     interface Fn<P extends @Nullable Object, R extends @Nullable Object> {
517    *         R apply(P p);
518    *     }
519    *     class C implements Fn<String, @Nullable String> {
520    *         public @Nullable String apply(String p) {
521    *             return null;
522    *         }
523    *     }
524    * </pre>
525    *
526    * Within the context of class {@code C}, the method {@code Fn.apply} has a return type of
527    * {@code @Nullable String}, since {@code @Nullable String} is passed as the type parameter for
528    * {@code R}. Hence, it is valid for overriding method {@code C.apply} to return {@code @Nullable
529    * String}.
530    *
531    * @param method the generic method
532    * @param enclosingSymbol the enclosing class in which we want to know {@code method}'s return
533    *     type nullability
534    * @param state Visitor state
535    * @param config The analysis config
536    * @return nullability of the return type of {@code method} in the context of {@code
537    *     enclosingType}
538    */
getGenericMethodReturnTypeNullness( Symbol.MethodSymbol method, Symbol enclosingSymbol, VisitorState state, Config config)539   public static Nullness getGenericMethodReturnTypeNullness(
540       Symbol.MethodSymbol method, Symbol enclosingSymbol, VisitorState state, Config config) {
541     Type enclosingType = getTypeForSymbol(enclosingSymbol, state);
542     return getGenericMethodReturnTypeNullness(method, enclosingType, state, config);
543   }
544 
545   /**
546    * Get the type for the symbol, accounting for anonymous classes
547    *
548    * @param symbol the symbol
549    * @param state the visitor state
550    * @return the type for {@code symbol}
551    */
552   @Nullable
getTypeForSymbol(Symbol symbol, VisitorState state)553   private static Type getTypeForSymbol(Symbol symbol, VisitorState state) {
554     if (symbol.isAnonymous()) {
555       // For anonymous classes, symbol.type does not contain annotations on generic type parameters.
556       // So, we get a correct type from the enclosing NewClassTree.
557       TreePath path = state.getPath();
558       NewClassTree newClassTree = ASTHelpers.findEnclosingNode(path, NewClassTree.class);
559       if (newClassTree == null) {
560         throw new RuntimeException(
561             "method should be inside a NewClassTree " + state.getSourceForNode(path.getLeaf()));
562       }
563       Type typeFromTree = getTreeType(newClassTree, state);
564       if (typeFromTree != null) {
565         verify(state.getTypes().isAssignable(symbol.type, typeFromTree));
566       }
567       return typeFromTree;
568     } else {
569       return symbol.type;
570     }
571   }
572 
getGenericMethodReturnTypeNullness( Symbol.MethodSymbol method, @Nullable Type enclosingType, VisitorState state, Config config)573   public static Nullness getGenericMethodReturnTypeNullness(
574       Symbol.MethodSymbol method, @Nullable Type enclosingType, VisitorState state, Config config) {
575     if (enclosingType == null) {
576       // we have no additional information from generics, so return NONNULL (presence of a @Nullable
577       // annotation should have been handled by the caller)
578       return Nullness.NONNULL;
579     }
580     Type overriddenMethodType = state.getTypes().memberType(enclosingType, method);
581     verify(
582         overriddenMethodType instanceof ExecutableType,
583         "expected ExecutableType but instead got %s",
584         overriddenMethodType.getClass());
585     return getTypeNullness(overriddenMethodType.getReturnType(), config);
586   }
587 
588   /**
589    * Computes the nullness of the return of a generic method at an invocation, in the context of the
590    * declared type of its receiver argument. If the return type is a type variable, its nullness
591    * depends on the nullability of the corresponding type parameter in the receiver's type.
592    *
593    * <p>Consider the following example:
594    *
595    * <pre>
596    *     interface Fn<P extends @Nullable Object, R extends @Nullable Object> {
597    *         R apply(P p);
598    *     }
599    *     class C implements Fn<String, @Nullable String> {
600    *         public @Nullable String apply(String p) {
601    *             return null;
602    *         }
603    *     }
604    *     static void m() {
605    *         Fn<String, @Nullable String> f = new C();
606    *         f.apply("hello").hashCode(); // NPE
607    *     }
608    * </pre>
609    *
610    * The declared type of {@code f} passes {@code Nullable String} as the type parameter for type
611    * variable {@code R}. So, the call {@code f.apply("hello")} returns {@code @Nullable} and an
612    * error should be reported.
613    *
614    * @param invokedMethodSymbol symbol for the invoked method
615    * @param tree the tree for the invocation
616    * @return Nullness of invocation's return type, or {@code NONNULL} if the call does not invoke an
617    *     instance method
618    */
getGenericReturnNullnessAtInvocation( Symbol.MethodSymbol invokedMethodSymbol, MethodInvocationTree tree, VisitorState state, Config config)619   public static Nullness getGenericReturnNullnessAtInvocation(
620       Symbol.MethodSymbol invokedMethodSymbol,
621       MethodInvocationTree tree,
622       VisitorState state,
623       Config config) {
624     if (!(tree.getMethodSelect() instanceof MemberSelectTree) || invokedMethodSymbol.isStatic()) {
625       return Nullness.NONNULL;
626     }
627     Type methodReceiverType =
628         getTreeType(((MemberSelectTree) tree.getMethodSelect()).getExpression(), state);
629     if (methodReceiverType == null) {
630       return Nullness.NONNULL;
631     } else {
632       return getGenericMethodReturnTypeNullness(
633           invokedMethodSymbol, methodReceiverType, state, config);
634     }
635   }
636 
637   /**
638    * Computes the nullness of a formal parameter of a generic method at an invocation, in the
639    * context of the declared type of its receiver argument. If the formal parameter's type is a type
640    * variable, its nullness depends on the nullability of the corresponding type parameter in the
641    * receiver's type.
642    *
643    * <p>Consider the following example:
644    *
645    * <pre>
646    *     interface Fn<P extends @Nullable Object, R extends @Nullable Object> {
647    *         R apply(P p);
648    *     }
649    *     class C implements Fn<@Nullable String, String> {
650    *         public String apply(@Nullable String p) {
651    *             return "";
652    *         }
653    *     }
654    *     static void m() {
655    *         Fn<@Nullable String, String> f = new C();
656    *         f.apply(null);
657    *     }
658    * </pre>
659    *
660    * The declared type of {@code f} passes {@code Nullable String} as the type parameter for type
661    * variable {@code P}. So, it is legal to pass {@code null} as a parameter to {@code f.apply}.
662    *
663    * @param paramIndex parameter index
664    * @param invokedMethodSymbol symbol for the invoked method
665    * @param tree the tree for the invocation
666    * @param state the visitor state
667    * @param config the analysis config
668    * @return Nullness of parameter at {@code paramIndex}, or {@code NONNULL} if the call does not
669    *     invoke an instance method
670    */
getGenericParameterNullnessAtInvocation( int paramIndex, Symbol.MethodSymbol invokedMethodSymbol, MethodInvocationTree tree, VisitorState state, Config config)671   public static Nullness getGenericParameterNullnessAtInvocation(
672       int paramIndex,
673       Symbol.MethodSymbol invokedMethodSymbol,
674       MethodInvocationTree tree,
675       VisitorState state,
676       Config config) {
677     if (!(tree.getMethodSelect() instanceof MemberSelectTree) || invokedMethodSymbol.isStatic()) {
678       return Nullness.NONNULL;
679     }
680     Type enclosingType =
681         castToNonNull(
682             getTreeType(((MemberSelectTree) tree.getMethodSelect()).getExpression(), state));
683     return getGenericMethodParameterNullness(
684         paramIndex, invokedMethodSymbol, enclosingType, state, config);
685   }
686 
687   /**
688    * Computes the nullability of a parameter type of some generic method when seen as a member of
689    * some class {@code C}, based on type parameter nullability within {@code C}.
690    *
691    * <p>Consider the following example:
692    *
693    * <pre>
694    *     interface Fn<P extends @Nullable Object, R extends @Nullable Object> {
695    *         R apply(P p);
696    *     }
697    *     class C implements Fn<@Nullable String, String> {
698    *         public String apply(@Nullable String p) {
699    *             return "";
700    *         }
701    *     }
702    * </pre>
703    *
704    * Within the context of class {@code C}, the method {@code Fn.apply} has a parameter type of
705    * {@code @Nullable String}, since {@code @Nullable String} is passed as the type parameter for
706    * {@code P}. Hence, overriding method {@code C.apply} must take a {@code @Nullable String} as a
707    * parameter.
708    *
709    * @param parameterIndex index of the parameter
710    * @param method the generic method
711    * @param enclosingSymbol the enclosing symbol in which we want to know {@code method}'s parameter
712    *     type nullability
713    * @param state the visitor state
714    * @param config the config
715    * @return nullability of the relevant parameter type of {@code method} in the context of {@code
716    *     enclosingSymbol}
717    */
getGenericMethodParameterNullness( int parameterIndex, Symbol.MethodSymbol method, Symbol enclosingSymbol, VisitorState state, Config config)718   public static Nullness getGenericMethodParameterNullness(
719       int parameterIndex,
720       Symbol.MethodSymbol method,
721       Symbol enclosingSymbol,
722       VisitorState state,
723       Config config) {
724     Type enclosingType = getTypeForSymbol(enclosingSymbol, state);
725     return getGenericMethodParameterNullness(parameterIndex, method, enclosingType, state, config);
726   }
727 
728   /**
729    * Just like {@link #getGenericMethodParameterNullness(int, Symbol.MethodSymbol, Symbol,
730    * VisitorState, Config)}, but takes the enclosing {@code Type} rather than the enclosing {@code
731    * Symbol}.
732    *
733    * @param parameterIndex index of the parameter
734    * @param method the generic method
735    * @param enclosingType the enclosing type in which we want to know {@code method}'s parameter
736    *     type nullability
737    * @param state the visitor state
738    * @param config the analysis config
739    * @return nullability of the relevant parameter type of {@code method} in the context of {@code
740    *     enclosingType}
741    */
getGenericMethodParameterNullness( int parameterIndex, Symbol.MethodSymbol method, @Nullable Type enclosingType, VisitorState state, Config config)742   public static Nullness getGenericMethodParameterNullness(
743       int parameterIndex,
744       Symbol.MethodSymbol method,
745       @Nullable Type enclosingType,
746       VisitorState state,
747       Config config) {
748     if (enclosingType == null) {
749       // we have no additional information from generics, so return NONNULL (presence of a top-level
750       // @Nullable annotation is handled elsewhere)
751       return Nullness.NONNULL;
752     }
753     Type methodType = state.getTypes().memberType(enclosingType, method);
754     Type paramType = methodType.getParameterTypes().get(parameterIndex);
755     return getTypeNullness(paramType, config);
756   }
757 
758   /**
759    * This method compares the type parameter annotations for overriding method parameters with
760    * corresponding type parameters for the overridden method and reports an error if they don't
761    * match
762    *
763    * @param tree tree for overriding method
764    * @param overriddenMethodType type of the overridden method
765    * @param analysis the analysis object
766    * @param state the visitor state
767    */
checkTypeParameterNullnessForOverridingMethodParameterType( MethodTree tree, Type overriddenMethodType, NullAway analysis, VisitorState state)768   private static void checkTypeParameterNullnessForOverridingMethodParameterType(
769       MethodTree tree, Type overriddenMethodType, NullAway analysis, VisitorState state) {
770     List<? extends VariableTree> methodParameters = tree.getParameters();
771     List<Type> overriddenMethodParameterTypes = overriddenMethodType.getParameterTypes();
772     // TODO handle varargs; they are not handled for now
773     for (int i = 0; i < methodParameters.size(); i++) {
774       Type overridingMethodParameterType = getTreeType(methodParameters.get(i), state);
775       Type overriddenMethodParameterType = overriddenMethodParameterTypes.get(i);
776       if (overriddenMethodParameterType != null && overridingMethodParameterType != null) {
777         if (!compareNullabilityAnnotations(
778             overriddenMethodParameterType, overridingMethodParameterType, state)) {
779           reportInvalidOverridingMethodParamTypeError(
780               methodParameters.get(i),
781               overriddenMethodParameterType,
782               overridingMethodParameterType,
783               analysis,
784               state);
785         }
786       }
787     }
788   }
789 
790   /**
791    * This method compares the type parameter annotations for an overriding method's return type with
792    * corresponding type parameters for the overridden method and reports an error if they don't
793    * match
794    *
795    * @param tree tree for overriding method
796    * @param overriddenMethodType type of the overridden method
797    * @param analysis the analysis object
798    * @param state the visitor state
799    */
checkTypeParameterNullnessForOverridingMethodReturnType( MethodTree tree, Type overriddenMethodType, NullAway analysis, VisitorState state)800   private static void checkTypeParameterNullnessForOverridingMethodReturnType(
801       MethodTree tree, Type overriddenMethodType, NullAway analysis, VisitorState state) {
802     Type overriddenMethodReturnType = overriddenMethodType.getReturnType();
803     Type overridingMethodReturnType = getTreeType(tree.getReturnType(), state);
804     if (overriddenMethodReturnType == null || overridingMethodReturnType == null) {
805       return;
806     }
807     if (!compareNullabilityAnnotations(
808         overriddenMethodReturnType, overridingMethodReturnType, state)) {
809       reportInvalidOverridingMethodReturnTypeError(
810           tree, overriddenMethodReturnType, overridingMethodReturnType, analysis, state);
811     }
812   }
813 
814   /**
815    * @param type A type for which we need the Nullness.
816    * @param config The analysis config
817    * @return Returns the Nullness of the type based on the Nullability annotation.
818    */
getTypeNullness(Type type, Config config)819   private static Nullness getTypeNullness(Type type, Config config) {
820     boolean hasNullableAnnotation =
821         Nullness.hasNullableAnnotation(type.getAnnotationMirrors().stream(), config);
822     if (hasNullableAnnotation) {
823       return Nullness.NULLABLE;
824     }
825     return Nullness.NONNULL;
826   }
827 
828   /**
829    * Returns a pretty-printed representation of type suitable for error messages. The representation
830    * uses simple names rather than fully-qualified names, and retains all type-use annotations.
831    */
prettyTypeForError(Type type, VisitorState state)832   public static String prettyTypeForError(Type type, VisitorState state) {
833     return type.accept(new GenericTypePrettyPrintingVisitor(state), null);
834   }
835 }
836