in nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java [882:963]
private MethodInferenceResult runInferenceForCall(
VisitorState state,
@Nullable TreePath path,
MethodInvocationTree invocationTree,
@Nullable Type typeFromAssignmentContext,
boolean assignedToLocal,
boolean calledFromDataflow) {
Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(invocationTree);
ConstraintSolver solver = makeSolver(state, analysis);
// allInvocations tracks the top-level invocations and any nested invocations that also
// require inference
Set<MethodInvocationTree> allInvocations = new LinkedHashSet<>();
allInvocations.add(invocationTree);
Map<Element, ConstraintSolver.InferredNullability> typeVarNullability;
try {
generateConstraintsForCall(
state,
path,
typeFromAssignmentContext,
assignedToLocal,
solver,
methodSymbol,
invocationTree,
allInvocations);
typeVarNullability = new HashMap<>(solver.solve());
// The solver only computes a solution for variables that appear in constraints. For
// unconstrained variables, treat them as NONNULL, consistent with solver behavior for
// unconstrained variables that do appear in the constraint graph.
for (int i = 0; i < methodSymbol.getTypeParameters().size(); i++) {
Symbol.TypeVariableSymbol typeVar = methodSymbol.getTypeParameters().get(i);
typeVarNullability.putIfAbsent(typeVar, ConstraintSolver.InferredNullability.NONNULL);
}
// Store inferred types for lambda arguments
new InvocationArguments(invocationTree, methodSymbol.type.asMethodType())
.forEach(
(argument, argPos, formalParamType, unused) -> {
if (argument instanceof LambdaExpressionTree
|| argument instanceof MemberReferenceTree) {
Type polyExprTreeType = ASTHelpers.getType(argument);
if (polyExprTreeType != null) {
Type typeWithInferredNullability =
TypeSubstitutionUtils.updateTypeWithInferredNullability(
polyExprTreeType, formalParamType, typeVarNullability, state, config);
inferredPolyExpressionTypes.put(argument, typeWithInferredNullability);
}
}
});
InferenceSuccess successResult = new InferenceSuccess(typeVarNullability);
// don't cache result if we were called from dataflow, since the result may rely on dataflow
// facts that do not reflect the fixed point
if (!calledFromDataflow) {
for (MethodInvocationTree invTree : allInvocations) {
inferredTypeVarNullabilityForGenericCalls.put(invTree, successResult);
}
}
return successResult;
} catch (UnsatisfiableConstraintsException e) {
if (config.warnOnGenericInferenceFailure()) {
ErrorBuilder errorBuilder = analysis.getErrorBuilder();
ErrorMessage errorMessage =
new ErrorMessage(
ErrorMessage.MessageTypes.GENERIC_INFERENCE_FAILURE,
String.format(
"Failed to infer type argument nullability for call %s: %s",
state.getSourceForNode(invocationTree), e.getMessage()));
state.reportMatch(
errorBuilder.createErrorDescription(
errorMessage, analysis.buildDescription(invocationTree), state, null));
}
InferenceFailure failureResult = new InferenceFailure(e.getMessage());
// don't cache result if we were called from dataflow, since the result may rely on dataflow
// facts that do not reflect the fixed point
if (!calledFromDataflow) {
for (MethodInvocationTree invTree : allInvocations) {
inferredTypeVarNullabilityForGenericCalls.put(invTree, failureResult);
}
}
return failureResult;
}
}