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