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);
}