private Map extractGenericsConnectionsFromArguments()

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