Search in sources :

Example 16 with GenericsType

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);
    }
}
Also used : Variable(org.codehaus.groovy.ast.Variable) DynamicVariable(org.codehaus.groovy.ast.DynamicVariable) MethodNode(org.codehaus.groovy.ast.MethodNode) GenericsType(org.codehaus.groovy.ast.GenericsType) ASTNode(org.codehaus.groovy.ast.ASTNode) LowestUpperBoundClassNode(org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode) ClassNode(org.codehaus.groovy.ast.ClassNode) InnerClassNode(org.codehaus.groovy.ast.InnerClassNode) FieldNode(org.codehaus.groovy.ast.FieldNode) ClosureSignatureHint(groovy.transform.stc.ClosureSignatureHint) LinkedList(java.util.LinkedList) Parameter(org.codehaus.groovy.ast.Parameter)

Example 17 with GenericsType

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;
}
Also used : LowestUpperBoundClassNode(org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode) ClassNode(org.codehaus.groovy.ast.ClassNode) InnerClassNode(org.codehaus.groovy.ast.InnerClassNode) LinkedHashMap(java.util.LinkedHashMap) ListHashMap(org.codehaus.groovy.util.ListHashMap) HashMap(java.util.HashMap) GroovyBugError(org.codehaus.groovy.GroovyBugError) GenericsType(org.codehaus.groovy.ast.GenericsType) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) ListHashMap(org.codehaus.groovy.util.ListHashMap) HashMap(java.util.HashMap)

Example 18 with GenericsType

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;
}
Also used : GenericsType(org.codehaus.groovy.ast.GenericsType)

Example 19 with GenericsType

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;
}
Also used : LowestUpperBoundClassNode(org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode) ClassNode(org.codehaus.groovy.ast.ClassNode) InnerClassNode(org.codehaus.groovy.ast.InnerClassNode) GenericsType(org.codehaus.groovy.ast.GenericsType) LinkedList(java.util.LinkedList)

Example 20 with GenericsType

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());
        }
    }
}
Also used : ClassNode(org.codehaus.groovy.ast.ClassNode) GenericsType(org.codehaus.groovy.ast.GenericsType)

Aggregations

GenericsType (org.codehaus.groovy.ast.GenericsType)165 ClassNode (org.codehaus.groovy.ast.ClassNode)143 InnerClassNode (org.codehaus.groovy.ast.InnerClassNode)71 LowestUpperBoundClassNode (org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode)52 Parameter (org.codehaus.groovy.ast.Parameter)20 ClosureSignatureHint (groovy.transform.stc.ClosureSignatureHint)19 MethodNode (org.codehaus.groovy.ast.MethodNode)19 LinkedList (java.util.LinkedList)18 HashMap (java.util.HashMap)17 ArrayList (java.util.ArrayList)15 LinkedHashMap (java.util.LinkedHashMap)9 AST (antlr.collections.AST)8 AnnotationNode (org.codehaus.groovy.ast.AnnotationNode)8 ListHashMap (org.codehaus.groovy.util.ListHashMap)8 Map (java.util.Map)6 AtomicReference (java.util.concurrent.atomic.AtomicReference)6 EnumConstantClassNode (org.codehaus.groovy.ast.EnumConstantClassNode)6 FieldNode (org.codehaus.groovy.ast.FieldNode)6 GroovyBugError (org.codehaus.groovy.GroovyBugError)5 DynamicVariable (org.codehaus.groovy.ast.DynamicVariable)4