private Description handleInvocation()

in nullaway/src/main/java/com/uber/nullaway/NullAway.java [1887:2019]


  private Description handleInvocation(
      Tree tree,
      VisitorState state,
      Symbol.MethodSymbol methodSymbol,
      List<? extends ExpressionTree> actualParams) {
    List<VarSymbol> formalParams = methodSymbol.getParameters();
    boolean varArgsMethod = methodSymbol.isVarArgs();

    // always do unboxing checks, whether or not the invoked method is annotated
    for (int i = 0; i < formalParams.size() && i < actualParams.size(); i++) {
      if (formalParams.get(i).type.isPrimitive()) {
        doUnboxingCheck(state, actualParams.get(i));
      }
    }
    boolean isMethodAnnotated =
        !codeAnnotationInfo.isSymbolUnannotated(methodSymbol, config, handler);
    // If argumentPositionNullness[i] == null, parameter i is unannotated
    @Nullable Nullness[] argumentPositionNullness = new Nullness[formalParams.size()];

    if (isMethodAnnotated) {
      // compute which arguments are @NonNull
      for (int i = 0; i < formalParams.size(); i++) {
        VarSymbol param = formalParams.get(i);
        if (param.type.isPrimitive()) {
          argumentPositionNullness[i] = Nullness.NONNULL;
        } else if (ASTHelpers.isSameType(
            param.type, Suppliers.JAVA_LANG_VOID_TYPE.get(state), state)) {
          // Temporarily treat a Void argument type as if it were @Nullable Void. Handling of Void
          // without special-casing, as recommended by JSpecify might: a) require generics support
          // and, b) require checking that third-party libraries considered annotated adopt
          // JSpecify semantics.
          // See the suppression in https://github.com/uber/NullAway/pull/608 for an example of why
          // this is needed.
          argumentPositionNullness[i] = Nullness.NULLABLE;
        } else {
          // we need to call paramHasNullableAnnotation here since the invoked method may be defined
          // in a class file
          argumentPositionNullness[i] =
              Nullness.paramHasNullableAnnotation(methodSymbol, i, config)
                  ? Nullness.NULLABLE
                  : ((config.isJSpecifyMode() && tree instanceof MethodInvocationTree)
                      ? genericsChecks.getGenericParameterNullnessAtInvocation(
                          i, methodSymbol, (MethodInvocationTree) tree, state, config)
                      : Nullness.NONNULL);
        }
      }
      if (config.isJSpecifyMode()) {
        genericsChecks.compareGenericTypeParameterNullabilityForCall(
            methodSymbol, tree, actualParams, varArgsMethod, this, state);
        if (!methodSymbol.getTypeParameters().isEmpty()) {
          GenericsChecks.checkGenericMethodCallTypeArguments(tree, state, this, config, handler);
        }
      }
    }

    // Allow handlers to override the list of non-null argument positions
    argumentPositionNullness =
        handler.onOverrideMethodInvocationParametersNullability(
            state.context, methodSymbol, isMethodAnnotated, argumentPositionNullness);

    // now actually check the arguments
    // NOTE: the case of an invocation on a possibly-null reference
    // is handled by matchMemberSelect()
    for (int argPos = 0; argPos < argumentPositionNullness.length; argPos++) {
      boolean varargPosition = varArgsMethod && argPos == formalParams.size() - 1;
      boolean argIsNonNull = Objects.equals(Nullness.NONNULL, argumentPositionNullness[argPos]);
      if (!varargPosition && !argIsNonNull) {
        continue;
      }
      ExpressionTree actual;
      boolean mayActualBeNull = false;
      if (varargPosition) {
        // Check all vararg actual arguments for nullability
        // This is the case where no actual parameter is passed for the var args parameter
        // (i.e. it defaults to an empty array)
        if (actualParams.size() <= argPos) {
          continue;
        }
        actual = actualParams.get(argPos);
        VarSymbol formalParamSymbol = formalParams.get(formalParams.size() - 1);
        boolean isVarArgsCall = isVarArgsCall(tree);
        if (isVarArgsCall) {
          // This is the case were varargs are being passed individually, as 1 or more actual
          // arguments starting at the position of the var args formal.
          // If the formal var args accepts `@Nullable`, then there is nothing for us to check.
          if (!argIsNonNull) {
            continue;
          }
          // TODO report all varargs errors in a single build; this code only reports the first
          //  error
          for (ExpressionTree arg : actualParams.subList(argPos, actualParams.size())) {
            actual = arg;
            mayActualBeNull = mayBeNullExpr(state, actual);
            if (mayActualBeNull) {
              break;
            }
          }
        } else {
          // This is the case where an array is explicitly passed in the position of the var args
          // parameter
          // Only check for a nullable varargs array if the method is annotated, or a @NonNull
          // restrictive annotation is present in legacy mode (as previously the annotation was
          // applied to both the array itself and the elements), or a JetBrains @NotNull declaration
          // annotation is present (due to https://github.com/uber/NullAway/issues/720)
          boolean checkForNullableVarargsArray =
              isMethodAnnotated
                  || (config.isLegacyAnnotationLocation() && argIsNonNull)
                  || NullabilityUtil.hasJetBrainsNotNullDeclarationAnnotation(formalParamSymbol);
          if (checkForNullableVarargsArray) {
            // If varargs array itself is not @Nullable, cannot pass @Nullable array
            if (!Nullness.varargsArrayIsNullable(formalParams.get(argPos), config)) {
              mayActualBeNull = mayBeNullExpr(state, actual);
            }
          }
        }
      } else { // not the vararg position
        actual = actualParams.get(argPos);
        mayActualBeNull = mayBeNullExpr(state, actual);
      }
      if (mayActualBeNull) {
        String message =
            "passing @Nullable parameter '"
                + state.getSourceForNode(actual)
                + "' where @NonNull is required";
        ErrorMessage errorMessage = new ErrorMessage(MessageTypes.PASS_NULLABLE, message);
        state.reportMatch(
            errorBuilder.createErrorDescriptionForNullAssignment(
                errorMessage, actual, buildDescription(actual), state, formalParams.get(argPos)));
      }
    }
    // Check for @NonNull being passed to castToNonNull (if configured)
    return checkCastToNonNullTakesNullable(tree, state, methodSymbol, actualParams);
  }