public void compareGenericTypeParameterNullabilityForCall()

in nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java [675:747]


  public void compareGenericTypeParameterNullabilityForCall(
      Symbol.MethodSymbol methodSymbol,
      Tree tree,
      List<? extends ExpressionTree> actualParams,
      boolean isVarArgs,
      NullAway analysis,
      VisitorState state) {
    Config config = analysis.getConfig();
    if (!config.isJSpecifyMode()) {
      return;
    }
    Type invokedMethodType = methodSymbol.type;
    // substitute class-level type arguments for instance methods
    if (!methodSymbol.isStatic() && tree instanceof MethodInvocationTree) {
      ExpressionTree methodSelect = ((MethodInvocationTree) tree).getMethodSelect();
      Type enclosingType;
      if (methodSelect instanceof MemberSelectTree) {
        enclosingType = getTreeType(((MemberSelectTree) methodSelect).getExpression(), config);
      } else {
        // implicit this parameter
        enclosingType = methodSymbol.owner.type;
      }
      if (enclosingType != null) {
        invokedMethodType =
            TypeSubstitutionUtils.memberType(state.getTypes(), enclosingType, methodSymbol, config);
      }
    }
    // substitute type arguments for generic methods with explicit type arguments
    if (tree instanceof MethodInvocationTree && methodSymbol.type instanceof Type.ForAll) {
      invokedMethodType =
          substituteTypeArgsInGenericMethodType(
              (MethodInvocationTree) tree, methodSymbol, state, config);
    }
    List<Type> formalParamTypes = invokedMethodType.getParameterTypes();
    int n = formalParamTypes.size();
    if (isVarArgs) {
      // If the last argument is var args, don't check it now, it will be checked against
      // all remaining actual arguments in the next loop.
      n = n - 1;
    }
    for (int i = 0; i < n; i++) {
      Type formalParameter = formalParamTypes.get(i);
      if (formalParameter.isRaw()) {
        // bail out of any checking involving raw types for now
        return;
      }
      Type actualParameter = getTreeType(actualParams.get(i), config);
      if (actualParameter != null) {
        if (!subtypeParameterNullability(formalParameter, actualParameter, state, config)) {
          reportInvalidParametersNullabilityError(
              formalParameter, actualParameter, actualParams.get(i), state, analysis);
        }
      }
    }
    if (isVarArgs && !formalParamTypes.isEmpty()) {
      Type.ArrayType varargsArrayType =
          (Type.ArrayType) formalParamTypes.get(formalParamTypes.size() - 1);
      Type varargsElementType = varargsArrayType.elemtype;
      for (int i = formalParamTypes.size() - 1; i < actualParams.size(); i++) {
        Type actualParameterType = getTreeType(actualParams.get(i), config);
        // If the actual parameter type is assignable to the varargs array type, then the call site
        // is passing the varargs directly in an array, and we should skip our check.
        if (actualParameterType != null
            && !state.getTypes().isAssignable(actualParameterType, varargsArrayType)) {
          if (!subtypeParameterNullability(
              varargsElementType, actualParameterType, state, config)) {
            reportInvalidParametersNullabilityError(
                varargsElementType, actualParameterType, actualParams.get(i), state, analysis);
          }
        }
      }
    }
  }