use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method inferReturnTypeGenerics.
/**
* If a method call returns a parameterized type, then perform additional
* inference on the return type, so that the type gets actual type arguments.
* For example, the method {@code Arrays.asList(T...)} is parameterized with
* {@code T}, which can be deduced type arguments or call arguments.
*
* @param method the method node
* @param arguments the method call arguments
* @param receiver the object expression type
* @param explicitTypeHints type arguments (optional), for example {@code Collections.<String>emptyList()}
*/
protected ClassNode inferReturnTypeGenerics(final ClassNode receiver, final MethodNode method, final Expression arguments, final GenericsType[] explicitTypeHints) {
ClassNode returnType = method instanceof ConstructorNode ? method.getDeclaringClass() : method.getReturnType();
if (!GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GROOVY-7538: replace "Type<?>" with "Type<? extends/super X>" for any "Type<T extends/super X>"
if (getGenericsWithoutArray(returnType) != null)
returnType = boundUnboundedWildcards(returnType);
return returnType;
}
if (method instanceof ExtensionMethodNode) {
ArgumentListExpression args = getExtensionArguments(receiver, method, arguments);
MethodNode extension = ((ExtensionMethodNode) method).getExtensionMethodNode();
return inferReturnTypeGenerics(receiver, extension, args, explicitTypeHints);
}
Map<GenericsTypeName, GenericsType> context = method.isStatic() || method instanceof ConstructorNode ? null : extractPlaceHoldersVisibleToDeclaration(receiver, method, arguments);
GenericsType[] methodGenericTypes = method instanceof ConstructorNode ? method.getDeclaringClass().getGenericsTypes() : applyGenericsContext(context, method.getGenericsTypes());
if (methodGenericTypes != null) {
Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
for (GenericsType gt : methodGenericTypes) resolvedPlaceholders.put(new GenericsTypeName(gt.getName()), gt);
applyGenericsConnections(extractGenericsConnectionsFromArguments(methodGenericTypes, Arrays.stream(method.getParameters()).map(param -> new Parameter(applyGenericsContext(context, param.getType()), param.getName())).toArray(Parameter[]::new), arguments, explicitTypeHints), resolvedPlaceholders);
returnType = applyGenericsContext(resolvedPlaceholders, returnType);
}
if (context != null) {
returnType = applyGenericsContext(context, returnType);
if (receiver.getGenericsTypes() == null && receiver.redirect().getGenericsTypes() != null && GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GROOVY-10049: do not return "Stream<E>" for raw type "List#stream()"
returnType = returnType.getPlainNodeReference();
}
}
// 3) resolve bounds of type parameters from calling context
returnType = applyGenericsContext(extractGenericsParameterMapOfThis(typeCheckingContext), returnType);
return returnType;
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method inferSAMTypeGenericsInAssignment.
private ClassNode inferSAMTypeGenericsInAssignment(final ClassNode samType, final MethodNode abstractMethod, final ClassNode closureType, final ClosureExpression closureExpression) {
// if the sam type or closure type do not provide generics information,
// we cannot infer anything, thus we simply return the provided samUsage
GenericsType[] samTypeGenerics = samType.getGenericsTypes();
GenericsType[] closureGenerics = closureType.getGenericsTypes();
if (samTypeGenerics == null || closureGenerics == null)
return samType;
// extract the generics from the return type
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
extractGenericsConnections(connections, wrapTypeIfNecessary(getInferredReturnType(closureExpression)), abstractMethod.getReturnType());
// TODO: add vargs handling
if (closureExpression.isParameterSpecified()) {
Parameter[] closureParams = closureExpression.getParameters();
Parameter[] methodParams = abstractMethod.getParameters();
for (int i = 0, n = Math.min(closureParams.length, methodParams.length); i < n; i += 1) {
ClassNode closureParamType = closureParams[i].getType();
ClassNode methodParamType = methodParams[i].getType();
extractGenericsConnections(connections, closureParamType, methodParamType);
}
}
return applyGenericsContext(connections, samType.redirect());
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class StaticTypeCheckingVisitor method extractGenericsConnectionsFromArguments.
/**
* Resolves type parameters declared by method from type or call arguments.
*/
private Map<GenericsTypeName, GenericsType> extractGenericsConnectionsFromArguments(final GenericsType[] methodGenericTypes, final Parameter[] parameters, final Expression arguments, final GenericsType[] explicitTypeHints) {
Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
if (explicitTypeHints != null) {
// 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) {
if (isNullConstant(expressions.get(i)))
// GROOVY-9984: skip null
continue;
ClassNode paramType = parameters[Math.min(i, nParams - 1)].getType();
ClassNode argumentType = getDeclaredOrInferredType(expressions.get(i));
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();
}
if (isClosureWithType(argumentType)) {
MethodNode sam = findSAM(paramType);
if (sam != null) {
// adapt closure to functional interface or other single-abstract-method class
argumentType = convertClosureTypeToSAMType(expressions.get(i), argumentType, sam, paramType);
}
}
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
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 resolved "U"
Map<GenericsTypeName, GenericsType> connections = Arrays.stream(methodGenericTypes).collect(toMap(gt -> new GenericsTypeName(gt.getName()), gt -> gt, (v, x) -> v));
extractGenericsConnectionsForSuperClassAndInterfaces(connections, 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 -> {
GenericsType xxx = new GenericsType(ClassHelper.makeWithoutCaching("#"), applyGenericsContext(resolvedPlaceholders, gt.getUpperBounds()), applyGenericsContext(resolvedPlaceholders, gt.getLowerBound()));
xxx.getType().setRedirect(gt.getType().redirect());
xxx.putNodeMetaData(GenericsType.class, gt);
xxx.setName("#" + gt.getName());
xxx.setPlaceholder(true);
return xxx;
});
}
return resolvedPlaceholders;
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class ResolveVisitor method resolveGenericsType.
private boolean resolveGenericsType(final GenericsType genericsType) {
if (genericsType.isResolved())
return true;
currentClass.setUsingGenerics(true);
ClassNode type = genericsType.getType();
// save name before redirect
GenericsTypeName name = new GenericsTypeName(type.getName());
visitTypeAnnotations(type);
ClassNode[] bounds = genericsType.getUpperBounds();
if (!genericParameterNames.containsKey(name)) {
if (bounds != null) {
for (ClassNode upperBound : bounds) {
resolveOrFail(upperBound, genericsType);
type.setRedirect(upperBound);
resolveGenericsTypes(upperBound.getGenericsTypes());
}
} else if (genericsType.isWildcard()) {
type.setRedirect(ClassHelper.OBJECT_TYPE);
} else {
resolveOrFail(type, genericsType);
}
} else {
GenericsType gt = genericParameterNames.get(name);
type.setRedirect(gt.getType());
genericsType.setPlaceholder(true);
}
if (genericsType.getLowerBound() != null) {
resolveOrFail(genericsType.getLowerBound(), genericsType);
}
if (resolveGenericsTypes(type.getGenericsTypes())) {
genericsType.setResolved(genericsType.getType().isResolved());
}
return genericsType.isResolved();
}
use of org.codehaus.groovy.ast.GenericsType.GenericsTypeName in project groovy by apache.
the class ResolveVisitor method visitProperty.
@Override
public void visitProperty(final PropertyNode node) {
Map<GenericsTypeName, GenericsType> oldPNames = genericParameterNames;
if (node.isStatic() && !Traits.isTrait(node.getDeclaringClass())) {
genericParameterNames = new HashMap<>();
}
ClassNode t = node.getType();
resolveOrFail(t, node);
super.visitProperty(node);
fieldTypesChecked.add(node.getField());
genericParameterNames = oldPNames;
}
Aggregations