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