Search in sources :

Example 36 with GenericsType

use of org.codehaus.groovy.ast.GenericsType in project groovy-core by groovy.

the class GenericsUtils method buildWildcardType.

/**
     * Generates a wildcard generic type in order to be used for checks against class nodes.
     * See {@link GenericsType#isCompatibleWith(org.codehaus.groovy.ast.ClassNode)}.
     * @param types the type to be used as the wildcard upper bound
     * @return a wildcard generics type
     */
public static GenericsType buildWildcardType(final ClassNode... types) {
    ClassNode base = ClassHelper.makeWithoutCaching("?");
    GenericsType gt = new GenericsType(base, types, null);
    gt.setWildcard(true);
    return gt;
}
Also used : ClassNode(org.codehaus.groovy.ast.ClassNode) GenericsType(org.codehaus.groovy.ast.GenericsType)

Example 37 with GenericsType

use of org.codehaus.groovy.ast.GenericsType in project groovy-core by groovy.

the class GenericsUtils method applyGenericsContextToPlaceHolders.

/**
     * transforms generics types from an old context to a new context using the given spec. This method assumes
     * all generics types will be placeholders. WARNING: The resulting generics types may or may not be placeholders
     * after the transformation.
     * @param genericsSpec the generics context information spec
     * @param oldPlaceHolders the old placeholders
     * @return the new generics types
     */
public static GenericsType[] applyGenericsContextToPlaceHolders(Map<String, ClassNode> genericsSpec, GenericsType[] oldPlaceHolders) {
    if (oldPlaceHolders == null || oldPlaceHolders.length == 0)
        return oldPlaceHolders;
    if (genericsSpec.isEmpty())
        return oldPlaceHolders;
    GenericsType[] newTypes = new GenericsType[oldPlaceHolders.length];
    for (int i = 0; i < oldPlaceHolders.length; i++) {
        GenericsType old = oldPlaceHolders[i];
        if (!old.isPlaceholder())
            throw new GroovyBugError("Given generics type " + old + " must be a placeholder!");
        ClassNode fromSpec = genericsSpec.get(old.getName());
        if (fromSpec != null) {
            if (fromSpec.isGenericsPlaceHolder()) {
                ClassNode[] upper = new ClassNode[] { fromSpec.redirect() };
                newTypes[i] = new GenericsType(fromSpec, upper, null);
            } else {
                newTypes[i] = new GenericsType(fromSpec);
            }
        } else {
            ClassNode[] upper = old.getUpperBounds();
            ClassNode[] newUpper = upper;
            if (upper != null && upper.length > 0) {
                ClassNode[] upperCorrected = new ClassNode[upper.length];
                for (int j = 0; j < upper.length; j++) {
                    upperCorrected[i] = correctToGenericsSpecRecurse(genericsSpec, upper[j]);
                }
                upper = upperCorrected;
            }
            ClassNode lower = old.getLowerBound();
            ClassNode newLower = correctToGenericsSpecRecurse(genericsSpec, lower);
            if (lower == newLower && upper == newUpper) {
                newTypes[i] = oldPlaceHolders[i];
            } else {
                ClassNode newPlaceHolder = ClassHelper.make(old.getName());
                GenericsType gt = new GenericsType(newPlaceHolder, newUpper, newLower);
                gt.setPlaceholder(true);
                newTypes[i] = gt;
            }
        }
    }
    return newTypes;
}
Also used : ClassNode(org.codehaus.groovy.ast.ClassNode) GenericsType(org.codehaus.groovy.ast.GenericsType) GroovyBugError(org.codehaus.groovy.GroovyBugError)

Example 38 with GenericsType

use of org.codehaus.groovy.ast.GenericsType in project groovy-core by groovy.

the class GenericsUtils method alignGenericTypes.

/**
     * Given a parameterized type and a generic type information, aligns actual type parameters. For example, if a
     * class uses generic type <pre>&lt;T,U,V&gt;</pre> (redirectGenericTypes), is used with actual type parameters
     * <pre>&lt;java.lang.String, U,V&gt;</pre>, then a class or interface using generic types <pre>&lt;T,V&gt;</pre>
     * will be aligned to <pre>&lt;java.lang.String,V&gt;</pre>
     * @param redirectGenericTypes the type arguments or the redirect class node
     * @param parameterizedTypes the actual type arguments used on this class node
     * @param alignmentTarget the generic type arguments to which we want to align to
     * @return aligned type arguments
     * @deprecated You shouldn't call this method because it is inherently unreliable
     */
