use of org.codehaus.groovy.ast.GenericsType in project groovy by apache.
the class StaticTypeCheckingVisitor method visitMethodCallExpression.
@Override
public void visitMethodCallExpression(MethodCallExpression call) {
final String name = call.getMethodAsString();
if (name == null) {
addStaticTypeError("cannot resolve dynamic method name at compile time.", call.getMethod());
return;
}
if (extension.beforeMethodCall(call)) {
extension.afterMethodCall(call);
return;
}
typeCheckingContext.pushEnclosingMethodCall(call);
final Expression objectExpression = call.getObjectExpression();
objectExpression.visit(this);
call.getMethod().visit(this);
// the call is made on a collection type
if (call.isSpreadSafe()) {
//TODO check if this should not be change to iterator based call logic
ClassNode expressionType = getType(objectExpression);
if (!implementsInterfaceOrIsSubclassOf(expressionType, Collection_TYPE) && !expressionType.isArray()) {
addStaticTypeError("Spread operator can only be used on collection types", objectExpression);
return;
} else {
// type check call as if it was made on component type
ClassNode componentType = inferComponentType(expressionType, int_TYPE);
MethodCallExpression subcall = callX(castX(componentType, EmptyExpression.INSTANCE), name, call.getArguments());
subcall.setLineNumber(call.getLineNumber());
subcall.setColumnNumber(call.getColumnNumber());
subcall.setImplicitThis(call.isImplicitThis());
visitMethodCallExpression(subcall);
// the inferred type here should be a list of what the subcall returns
ClassNode subcallReturnType = getType(subcall);
ClassNode listNode = LIST_TYPE.getPlainNodeReference();
listNode.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(subcallReturnType)) });
storeType(call, listNode);
// store target method
storeTargetMethod(call, (MethodNode) subcall.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
typeCheckingContext.popEnclosingMethodCall();
return;
}
}
Expression callArguments = call.getArguments();
ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(callArguments);
checkForbiddenSpreadArgument(argumentList);
// for arguments, we need to visit closures *after* the method has been chosen
final ClassNode receiver = getType(objectExpression);
visitMethodCallArguments(receiver, argumentList, false, null);
ClassNode[] args = getArgumentTypes(argumentList);
final boolean isCallOnClosure = isClosureCall(name, objectExpression, callArguments);
try {
boolean callArgsVisited = false;
if (isCallOnClosure) {
// this is a closure.call() call
if (objectExpression == VariableExpression.THIS_EXPRESSION) {
// isClosureCall() check verified earlier that a field exists
FieldNode field = typeCheckingContext.getEnclosingClassNode().getDeclaredField(name);
GenericsType[] genericsTypes = field.getType().getGenericsTypes();
if (genericsTypes != null) {
ClassNode closureReturnType = genericsTypes[0].getType();
Object data = field.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
if (data != null) {
Parameter[] parameters = (Parameter[]) data;
typeCheckClosureCall(callArguments, args, parameters);
}
storeType(call, closureReturnType);
}
} else if (objectExpression instanceof VariableExpression) {
Variable variable = findTargetVariable((VariableExpression) objectExpression);
if (variable instanceof ASTNode) {
Object data = ((ASTNode) variable).getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
if (data != null) {
Parameter[] parameters = (Parameter[]) data;
typeCheckClosureCall(callArguments, args, parameters);
}
ClassNode type = getType(((ASTNode) variable));
if (type != null && type.equals(CLOSURE_TYPE)) {
GenericsType[] genericsTypes = type.getGenericsTypes();
type = OBJECT_TYPE;
if (genericsTypes != null) {
if (!genericsTypes[0].isPlaceholder()) {
type = genericsTypes[0].getType();
}
}
}
if (type != null) {
storeType(call, type);
}
}
} else if (objectExpression instanceof ClosureExpression) {
// we can get actual parameters directly
Parameter[] parameters = ((ClosureExpression) objectExpression).getParameters();
typeCheckClosureCall(callArguments, args, parameters);
ClassNode data = getInferredReturnType(objectExpression);
if (data != null) {
storeType(call, data);
}
}
int nbOfArgs;
if (callArguments instanceof ArgumentListExpression) {
ArgumentListExpression list = (ArgumentListExpression) callArguments;
nbOfArgs = list.getExpressions().size();
} else {
// todo : other cases
nbOfArgs = 0;
}
storeTargetMethod(call, nbOfArgs == 0 ? CLOSURE_CALL_NO_ARG : nbOfArgs == 1 ? CLOSURE_CALL_ONE_ARG : CLOSURE_CALL_VARGS);
} else {
// method call receivers are :
// - possible "with" receivers
// - the actual receiver as found in the method call expression
// - any of the potential receivers found in the instanceof temporary table
// in that order
List<Receiver<String>> receivers = new LinkedList<Receiver<String>>();
List<Receiver<String>> owners = makeOwnerList(objectExpression);
addReceivers(receivers, owners, call.isImplicitThis());
List<MethodNode> mn = null;
Receiver<String> chosenReceiver = null;
for (Receiver<String> currentReceiver : receivers) {
ClassNode receiverType = currentReceiver.getType();
mn = findMethod(receiverType, name, args);
// ensure that all methods are either static or declared by the current receiver or a superclass
if (!mn.isEmpty() && (typeCheckingContext.isInStaticContext || (receiverType.getModifiers() & Opcodes.ACC_STATIC) != 0) && (call.isImplicitThis() || (objectExpression instanceof VariableExpression && ((VariableExpression) objectExpression).isThisExpression()))) {
// we create separate method lists just to be able to print out
// a nice error message to the user
// a method is accessible if it is static, or if we are not in a static context and it is
// declared by the current receiver or a superclass
List<MethodNode> accessibleMethods = new LinkedList<MethodNode>();
List<MethodNode> inaccessibleMethods = new LinkedList<MethodNode>();
for (final MethodNode node : mn) {
if (node.isStatic() || (!typeCheckingContext.isInStaticContext && implementsInterfaceOrIsSubclassOf(receiverType, node.getDeclaringClass()))) {
accessibleMethods.add(node);
} else {
inaccessibleMethods.add(node);
}
}
mn = accessibleMethods;
if (accessibleMethods.isEmpty()) {
// choose an arbitrary method to display an error message
MethodNode node = inaccessibleMethods.get(0);
ClassNode owner = node.getDeclaringClass();
addStaticTypeError("Non static method " + owner.getName() + "#" + node.getName() + " cannot be called from static context", call);
}
}
if (!mn.isEmpty()) {
chosenReceiver = currentReceiver;
break;
}
}
if (mn.isEmpty() && typeCheckingContext.getEnclosingClosure() != null && args.length == 0) {
// add special handling of getDelegate() and getOwner()
if ("getDelegate".equals(name)) {
mn = Collections.singletonList(GET_DELEGATE);
} else if ("getOwner".equals(name)) {
mn = Collections.singletonList(GET_OWNER);
} else if ("getThisObject".equals(name)) {
mn = Collections.singletonList(GET_THISOBJECT);
}
}
if (mn.isEmpty()) {
mn = extension.handleMissingMethod(receiver, name, argumentList, args, call);
}
if (mn.isEmpty()) {
addNoMatchingMethodError(receiver, name, args, call);
} else {
if (areCategoryMethodCalls(mn, name, args)) {
addCategoryMethodCallError(call);
}
mn = disambiguateMethods(mn, chosenReceiver != null ? chosenReceiver.getType() : null, args, call);
if (mn.size() == 1) {
MethodNode directMethodCallCandidate = mn.get(0);
if (call.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION) == null && !directMethodCallCandidate.isStatic() && objectExpression instanceof ClassExpression && !"java.lang.Class".equals(directMethodCallCandidate.getDeclaringClass().getName())) {
ClassNode owner = directMethodCallCandidate.getDeclaringClass();
addStaticTypeError("Non static method " + owner.getName() + "#" + directMethodCallCandidate.getName() + " cannot be called from static context", call);
}
if (chosenReceiver == null) {
chosenReceiver = Receiver.make(directMethodCallCandidate.getDeclaringClass());
}
ClassNode returnType = getType(directMethodCallCandidate);
if (isUsingGenericsOrIsArrayUsingGenerics(returnType)) {
visitMethodCallArguments(chosenReceiver.getType(), argumentList, true, directMethodCallCandidate);
ClassNode irtg = inferReturnTypeGenerics(chosenReceiver.getType(), directMethodCallCandidate, callArguments, call.getGenericsTypes());
returnType = irtg != null && implementsInterfaceOrIsSubclassOf(irtg, returnType) ? irtg : returnType;
callArgsVisited = true;
}
if (directMethodCallCandidate == GET_DELEGATE && typeCheckingContext.getEnclosingClosure() != null) {
DelegationMetadata md = getDelegationMetadata(typeCheckingContext.getEnclosingClosure().getClosureExpression());
returnType = typeCheckingContext.getEnclosingClassNode();
if (md != null) {
returnType = md.getType();
}
}
if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, mn.get(0), call)) {
returnType = adjustWithTraits(directMethodCallCandidate, chosenReceiver.getType(), args, returnType);
storeType(call, returnType);
storeTargetMethod(call, directMethodCallCandidate);
String data = chosenReceiver.getData();
if (data != null) {
// the method which has been chosen is supposed to be a call on delegate or owner
// so we store the information so that the static compiler may reuse it
call.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, data);
}
// if the object expression is a closure shared variable, we will have to perform a second pass
if (objectExpression instanceof VariableExpression) {
VariableExpression var = (VariableExpression) objectExpression;
if (var.isClosureSharedVariable()) {
SecondPassExpression<ClassNode[]> wrapper = new SecondPassExpression<ClassNode[]>(call, args);
typeCheckingContext.secondPassExpressions.add(wrapper);
}
}
}
} else {
addAmbiguousErrorMessage(mn, name, args, call);
}
}
}
// now that a method has been chosen, we are allowed to visit the closures
if (!callArgsVisited) {
MethodNode mn = (MethodNode) call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
visitMethodCallArguments(receiver, argumentList, true, mn);
// GROOVY-6219
if (mn != null) {
List<Expression> argExpressions = argumentList.getExpressions();
Parameter[] parameters = mn.getParameters();
for (int i = 0; i < argExpressions.size() && i < parameters.length; i++) {
Expression arg = argExpressions.get(i);
ClassNode pType = parameters[i].getType();
ClassNode aType = getType(arg);
if (CLOSURE_TYPE.equals(pType) && CLOSURE_TYPE.equals(aType)) {
if (!isAssignableTo(aType, pType)) {
addNoMatchingMethodError(receiver, name, getArgumentTypes(argumentList), call);
call.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
}
}
}
}
}
} finally {
typeCheckingContext.popEnclosingMethodCall();
extension.afterMethodCall(call);
}
}
use of org.codehaus.groovy.ast.GenericsType in project groovy by apache.
the class StaticTypeCheckingVisitor method extractPlaceHolders.
private static Map<String, GenericsType> extractPlaceHolders(MethodNode method, ClassNode receiver, ClassNode declaringClass) {
if (declaringClass.equals(OBJECT_TYPE)) {
Map<String, GenericsType> resolvedPlaceholders = new HashMap<String, GenericsType>();
if (method != null)
addMethodLevelDeclaredGenerics(method, resolvedPlaceholders);
return resolvedPlaceholders;
}
Map<String, GenericsType> resolvedPlaceholders = null;
if (isPrimitiveType(receiver) && !isPrimitiveType(declaringClass)) {
receiver = getWrapper(receiver);
}
final List<ClassNode> queue;
if (receiver instanceof UnionTypeClassNode) {
queue = Arrays.asList(((UnionTypeClassNode) receiver).getDelegates());
} else {
queue = Collections.singletonList(receiver);
}
for (ClassNode item : queue) {
ClassNode current = item;
while (current != null) {
boolean continueLoop = true;
//extract the place holders
Map<String, GenericsType> currentPlaceHolders = new HashMap<String, GenericsType>();
if (isGenericsPlaceHolderOrArrayOf(declaringClass) || declaringClass.equals(current)) {
extractGenericsConnections(currentPlaceHolders, current, declaringClass);
if (method != null)
addMethodLevelDeclaredGenerics(method, currentPlaceHolders);
continueLoop = false;
} else {
GenericsUtils.extractPlaceholders(current, currentPlaceHolders);
}
if (resolvedPlaceholders != null) {
// merge maps
Set<Map.Entry<String, GenericsType>> entries = currentPlaceHolders.entrySet();
for (Map.Entry<String, GenericsType> entry : entries) {
GenericsType gt = entry.getValue();
if (!gt.isPlaceholder())
continue;
GenericsType referenced = resolvedPlaceholders.get(gt.getName());
if (referenced == null)
continue;
entry.setValue(referenced);
}
}
resolvedPlaceholders = currentPlaceHolders;
// we are done if we are now in the declaring class
if (!continueLoop)
break;
current = getNextSuperClass(current, declaringClass);
if (current == null && CLASS_Type.equals(declaringClass)) {
// this can happen if the receiver is Class<Foo>, then
// the actual receiver is Foo and declaringClass is Class
current = declaringClass;
}
}
}
if (resolvedPlaceholders == null) {
String descriptor = "<>";
if (method != null)
descriptor = method.getTypeDescriptor();
throw new GroovyBugError("Declaring class for method call to '" + descriptor + "' declared in " + declaringClass.getName() + " was not matched with found receiver " + receiver.getName() + "." + " This should not have happened!");
}
return resolvedPlaceholders;
}
use of org.codehaus.groovy.ast.GenericsType in project groovy by apache.
the class StaticTypeCheckingVisitor method hasRHSIncompleteGenericTypeInfo.
protected static boolean hasRHSIncompleteGenericTypeInfo(final ClassNode inferredRightExpressionType) {
boolean replaceType = false;
GenericsType[] genericsTypes = inferredRightExpressionType.getGenericsTypes();
if (genericsTypes != null) {
for (GenericsType genericsType : genericsTypes) {
if (genericsType.isPlaceholder()) {
replaceType = true;
break;
}
}
}
return replaceType;
}
use of org.codehaus.groovy.ast.GenericsType in project groovy by apache.
the class StaticTypeCheckingVisitor method inferMapExpressionType.
protected ClassNode inferMapExpressionType(final MapExpression map) {
ClassNode mapType = LINKEDHASHMAP_CLASSNODE.getPlainNodeReference();
List<MapEntryExpression> entryExpressions = map.getMapEntryExpressions();
if (entryExpressions.isEmpty())
return mapType;
GenericsType[] genericsTypes = mapType.getGenericsTypes();
if (genericsTypes == null || genericsTypes.length < 2 || (genericsTypes.length == 2 && OBJECT_TYPE.equals(genericsTypes[0].getType()) && OBJECT_TYPE.equals(genericsTypes[1].getType()))) {
List<ClassNode> keyTypes = new LinkedList<ClassNode>();
List<ClassNode> valueTypes = new LinkedList<ClassNode>();
for (MapEntryExpression entryExpression : entryExpressions) {
keyTypes.add(getType(entryExpression.getKeyExpression()));
valueTypes.add(getType(entryExpression.getValueExpression()));
}
// to be used in generics, type must be boxed
ClassNode keyType = getWrapper(lowestUpperBound(keyTypes));
// to be used in generics, type must be boxed
ClassNode valueType = getWrapper(lowestUpperBound(valueTypes));
if (!OBJECT_TYPE.equals(keyType) || !OBJECT_TYPE.equals(valueType)) {
ClassNode inferred = mapType.getPlainNodeReference();
inferred.setGenericsTypes(new GenericsType[] { new GenericsType(wrapTypeIfNecessary(keyType)), new GenericsType(wrapTypeIfNecessary(valueType)) });
return inferred;
}
}
return mapType;
}
use of org.codehaus.groovy.ast.GenericsType in project groovy by apache.
the class StaticTypeCheckingSupport method extractGenericsConnections.
private static void extractGenericsConnections(Map<String, GenericsType> connections, GenericsType[] usage, GenericsType[] declaration) {
// if declaration does not provide generics, there is no connection to make
if (usage == null || declaration == null || declaration.length == 0)
return;
if (usage.length != declaration.length)
return;
// both have generics
for (int i = 0; i < usage.length; i++) {
GenericsType ui = usage[i];
GenericsType di = declaration[i];
if (di.isPlaceholder()) {
connections.put(di.getName(), ui);
} else if (di.isWildcard()) {
if (ui.isWildcard()) {
extractGenericsConnections(connections, ui.getLowerBound(), di.getLowerBound());
extractGenericsConnections(connections, ui.getUpperBounds(), di.getUpperBounds());
} else {
ClassNode cu = ui.getType();
extractGenericsConnections(connections, cu, di.getLowerBound());
ClassNode[] upperBounds = di.getUpperBounds();
if (upperBounds != null) {
for (ClassNode cn : upperBounds) {
extractGenericsConnections(connections, cu, cn);
}
}
}
} else {
extractGenericsConnections(connections, ui.getType(), di.getType());
}
}
}
Aggregations