private Description handleInvocation()

in nullaway/src/main/java/com/uber/nullaway/NullAway.java [1721:1824]


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

    if (formalParams.size() != actualParams.size()
        && !methodSymbol.isVarArgs()
        && !methodSymbol.isStatic()
        && methodSymbol.isConstructor()
        && methodSymbol.enclClass().isInner()) {
      // In special cases like one in issue #366
      // formal params and actual params do not match while using JDK11+
      // we bail out in this particular case
      return Description.NO_MATCH;
    }

    final boolean isMethodAnnotated =
        !codeAnnotationInfo.isSymbolUnannotated(methodSymbol, config, handler);
    // If argumentPositionNullness[i] == null, parameter i is unannotated
    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()) {
          doUnboxingCheck(state, actualParams.get(i));
          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(
            formalParams, actualParams, methodSymbol.isVarArgs(), this, state);
      }
    }

    // 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++) {
      if (!Objects.equals(Nullness.NONNULL, argumentPositionNullness[argPos])) {
        continue;
      }
      ExpressionTree actual = null;
      boolean mayActualBeNull = false;
      if (argPos == formalParams.size() - 1 && methodSymbol.isVarArgs()) {
        // Check all vararg actual arguments for nullability
        if (actualParams.size() <= argPos) {
          continue;
        }
        for (ExpressionTree arg : actualParams.subList(argPos, actualParams.size())) {
          actual = arg;
          mayActualBeNull = mayBeNullExpr(state, actual);
          if (mayActualBeNull) {
            break;
          }
        }
      } else {
        actual = actualParams.get(argPos);
        mayActualBeNull = mayBeNullExpr(state, actual);
      }
      // This statement should be unreachable without assigning actual beforehand:
      Preconditions.checkNotNull(actual);
      // make sure we are passing a non-null value
      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);
  }