@Deprecated
public static GenericsType[] alignGenericTypes(final GenericsType[] redirectGenericTypes, final GenericsType[] parameterizedTypes, final GenericsType[] alignmentTarget) {
    if (alignmentTarget == null)
        return EMPTY_GENERICS_ARRAY;
    if (parameterizedTypes == null || parameterizedTypes.length == 0)
        return alignmentTarget;
    GenericsType[] generics = new GenericsType[alignmentTarget.length];
    for (int i = 0, scgtLength = alignmentTarget.length; i < scgtLength; i++) {
        final GenericsType currentTarget = alignmentTarget[i];
        GenericsType match = null;
        if (redirectGenericTypes != null) {
            for (int j = 0; j < redirectGenericTypes.length && match == null; j++) {
                GenericsType redirectGenericType = redirectGenericTypes[j];
                if (redirectGenericType.isCompatibleWith(currentTarget.getType())) {
                    if (currentTarget.isPlaceholder() && redirectGenericType.isPlaceholder() && !currentTarget.getName().equals(redirectGenericType.getName())) {
                        // check if there's a potential better match
                        boolean skip = false;
                        for (int k = j + 1; k < redirectGenericTypes.length && !skip; k++) {
                            GenericsType ogt = redirectGenericTypes[k];
                            if (ogt.isPlaceholder() && ogt.isCompatibleWith(currentTarget.getType()) && ogt.getName().equals(currentTarget.getName())) {
                                skip = true;
                            }
                        }
                        if (skip)
                            continue;
                    }
                    match = parameterizedTypes[j];
                    if (currentTarget.isWildcard()) {
                        // if alignment target is a wildcard type
                        // then we must make best effort to return a parameterized
                        // wildcard
                        ClassNode lower = currentTarget.getLowerBound() != null ? match.getType() : null;
                        ClassNode[] currentUpper = currentTarget.getUpperBounds();
                        ClassNode[] upper = currentUpper != null ? new ClassNode[currentUpper.length] : null;
                        if (upper != null) {
                            for (int k = 0; k < upper.length; k++) {
                                upper[k] = currentUpper[k].isGenericsPlaceHolder() ? match.getType() : currentUpper[k];
                            }
                        }
                        match = new GenericsType(ClassHelper.makeWithoutCaching("?"), upper, lower);
                        match.setWildcard(true);
                    }
                }
            }
        }
        if (match == null) {
            match = currentTarget;
        }
        generics[i] = match;
    }
    return generics;
}
Also used : ClassNode(org.codehaus.groovy.ast.ClassNode) GenericsType(org.codehaus.groovy.ast.GenericsType)

Example 39 with GenericsType

use of org.codehaus.groovy.ast.GenericsType in project groovy-core by groovy.

the class GenericsUtils method extractPlaceholders.

/**
     * For a given classnode, fills in the supplied map with the parameterized
     * types it defines.
     * @param node
     * @param map
     */
public static void extractPlaceholders(ClassNode node, Map<String, GenericsType> map) {
    if (node == null)
        return;
    if (node.isArray()) {
        extractPlaceholders(node.getComponentType(), map);
        return;
    }
    if (!node.isUsingGenerics() || !node.isRedirectNode())
        return;
    GenericsType[] parameterized = node.getGenericsTypes();
    if (parameterized == null || parameterized.length == 0)
        return;
    GenericsType[] redirectGenericsTypes = node.redirect().getGenericsTypes();
    if (redirectGenericsTypes == null)
        redirectGenericsTypes = parameterized;
    for (int i = 0; i < redirectGenericsTypes.length; i++) {
        GenericsType redirectType = redirectGenericsTypes[i];
        if (redirectType.isPlaceholder()) {
            String name = redirectType.getName();
            if (!map.containsKey(name)) {
                GenericsType value = parameterized[i];
                map.put(name, value);
                if (value.isWildcard()) {
                    ClassNode lowerBound = value.getLowerBound();
                    if (lowerBound != null) {
                        extractPlaceholders(lowerBound, map);
                    }
                    ClassNode[] upperBounds = value.getUpperBounds();
                    if (upperBounds != null) {
                        for (ClassNode upperBound : upperBounds) {
                            extractPlaceholders(upperBound, map);
                        }
                    }
                } else if (!value.isPlaceholder()) {
                    extractPlaceholders(value.getType(), map);
                }
            }
        }
    }
}
Also used : ClassNode(org.codehaus.groovy.ast.ClassNode) GenericsType(org.codehaus.groovy.ast.GenericsType)

Example 40 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)

Aggregations

GenericsType (org.codehaus.groovy.ast.GenericsType)171 ClassNode (org.codehaus.groovy.ast.ClassNode)148 InnerClassNode (org.codehaus.groovy.ast.InnerClassNode)76 LowestUpperBoundClassNode (org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode)52 Parameter (org.codehaus.groovy.ast.Parameter)21 MethodNode (org.codehaus.groovy.ast.MethodNode)20 ClosureSignatureHint (groovy.transform.stc.ClosureSignatureHint)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