use of groovy.transform.stc.ClosureParams in project groovy by apache.
the class StaticTypeCheckingVisitor method inferClosureParameterTypes.
/**
* Performs type inference on closure argument types whenever code like this
* is found: <code>foo.collect { it.toUpperCase() }</code>.
* <p>
* In this case the type checker tries to find if the {@code collect} method
* has its {@link Closure} argument annotated with {@link ClosureParams}. If
* so, then additional type inference can be performed and the type of
* {@code it} may be inferred.
*
* @param receiver
* @param arguments
* @param expression closure or lambda expression for which the argument types should be inferred
* @param target parameter which may provide {@link ClosureParams} annotation or SAM type
* @param method method that declares {@code target}
*/
protected void inferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final Parameter target, final MethodNode method) {
List<AnnotationNode> annotations = target.getAnnotations(CLOSUREPARAMS_CLASSNODE);
if (annotations != null && !annotations.isEmpty()) {
for (AnnotationNode annotation : annotations) {
Expression value = annotation.getMember("value");
Expression options = annotation.getMember("options");
Expression conflictResolver = annotation.getMember("conflictResolutionStrategy");
doInferClosureParameterTypes(receiver, arguments, expression, method, value, conflictResolver, options);
}
} else if (isSAMType(target.getOriginType())) {
// SAM-type coercion
Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
GenericsType[] typeParameters = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
if (typeParameters != null) {
boolean typeParametersResolved = false;
// first check for explicit type arguments
Expression emc = typeCheckingContext.getEnclosingMethodCall();
if (emc instanceof MethodCallExpression) {
MethodCallExpression mce = (MethodCallExpression) emc;
if (mce.getArguments() == arguments) {
GenericsType[] typeArguments = mce.getGenericsTypes();
if (typeArguments != null) {
int n = typeParameters.length;
if (n == typeArguments.length) {
typeParametersResolved = true;
for (int i = 0; i < n; i += 1) {
context.put(new GenericsTypeName(typeParameters[i].getName()), typeArguments[i]);
}
}
}
}
}
if (!typeParametersResolved) {
// check for implicit type arguments
int i = -1;
Parameter[] p = method.getParameters();
for (Expression argument : (ArgumentListExpression) arguments) {
i += 1;
if (argument instanceof ClosureExpression || isNullConstant(argument))
continue;
ClassNode pType = p[Math.min(i, p.length - 1)].getType();
Map<GenericsTypeName, GenericsType> gc = new HashMap<>();
extractGenericsConnections(gc, wrapTypeIfNecessary(getType(argument)), pType);
gc.forEach((key, gt) -> {
for (GenericsType tp : typeParameters) {
if (tp.getName().equals(key.getName())) {
// TODO: merge
context.putIfAbsent(key, gt);
break;
}
}
});
}
for (GenericsType tp : typeParameters) {
context.computeIfAbsent(new GenericsTypeName(tp.getName()), x -> fullyResolve(tp, context));
}
}
}
ClassNode[] samParamTypes = GenericsUtils.parameterizeSAM(applyGenericsContext(context, target.getType())).getV1();
ClassNode[] paramTypes = expression.getNodeMetaData(CLOSURE_ARGUMENTS);
if (paramTypes == null) {
int n;
Parameter[] p = expression.getParameters();
if (p == null) {
// zero parameters
paramTypes = ClassNode.EMPTY_ARRAY;
} else if ((n = p.length) == 0) {
// implicit parameter(s)
paramTypes = samParamTypes;
} else {
// TODO: error for length mismatch
paramTypes = Arrays.copyOf(samParamTypes, n);
for (int i = 0; i < Math.min(n, samParamTypes.length); i += 1) {
checkParamType(p[i], paramTypes[i], i == n - 1, expression instanceof LambdaExpression);
}
}
expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
}
}
}
Aggregations