in nullaway/src/main/java/com/uber/nullaway/generics/GenericsUtils.java [46:156]
static void processMethodRefTypeRelations(
GenericsChecks genericsChecks,
Type targetType,
MemberReferenceTree memberReferenceTree,
VisitorState state,
MethodRefTypeRelationHandler relationHandler) {
if (targetType.isRaw()) {
return;
}
Types types = state.getTypes();
// first, figure out the proper method type to use for the member reference
Symbol.MethodSymbol referencedMethod = ASTHelpers.getSymbol(memberReferenceTree);
if (referencedMethod == null || referencedMethod.isConstructor()) {
// TODO handle constructor references like Foo::new;
// https://github.com/uber/NullAway/issues/1468
return;
}
Type.MethodType referencedMethodType =
genericsChecks.getMemberReferenceMethodType(memberReferenceTree, referencedMethod, state);
if (referencedMethodType == null) {
return;
}
Type qualifierType = null;
if (!referencedMethod.isStatic()) {
qualifierType =
genericsChecks.getTreeType(memberReferenceTree.getQualifierExpression(), state);
}
// now, get the type of the corresponding functional interface method, as a member of targetType
Symbol.MethodSymbol fiMethod =
NullabilityUtil.getFunctionalInterfaceMethod(memberReferenceTree, types);
Type.MethodType fiMethodTypeAsMember =
TypeSubstitutionUtils.memberType(types, targetType, fiMethod, genericsChecks.getConfig())
.asMethodType();
// method reference return type <: functional interface return type
Type fiReturnType = fiMethodTypeAsMember.getReturnType();
Type referencedReturnType = referencedMethodType.getReturnType();
if (fiReturnType.getKind() != TypeKind.VOID
&& referencedReturnType.getKind() != TypeKind.VOID) {
relationHandler.handle(referencedReturnType, fiReturnType, MethodRefTypeRelationKind.RETURN);
}
// i^{th} functional interface parameter type <: i^{th} method reference parameter type,
// aligned appropriately in the case of unbound method references
com.sun.tools.javac.util.List<Type> fiParamTypes = fiMethodTypeAsMember.getParameterTypes();
com.sun.tools.javac.util.List<Type> referencedParamTypes =
referencedMethodType.getParameterTypes();
int fiStartIndex = 0;
if (((JCTree.JCMemberReference) memberReferenceTree).kind.isUnbound()) {
Verify.verify(
!fiParamTypes.isEmpty(),
"Expected receiver parameter for unbound method ref %s",
memberReferenceTree);
if (qualifierType != null) {
relationHandler.handle(
fiParamTypes.get(0), qualifierType, MethodRefTypeRelationKind.PARAMETER);
}
fiStartIndex = 1;
}
// first, handle the non-varargs case
int fiParamCount = fiParamTypes.size() - fiStartIndex;
int nonVarargsParamCount =
referencedMethod.isVarArgs()
? Math.min(fiParamCount, referencedParamTypes.size() - 1)
: referencedParamTypes.size();
for (int i = 0; i < nonVarargsParamCount; i++) {
relationHandler.handle(
fiParamTypes.get(fiStartIndex + i),
referencedParamTypes.get(i),
MethodRefTypeRelationKind.PARAMETER);
}
if (!referencedMethod.isVarArgs()) {
return;
}
// For varargs references, the functional interface can map to fixed-arity form (single array
// argument at the varargs position) or variable-arity form (zero or more element arguments).
int varargsParamPosition = referencedParamTypes.size() - 1;
if (fiParamCount == varargsParamPosition) {
// No varargs arguments; this is the variable-arity case, passing zero arguments
return;
}
Type varargsArrayType = referencedParamTypes.get(varargsParamPosition);
Verify.verify(
varargsArrayType.getKind() == TypeKind.ARRAY,
"Expected array type for varargs parameter in %s, got %s",
memberReferenceTree,
varargsArrayType);
JCTree.JCMemberReference javacMemberRef = (JCTree.JCMemberReference) memberReferenceTree;
int firstVarargsFiParamIndex = fiStartIndex + varargsParamPosition;
if (javacMemberRef.varargsElement == null) {
// javac resolved this member reference using non-varargs (fixed-arity) adaptation.
relationHandler.handle(
fiParamTypes.get(firstVarargsFiParamIndex),
varargsArrayType,
MethodRefTypeRelationKind.PARAMETER);
} else {
// javac resolved this member reference using varargs (variable-arity) adaptation.
// Use the element type from the referenced varargs array type
Type varargsElementType = types.elemtype(varargsArrayType);
for (int i = varargsParamPosition; i < fiParamCount; i++) {
relationHandler.handle(
fiParamTypes.get(fiStartIndex + i),
varargsElementType,
MethodRefTypeRelationKind.PARAMETER);
}
}
}