private Description checkParamOverriding()

in nullaway/src/main/java/com/uber/nullaway/NullAway.java [795:964]


  private Description checkParamOverriding(
      List<VarSymbol> overridingParamSymbols,
      Symbol.MethodSymbol overriddenMethod,
      @Nullable LambdaExpressionTree lambdaExpressionTree,
      @Nullable MemberReferenceTree memberReferenceTree,
      VisitorState state,
      Symbol.@Nullable MethodSymbol overridingMethod) {
    com.sun.tools.javac.util.List<VarSymbol> superParamSymbols = overriddenMethod.getParameters();
    boolean unboundMemberRef =
        (memberReferenceTree != null)
            && ((JCTree.JCMemberReference) memberReferenceTree).kind.isUnbound();
    boolean isOverriddenMethodAnnotated =
        !codeAnnotationInfo.isSymbolUnannotated(overriddenMethod, config, handler);
    boolean isOverridingMethodAnnotated =
        (overridingMethod != null
                && !codeAnnotationInfo.isSymbolUnannotated(overridingMethod, config, handler))
            || lambdaExpressionTree != null;
    Type.MethodType jspecifyMemberReferenceMethodType = null;
    if (memberReferenceTree != null) {
      jspecifyMemberReferenceMethodType =
          genericsChecks.getMemberReferenceMethodType(
              memberReferenceTree, castToNonNull(overridingMethod), state);
    }

    MethodParameterNullness overriddenMethodArgumentNullness =
        MethodParameterNullness.create(overriddenMethod);

    // Collect @Nullable params of overridden method iff the overridden method is in annotated code
    // (otherwise, whether we acknowledge @Nullable in unannotated code or not depends on the
    // -XepOpt:NullAway:AcknowledgeRestrictiveAnnotations flag and its handler).
    if (isOverriddenMethodAnnotated) {
      boolean overriddenMethodIsVarArgs = overriddenMethod.isVarArgs();
      for (int i = 0; i < superParamSymbols.size(); i++) {
        Nullness paramNullness;
        if (overriddenMethodIsVarArgs && i == superParamSymbols.size() - 1) {
          // For a varargs position, we need to check if the array itself is @Nullable
          paramNullness =
              Nullness.varargsArrayIsNullable(superParamSymbols.get(i), config)
                  ? Nullness.NULLABLE
                  : Nullness.NONNULL;
        } else if (Nullness.paramHasNullableAnnotation(overriddenMethod, i, config)) {
          paramNullness = Nullness.NULLABLE;
        } else if (config.isJSpecifyMode()) {
          // Check if the parameter type is a type variable and the corresponding generic type
          // argument is @Nullable
          if (memberReferenceTree != null || lambdaExpressionTree != null) {
            // For a method reference or lambda, try to use a type inferred by GenericsChecks for
            // the tree.  Fall back on the type inferred by javac.
            Tree polyExprTree =
                castToNonNull(
                    memberReferenceTree != null ? memberReferenceTree : lambdaExpressionTree);
            Type functionalInterfaceType =
                genericsChecks.getInferredPolyExpressionType(polyExprTree);
            if (functionalInterfaceType == null) {
              functionalInterfaceType = ASTHelpers.getType(polyExprTree);
            }
            paramNullness =
                genericsChecks.getGenericMethodParameterNullness(
                    i, overriddenMethod, functionalInterfaceType, state);
          } else {
            // Use the enclosing class of the overriding method to find generic type arguments
            paramNullness =
                genericsChecks.getGenericMethodParameterNullness(
                    i, overriddenMethod, overridingParamSymbols.get(i).owner.owner, state);
          }
        } else {
          paramNullness = Nullness.NONNULL;
        }
        overriddenMethodArgumentNullness.setParameterNullness(i, paramNullness);
      }
    }

    // Check handlers for any further/overriding nullness information
    overriddenMethodArgumentNullness =
        handler.onOverrideMethodInvocationParametersNullability(
            state.context,
            overriddenMethod,
            isOverriddenMethodAnnotated,
            overriddenMethodArgumentNullness);

    // If we have an unbound method reference, the first parameter of the overridden method must be
    // @NonNull, as this parameter will be used as a method receiver inside the generated lambda.
    // e.g. String::length is implemented as (@NonNull s -> s.length()) when used as a
    // SomeFunc<String> and thus incompatible with, for example, SomeFunc.apply(@Nullable T).
    if (unboundMemberRef
        && Objects.equals(
            overriddenMethodArgumentNullness.getParameterNullness(0), Nullness.NULLABLE)) {
      String message =
          "unbound instance method reference cannot be used, as first parameter of "
              + "functional interface method "
              + ASTHelpers.enclosingClass(overriddenMethod)
              + "."
              + overriddenMethod
              + " is @Nullable";
      return errorBuilder.createErrorDescription(
          new ErrorMessage(MessageTypes.WRONG_OVERRIDE_PARAM, message),
          buildDescription(memberReferenceTree),
          state,
          null);
    }

    // for unbound member references, we need to adjust parameter indices by 1 when matching with
    // overridden method
    int startParam = unboundMemberRef ? 1 : 0;

    for (int i = 0; i < superParamSymbols.size(); i++) {
      Nullness overriddenParameterNullness =
          overriddenMethodArgumentNullness.getParameterNullness(i);
      if (!Objects.equals(overriddenParameterNullness, Nullness.NULLABLE)) {
        // No need to check, unless the argument of the overridden method is effectively @Nullable,
        // in which case it can't be overridden by a @NonNull arg.
        continue;
      }
      int methodParamInd = i - startParam;
      VarSymbol paramSymbol = overridingParamSymbols.get(methodParamInd);
      boolean paramIsNonNull =
          paramOfOverridingMethodIsNonNull(
              paramSymbol,
              methodParamInd,
              overridingMethod,
              isOverridingMethodAnnotated,
              memberReferenceTree,
              jspecifyMemberReferenceMethodType);
      // in the case where we have a parameter of a lambda expression, we do
      // *not* force the parameter to be annotated with @Nullable; instead we "inherit"
      // nullability from the corresponding functional interface method.
      // So, we report an error if the @Nullable annotation is missing *and*
      // we don't have a lambda with implicitly typed parameters
      boolean implicitlyTypedLambdaParam =
          lambdaExpressionTree != null
              && NullabilityUtil.lambdaParamIsImplicitlyTyped(
                  lambdaExpressionTree.getParameters().get(methodParamInd));
      if (!implicitlyTypedLambdaParam && paramIsNonNull) {
        String message =
            "parameter "
                + paramSymbol.name.toString()
                + (memberReferenceTree != null ? " of referenced method" : "")
                + " is @NonNull, but parameter in "
                + ((lambdaExpressionTree != null || memberReferenceTree != null)
                    ? "functional interface "
                    : "superclass ")
                + "method "
                + ASTHelpers.enclosingClass(overriddenMethod)
                + "."
                + overriddenMethod
                + " is @Nullable";
        Tree errorTree;
        if (memberReferenceTree != null) {
          errorTree = memberReferenceTree;
        } else {
          errorTree = getTreesInstance(state).getTree(paramSymbol);
        }
        VisitorState paramState;
        if (memberReferenceTree != null) {
          // For nullability issues related to functional interfaces, the suppression should be on
          // the use site
          paramState = state;
        } else {
          TreePath path = getTreesInstance(state).getPath(paramSymbol);
          paramState = path != null ? state.withPath(path) : state;
        }
        return errorBuilder.createErrorDescription(
            new ErrorMessage(MessageTypes.WRONG_OVERRIDE_PARAM, message),
            buildDescription(errorTree),
            paramState,
            paramSymbol);
      }
    }
    return Description.NO_MATCH;
  }