in src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java [5542:5649]
private Map<GenericsTypeName, GenericsType> extractGenericsConnectionsFromArguments(final GenericsType[] methodGenericTypes, final Parameter[] parameters, final Expression arguments, final GenericsType[] explicitTypeHints) {
Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
if (asBoolean(explicitTypeHints)) { // resolve type parameters from type arguments
int n = methodGenericTypes.length;
if (n == explicitTypeHints.length) {
for (int i = 0; i < n; i += 1) {
resolvedPlaceholders.put(new GenericsTypeName(methodGenericTypes[i].getName()), explicitTypeHints[i]);
}
}
} else if (parameters.length > 0) { // resolve type parameters from call arguments
List<Expression> expressions = InvocationWriter.makeArgumentList(arguments).getExpressions();
boolean isVargs = isVargs(parameters);
int nArguments = expressions.size();
int nParams = parameters.length;
if (isVargs ? nArguments >= nParams - 1 : nArguments == nParams) {
for (int i = 0; i < nArguments; i += 1) {
Expression argument = expressions.get(i);
if (isNullConstant(argument)) continue; // GROOVY-9984: skip
ClassNode argumentType = getDeclaredOrInferredType(argument);
ClassNode paramType = parameters[Math.min(i, nParams - 1)].getType();
if (GenericsUtils.hasUnresolvedGenerics(paramType)) {
// if supplying array param with multiple arguments or single non-array argument, infer using element type
if (isVargs && (i >= nParams || (i == nParams - 1 && (nArguments > nParams || !argumentType.isArray())))) {
paramType = paramType.getComponentType();
}
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
if ((argument instanceof ClosureExpression || argument instanceof MethodPointerExpression) && isSAMType(paramType)) {
// target type information
Tuple2<ClassNode[], ClassNode> samParamsAndReturnType = GenericsUtils.parameterizeSAM(paramType);
ClassNode[] q = samParamsAndReturnType.getV1();
// source type information
ClassNode returnType = isClosureWithType(argumentType)
? getCombinedBoundType(argumentType.getGenericsTypes()[0])
: wrapTypeIfNecessary(getInferredReturnType(argument));
ClassNode[] p;
if (argument instanceof ClosureExpression) {
ClosureExpression closure = (ClosureExpression) argument;
p = extractTypesFromParameters(getParametersSafe(closure));
} else { // argument instanceof MethodPointerExpression
List<MethodNode> candidates = argument.getNodeMetaData(MethodNode.class);
if (candidates != null && !candidates.isEmpty()) {
MethodPointerExpression methodPointer = (MethodPointerExpression) argument;
p = collateMethodReferenceParameterTypes(methodPointer, candidates.get(0));
if (p.length > 0 && GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GROOVY-11241: implicit receiver for "Optional::get" is resolved
if (!candidates.get(0).isStatic() && isClassClassNodeWrappingConcreteType(getType(methodPointer.getExpression()))) {
extractGenericsConnections(connections, q[0], p[0].redirect());
}
for (int j = 0; j < q.length; j += 1) {
// SAM parameters are like arguments in this case
extractGenericsConnections(connections, q[j], p[j]);
}
// convert the method's generics into the SAM's generics
returnType = applyGenericsContext(connections, returnType);
p = applyGenericsContext(connections, p );
connections.clear();
}
} else {
p = ClassNode.EMPTY_ARRAY;
}
}
// parameters and return type correspond to the SAM's
for (int j = 0; j < p.length && j < q.length; j += 1) {
if (!isDynamicTyped(p[j]))
// GROOVY-10054, GROOVY-10699, GROOVY-10749, et al.
extractGenericsConnections(connections, wrapTypeIfNecessary(p[j]), q[j]);
}
extractGenericsConnections(connections, returnType, samParamsAndReturnType.getV2());
} else {
extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType);
}
connections.forEach((gtn, gt) -> resolvedPlaceholders.merge(gtn, gt, (gt1, gt2) -> {
// GROOVY-10339: incorporate another witness
return getCombinedGenericsType(gt1, gt2);
}));
}
}
}
// in case of "<T, U extends Type<T>>", we can learn about "T" from a resolved "U"
extractGenericsConnectionsForBoundTypes(methodGenericTypes, resolvedPlaceholders);
}
for (GenericsType gt : methodGenericTypes) {
// GROOVY-8409, GROOVY-10067, et al.: provide "no type witness" mapping for param
resolvedPlaceholders.computeIfAbsent(new GenericsTypeName(gt.getName()), gtn -> {
ClassNode hash = ClassHelper.makeWithoutCaching("#" + gt.getName());
hash.setRedirect(getCombinedBoundType(gt));
hash.setGenericsPlaceHolder(true);
GenericsType gtx = new GenericsType(hash, applyGenericsContext(resolvedPlaceholders, gt.getUpperBounds()), null);
gtx.putNodeMetaData(GenericsType.class, gt);
gtx.setResolved(true);
return gtx;
});
}
return resolvedPlaceholders;
}