private InvocationAndContext getInvocationAndContextForInference()

in nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java [2070:2141]


  private InvocationAndContext getInvocationAndContextForInference(
      TreePath path, VisitorState state, boolean calledFromDataflow) {
    MethodInvocationTree invocation = (MethodInvocationTree) path.getLeaf();
    TreePath parentPath = path.getParentPath();
    Tree parent = parentPath.getLeaf();
    while (parent instanceof ParenthesizedTree) {
      parentPath = parentPath.getParentPath();
      parent = parentPath.getLeaf();
    }
    if (parent instanceof AssignmentTree || parent instanceof VariableTree) {
      return getInvocationInferenceInfoForAssignment(parent, invocation, state);
    } else if (parent instanceof ReturnTree) {
      // find the enclosing method and return its return type
      TreePath enclosingMethodOrLambda =
          NullabilityUtil.findEnclosingMethodOrLambdaOrInitializer(parentPath);
      // TODO handle lambdas; https://github.com/uber/NullAway/issues/1288
      if (enclosingMethodOrLambda != null
          && enclosingMethodOrLambda.getLeaf() instanceof MethodTree enclosingMethod) {
        Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(enclosingMethod);
        if (methodSymbol != null) {
          return new InvocationAndContext(invocation, methodSymbol.getReturnType(), false);
        }
      }
    } else if (parent instanceof ExpressionTree exprParent) {
      // could be a parameter to another method call, or part of a conditional expression, etc.
      // in any case, just return the type of the parent expression
      if (exprParent instanceof MethodInvocationTree parentInvocation) {
        if (isGenericCallNeedingInference(parentInvocation)) {
          // this is the case of a nested generic call, e.g., id(id(x)) where id is generic
          // we want to find the outermost invocation that requires inference, since that is
          // the one whose assignment context is relevant
          return getInvocationAndContextForInference(parentPath, state, calledFromDataflow);
        }
        // the generic invocation is either a regular parameter to the parent call, or the
        // receiver expression
        Type formalParamType =
            getFormalParameterTypeForArgument(
                parentInvocation,
                castToNonNull(ASTHelpers.getType(parentInvocation.getMethodSelect()))
                    .asMethodType(),
                invocation);
        if (formalParamType == null) {
          // this can happen if the invocation is the receiver expression of the call, e.g.,
          // id(x).foo() (note that foo() need not be generic)
          ExpressionTree methodSelect =
              ASTHelpers.stripParentheses(parentInvocation.getMethodSelect());
          if (methodSelect instanceof MemberSelectTree mst) {
            if (ASTHelpers.stripParentheses(mst.getExpression()) == invocation) {
              // the invocation is the receiver expression, so we want the enclosing type of the
              // parent invocation
              formalParamType =
                  getEnclosingTypeForCallExpression(
                      ASTHelpers.getSymbol(parentInvocation),
                      parentInvocation,
                      parentPath,
                      state,
                      calledFromDataflow);
            } else {
              throw new RuntimeException(
                  "did not find invocation "
                      + state.getSourceForNode(invocation)
                      + " as receiver expression of "
                      + state.getSourceForNode(parentInvocation));
            }
          }
        }
        return new InvocationAndContext(invocation, formalParamType, false);
      }
    }
    // an unhandled case; for now, give up and return no assignment context
    return new InvocationAndContext(invocation, null, false);
  }