in nullaway/src/main/java/com/uber/nullaway/NullAway.java [1721:1824]
private Description handleInvocation(
Tree tree,
VisitorState state,
Symbol.MethodSymbol methodSymbol,
List<? extends ExpressionTree> actualParams) {
List<VarSymbol> formalParams = methodSymbol.getParameters();
if (formalParams.size() != actualParams.size()
&& !methodSymbol.isVarArgs()
&& !methodSymbol.isStatic()
&& methodSymbol.isConstructor()
&& methodSymbol.enclClass().isInner()) {
// In special cases like one in issue #366
// formal params and actual params do not match while using JDK11+
// we bail out in this particular case
return Description.NO_MATCH;
}
final boolean isMethodAnnotated =
!codeAnnotationInfo.isSymbolUnannotated(methodSymbol, config, handler);
// If argumentPositionNullness[i] == null, parameter i is unannotated
Nullness[] argumentPositionNullness = new Nullness[formalParams.size()];
if (isMethodAnnotated) {
// compute which arguments are @NonNull
for (int i = 0; i < formalParams.size(); i++) {
VarSymbol param = formalParams.get(i);
if (param.type.isPrimitive()) {
doUnboxingCheck(state, actualParams.get(i));
argumentPositionNullness[i] = Nullness.NONNULL;
} else if (ASTHelpers.isSameType(
param.type, Suppliers.JAVA_LANG_VOID_TYPE.get(state), state)) {
// Temporarily treat a Void argument type as if it were @Nullable Void. Handling of Void
// without special-casing, as recommended by JSpecify might: a) require generics support
// and, b) require checking that third-party libraries considered annotated adopt
// JSpecify semantics.
// See the suppression in https://github.com/uber/NullAway/pull/608 for an example of why
// this is needed.
argumentPositionNullness[i] = Nullness.NULLABLE;
} else {
// we need to call paramHasNullableAnnotation here since the invoked method may be defined
// in a class file
argumentPositionNullness[i] =
Nullness.paramHasNullableAnnotation(methodSymbol, i, config)
? Nullness.NULLABLE
: ((config.isJSpecifyMode() && tree instanceof MethodInvocationTree)
? GenericsChecks.getGenericParameterNullnessAtInvocation(
i, methodSymbol, (MethodInvocationTree) tree, state, config)
: Nullness.NONNULL);
}
}
if (config.isJSpecifyMode()) {
GenericsChecks.compareGenericTypeParameterNullabilityForCall(
formalParams, actualParams, methodSymbol.isVarArgs(), this, state);
}
}
// Allow handlers to override the list of non-null argument positions
argumentPositionNullness =
handler.onOverrideMethodInvocationParametersNullability(
state.context, methodSymbol, isMethodAnnotated, argumentPositionNullness);
// now actually check the arguments
// NOTE: the case of an invocation on a possibly-null reference
// is handled by matchMemberSelect()
for (int argPos = 0; argPos < argumentPositionNullness.length; argPos++) {
if (!Objects.equals(Nullness.NONNULL, argumentPositionNullness[argPos])) {
continue;
}
ExpressionTree actual = null;
boolean mayActualBeNull = false;
if (argPos == formalParams.size() - 1 && methodSymbol.isVarArgs()) {
// Check all vararg actual arguments for nullability
if (actualParams.size() <= argPos) {
continue;
}
for (ExpressionTree arg : actualParams.subList(argPos, actualParams.size())) {
actual = arg;
mayActualBeNull = mayBeNullExpr(state, actual);
if (mayActualBeNull) {
break;
}
}
} else {
actual = actualParams.get(argPos);
mayActualBeNull = mayBeNullExpr(state, actual);
}
// This statement should be unreachable without assigning actual beforehand:
Preconditions.checkNotNull(actual);
// make sure we are passing a non-null value
if (mayActualBeNull) {
String message =
"passing @Nullable parameter '"
+ state.getSourceForNode(actual)
+ "' where @NonNull is required";
ErrorMessage errorMessage = new ErrorMessage(MessageTypes.PASS_NULLABLE, message);
state.reportMatch(
errorBuilder.createErrorDescriptionForNullAssignment(
errorMessage, actual, buildDescription(actual), state, formalParams.get(argPos)));
}
}
// Check for @NonNull being passed to castToNonNull (if configured)
return checkCastToNonNullTakesNullable(tree, state, methodSymbol, actualParams);
}