Java Code Examples for org.codehaus.groovy.ast.ClassNode#isGenericsPlaceHolder()

The following examples show how to use org.codehaus.groovy.ast.ClassNode#isGenericsPlaceHolder() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: InstanceOfVerifier.java    From groovy with Apache License 2.0 6 votes vote down vote up
@Override
public void visitBinaryExpression(BinaryExpression expression) {
    if (expression.getOperation().isA(Types.INSTANCEOF_OPERATOR) &&
            expression.getRightExpression() instanceof ClassExpression) {
        ClassNode referenceType = expression.getRightExpression().getType();

        if (ClassHelper.isPrimitiveType(referenceType)) {
            addTypeError(expression.getRightExpression(), "primitive type " + referenceType.getName());
        } else {
            while (referenceType.isArray()) {
                referenceType = referenceType.getComponentType();
            }

            if (referenceType.isGenericsPlaceHolder()) {
                addTypeError(expression.getRightExpression(), "type parameter " + referenceType.getUnresolvedName() +
                    ". Use its erasure " + referenceType.getNameWithoutPackage() + " instead since further generic type information will be erased at runtime");
            } else if (referenceType.getGenericsTypes() != null) {
                // TODO: Cannot perform instanceof check against parameterized type Class<Type>. Use the form Class<?> instead since further eneric type information will be erased at runtime
            }
        }
    }
    super.visitBinaryExpression(expression);
}
 
Example 2
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 6 votes vote down vote up
/**
 * Returns true if the provided class node, when considered as a receiver of a message or as a parameter,
 * is using a placeholder in its generics type. In this case, we're facing unchecked generics and type
 * checking is limited (ex: void foo(Set s) { s.keySet() }
 *
 * @param node the node to test
 * @return true if it is using any placeholder in generics types
 */
public static boolean isUsingUncheckedGenerics(final ClassNode node) {
    if (node.isArray()) return isUsingUncheckedGenerics(node.getComponentType());
    GenericsType[] genericsTypes = node.getGenericsTypes();
    if (genericsTypes != null) {
        for (GenericsType genericsType : genericsTypes) {
            if (genericsType.isPlaceholder()) return true;
            if (genericsType.isWildcard()) {
                ClassNode lowerBound = genericsType.getLowerBound();
                ClassNode[] upperBounds = genericsType.getUpperBounds();
                if (lowerBound != null) {
                    if (lowerBound.isGenericsPlaceHolder() || isUsingUncheckedGenerics(lowerBound)) return true;
                } else if (upperBounds != null) {
                    if (upperBounds[0].isGenericsPlaceHolder() || isUsingUncheckedGenerics(upperBounds[0])) return true;
                }
            } else {
                if (isUsingUncheckedGenerics(genericsType.getType())) return true;
            }
        }
    }
    return false;
}
 
Example 3
Source File: BytecodeHelper.java    From groovy with Apache License 2.0 6 votes vote down vote up
public static String getGenericsMethodSignature(MethodNode node) {
    GenericsType[] generics = node.getGenericsTypes();
    Parameter[] param = node.getParameters();
    ClassNode returnType = node.getReturnType();

    if (generics == null && !hasGenerics(param) && !hasGenerics(returnType)) return null;

    StringBuilder ret = new StringBuilder(100);
    getGenericsTypeSpec(ret, generics);

    GenericsType[] paramTypes = new GenericsType[param.length];
    for (int i = 0; i < param.length; i++) {
        ClassNode pType = param[i].getType();
        if (pType.getGenericsTypes() == null || !pType.isGenericsPlaceHolder()) {
            paramTypes[i] = new GenericsType(pType);
        } else {
            paramTypes[i] = pType.getGenericsTypes()[0];
        }
    }
    addSubTypes(ret, paramTypes, "(", ")");
    addSubTypes(ret, new GenericsType[]{new GenericsType(returnType)}, "", "");
    return ret.toString();
}
 
Example 4
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 6 votes vote down vote up
/**
 * Uses supplied type to make a connection from usage to declaration.
 * <p>
 * The method operates in two modes:
 * <ul>
 * <li>For type !instanceof target a structural compare will be done
 *     (for example Type&lt;T&gt; and List&lt;E&gt; to get E -> T)
 * <li>If type equals target, a structural match is done as well
 *     (for example Collection&lt;T&gt; and Collection&lt;E&gt; to get E -> T)
 * <li>Otherwise we climb the hierarchy to find a case of type equals target
 *     to then execute the structural match, while applying possibly existing
 *     generics contexts on the way (for example for IntRange and Collection&lt;E&gt;
 *     to get E -> Integer, since IntRange is an AbstractList&lt;Integer&gt;)
 * </ul>
 * Should the target not have any generics this method does nothing.
 */
static void extractGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final ClassNode type, final ClassNode target) {
    if (target == null || type == target || !isUsingGenericsOrIsArrayUsingGenerics(target)) return;
    if (type == null || type == UNKNOWN_PARAMETER_TYPE) return;

    if (target.isGenericsPlaceHolder()) {
        connections.put(new GenericsTypeName(target.getGenericsTypes()[0].getName()), new GenericsType(type));

    } else if (type.isArray() && target.isArray()) {
        extractGenericsConnections(connections, type.getComponentType(), target.getComponentType());

    } else if (type.equals(target) || !implementsInterfaceOrIsSubclassOf(type, target)) {
        extractGenericsConnections(connections, type.getGenericsTypes(), target.getGenericsTypes());

    } else {
        // first find matching super class or interface
        ClassNode superClass = getSuperClass(type, target);
        if (superClass != null) {
            extractGenericsConnections(connections, getCorrectedClassNode(type, superClass, true), target);
        } else {
            // if we reach here, we have an unhandled case
            throw new GroovyBugError("The type " + type + " seems not to normally extend " + target + ". Sorry, I cannot handle this.");
        }
    }
}
 
Example 5
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 6 votes vote down vote up
static ClassNode applyGenericsContext(final Map<GenericsTypeName, GenericsType> spec, final ClassNode bound) {
    if (bound == null) return null;
    if (bound.isArray()) {
        return applyGenericsContext(spec, bound.getComponentType()).makeArray();
    }
    if (!bound.isUsingGenerics()) return bound;
    ClassNode newBound = bound.getPlainNodeReference();
    newBound.setGenericsTypes(applyGenericsContext(spec, bound.getGenericsTypes()));
    if (bound.isGenericsPlaceHolder()) {
        GenericsType[] gt = newBound.getGenericsTypes();
        boolean hasBounds = hasNonTrivialBounds(gt[0]);
        if (hasBounds || !gt[0].isPlaceholder()) return getCombinedBoundType(gt[0]);
        String placeHolderName = newBound.getGenericsTypes()[0].getName();
        if (!placeHolderName.equals(newBound.getUnresolvedName())) {
            // we should produce a clean placeholder ClassNode here
            ClassNode clean = make(placeHolderName);
            clean.setGenericsTypes(newBound.getGenericsTypes());
            clean.setRedirect(newBound);
            newBound = clean;
        }
        newBound.setGenericsPlaceHolder(true);
    }
    return newBound;
}
 
Example 6
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 6 votes vote down vote up
public static boolean missesGenericsTypes(final ClassNode cn) {
    if (cn.isArray()) return missesGenericsTypes(cn.getComponentType());
    GenericsType[] cnTypes = cn.getGenericsTypes();
    GenericsType[] rnTypes = cn.redirect().getGenericsTypes();
    if (rnTypes != null && cnTypes == null) return true;
    if (cnTypes != null) {
        for (GenericsType genericsType : cnTypes) {
            if (genericsType.isPlaceholder()) return true;
            if (genericsType.isWildcard()) {
                ClassNode lowerBound = genericsType.getLowerBound();
                ClassNode[] upperBounds = genericsType.getUpperBounds();
                if (lowerBound != null) {
                    if (lowerBound.isGenericsPlaceHolder() || missesGenericsTypes(lowerBound)) return true;
                } else if (upperBounds != null) {
                    if (upperBounds[0].isGenericsPlaceHolder() || missesGenericsTypes(upperBounds[0])) return true;
                }
            }
        }
    }
    return false;
}
 
Example 7
Source File: BytecodeHelper.java    From groovy with Apache License 2.0 5 votes vote down vote up
private static void writeGenericsBoundType(StringBuilder ret, ClassNode printType, boolean writeInterfaceMarker) {
    if (writeInterfaceMarker && printType.isInterface()) ret.append(":");
    if (printType.isGenericsPlaceHolder() && printType.getGenericsTypes()!=null) {
        ret.append("T");
        ret.append(printType.getGenericsTypes()[0].getName());
        ret.append(";");
    }
    else {
        ret.append(getTypeDescription(printType, false));
        addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
        if (!ClassHelper.isPrimitiveType(printType)) ret.append(";");
    }
}
 
Example 8
Source File: GroovyVirtualSourceProvider.java    From netbeans with Apache License 2.0 5 votes vote down vote up
private void writeGenericsBounds(PrintWriter out, ClassNode type, boolean skipName) {
    if (!skipName) {
        printTypeName(type, out);
    }
    if (java5 && !type.isGenericsPlaceHolder()) {
        writeGenericsBounds(out, type.getGenericsTypes());
    }
}
 
Example 9
Source File: BytecodeHelper.java    From groovy with Apache License 2.0 5 votes vote down vote up
public static String getGenericsBounds(ClassNode type) {
    GenericsType[] genericsTypes = type.getGenericsTypes();
    if (genericsTypes == null) return null;
    StringBuilder ret = new StringBuilder(100);
    if (type.isGenericsPlaceHolder()) {
        addSubTypes(ret, type.getGenericsTypes(), "", "");
    } else {
        GenericsType gt = new GenericsType(type);
        writeGenericsBounds(ret, gt, false);
    }
    return ret.toString();
}
 
Example 10
Source File: GenericsUtils.java    From groovy with Apache License 2.0 5 votes vote down vote up
private static void extractSuperClassGenerics(ClassNode[] usage, ClassNode[] declaration, Map<String, ClassNode> spec) {
    if (usage == null || declaration == null || declaration.length == 0) return;
    // both have generics
    for (int i = 0; i < usage.length; i++) {
        ClassNode ui = usage[i];
        ClassNode di = declaration[i];
        if (di.isGenericsPlaceHolder()) {
            spec.put(di.getGenericsTypes()[0].getName(), di);
        } else if (di.isUsingGenerics()) {
            extractSuperClassGenerics(ui.getGenericsTypes(), di.getGenericsTypes(), spec);
        }
    }
}
 
Example 11
Source File: BinaryExpressionHelper.java    From groovy with Apache License 2.0 5 votes vote down vote up
private VariableSlotLoader loadWithSubscript(final Expression expression) {
    AsmClassGenerator acg = controller.getAcg();
    // if we have a BinaryExpression, check if it is with subscription
    if (expression instanceof BinaryExpression) {
        BinaryExpression bexp = (BinaryExpression) expression;
        if (bexp.getOperation().getType() == LEFT_SQUARE_BRACKET) {
            // right expression is the subscript expression
            // we store the result of the subscription on the stack
            Expression subscript = bexp.getRightExpression();
            subscript.visit(acg);
            OperandStack operandStack = controller.getOperandStack();
            ClassNode subscriptType = operandStack.getTopOperand();
            if (subscriptType.isGenericsPlaceHolder() || GenericsUtils.hasPlaceHolders(subscriptType)) {
                subscriptType = controller.getTypeChooser().resolveType(bexp, controller.getClassNode());
            }
            int id = controller.getCompileStack().defineTemporaryVariable("$subscript", subscriptType, true);
            VariableSlotLoader subscriptExpression = new VariableSlotLoader(subscriptType, id, operandStack);
            BinaryExpression rewrite = binX(bexp.getLeftExpression(), bexp.getOperation(), subscriptExpression);
            rewrite.copyNodeMetaData(bexp);
            rewrite.setSourcePosition(bexp);
            rewrite.visit(acg);
            return subscriptExpression;
        }
    }

    // normal loading of expression
    expression.visit(acg);
    return null;
}
 
Example 12
Source File: GenericsUtils.java    From groovy with Apache License 2.0 5 votes vote down vote up
public static Map<String, ClassNode> addMethodGenerics(MethodNode current, Map<String, ClassNode> oldSpec) {
    Map<String, ClassNode> newSpec = new HashMap<>(oldSpec);
    GenericsType[] gts = current.getGenericsTypes();
    if (gts != null) {
        for (GenericsType gt : gts) {
            String name = gt.getName();
            ClassNode type = gt.getType();
            if (gt.isPlaceholder()) {
                ClassNode redirect;
                if (gt.getUpperBounds() != null) {
                    redirect = gt.getUpperBounds()[0];
                } else if (gt.getLowerBound() != null) {
                    redirect = gt.getLowerBound();
                } else {
                    redirect = ClassHelper.OBJECT_TYPE;
                }
                if (redirect.isGenericsPlaceHolder()) {
                    // "T extends U" or "T super U"
                    type = redirect;
                } else {
                    // "T" or "T extends Thing" or "T super Thing"
                    type = ClassHelper.makeWithoutCaching(name);
                    type.setGenericsPlaceHolder(true);
                    type.setRedirect(redirect);
                }
            }
            newSpec.put(name, type);
        }
    }
    return newSpec;
}
 
Example 13
Source File: GenericsUtils.java    From groovy with Apache License 2.0 5 votes vote down vote up
public static ClassNode correctToGenericsSpec(Map<String, ClassNode> genericsSpec, ClassNode type) {
    if (type.isArray()) {
        return correctToGenericsSpec(genericsSpec, type.getComponentType()).makeArray();
    }
    if (type.isGenericsPlaceHolder() && type.getGenericsTypes() != null) {
        String name = type.getGenericsTypes()[0].getName();
        type = genericsSpec.get(name);
        if (type != null && type.isGenericsPlaceHolder()
                && !name.equals(type.getUnresolvedName())) {
            return correctToGenericsSpec(genericsSpec, type);
        }
    }
    if (type == null) type = ClassHelper.OBJECT_TYPE;
    return type;
}
 
Example 14
Source File: JavaStubGenerator.java    From groovy with Apache License 2.0 5 votes vote down vote up
private void printType(PrintWriter out, ClassNode type) {
    if (type.isArray()) {
        printType(out, type.getComponentType());
        out.print("[]");
    } else if (java5 && type.isGenericsPlaceHolder()) {
        out.print(type.getUnresolvedName());
    } else {
        printGenericsBounds(out, type, false);
    }
}
 
Example 15
Source File: MemberSignatureParser.java    From groovy with Apache License 2.0 5 votes vote down vote up
private static ClassNode applyErasure(ClassNode genericType, ClassNode erasure) {
    if (genericType.isArray() && erasure.isArray() && genericType.getComponentType().isGenericsPlaceHolder()) {
        genericType.setRedirect(erasure);
        genericType.getComponentType().setRedirect(erasure.getComponentType());
    } else if (genericType.isGenericsPlaceHolder()) {
        genericType.setRedirect(erasure);
    }
    return genericType;
}
 
Example 16
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 5 votes vote down vote up
private static void extractGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final ClassNode[] usage, final ClassNode[] declaration) {
    if (usage == null || declaration == null || declaration.length == 0) return;
    // both have generics
    for (int i = 0, n = usage.length; i < n; i += 1) {
        ClassNode ui = usage[i];
        ClassNode di = declaration[i];
        if (di.isGenericsPlaceHolder()) {
            GenericsType gt = new GenericsType(di);
            gt.setPlaceholder(di.isGenericsPlaceHolder());
            connections.put(new GenericsTypeName(di.getGenericsTypes()[0].getName()), gt);
        } else if (di.isUsingGenerics()) {
            extractGenericsConnections(connections, ui.getGenericsTypes(), di.getGenericsTypes());
        }
    }
}
 
Example 17
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 5 votes vote down vote up
private static boolean equalIncludingGenerics(final ClassNode orig, final ClassNode copy) {
    if (orig == copy) return true;
    if (orig.isGenericsPlaceHolder() != copy.isGenericsPlaceHolder()) return false;
    if (!orig.equals(copy)) return false;
    GenericsType[] gt1 = orig.getGenericsTypes();
    GenericsType[] gt2 = orig.getGenericsTypes();
    if ((gt1 == null) ^ (gt2 == null)) return false;
    if (gt1 != gt2) {
        if (gt1.length != gt2.length) return false;
        for (int i = 0, n = gt1.length; i < n; i += 1) {
            if (!equalIncludingGenerics(gt1[i], gt2[i])) return false;
        }
    }
    return true;
}
 
Example 18
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 5 votes vote down vote up
private static boolean inferenceCheck(final Set<GenericsTypeName> fixedGenericsPlaceHolders, final Map<GenericsTypeName, GenericsType> resolvedMethodGenerics, ClassNode type, ClassNode wrappedArgument, final boolean lastArg) {
    Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
    if (isPrimitiveType(wrappedArgument)) wrappedArgument = getWrapper(wrappedArgument);

    if (lastArg &&
            type.isArray() && type.getComponentType().isGenericsPlaceHolder() &&
            !wrappedArgument.isArray() && wrappedArgument.isGenericsPlaceHolder()) {
        // GROOVY-8090: handle generics varargs, e.g. "U x = ...; Arrays.asList(x)"
        // we should connect the type of vararg(e.g. T is the type of T...) to the argument type
        type = type.getComponentType();
    }
    // the context we compare with in the end is the one of the callsite
    // so far we specified the context of the method declaration only
    // thus for each argument, we try to find the connected generics first
    extractGenericsConnections(connections, wrappedArgument, type);
    // each found connection must comply with already found connections
    boolean failure = !compatibleConnections(connections, resolvedMethodGenerics, fixedGenericsPlaceHolders);
    // and then apply the found information to refine the method level
    // information. This way the method level information slowly turns
    // into information for the callsite
    applyGenericsConnections(connections, resolvedMethodGenerics);
    // since it is possible that the callsite uses some generics as well,
    // we may have to add additional elements here
    addMissingEntries(connections, resolvedMethodGenerics);
    // to finally see if the parameter and the argument fit together,
    // we use the provided information to transform the parameter
    // into something that can exist in the callsite context
    type = applyGenericsContext(resolvedMethodGenerics, type);
    // there of course transformed parameter type and argument must fit
    failure = failure || !typeCheckMethodArgumentWithGenerics(type, wrappedArgument, lastArg);
    return failure;
}
 
Example 19
Source File: GenericsUtils.java    From groovy with Apache License 2.0 4 votes vote down vote up
public static ClassNode correctToGenericsSpecRecurse(Map<String, ClassNode> genericsSpec, ClassNode type, List<String> exclusions) {
    if (type.isArray()) {
        return correctToGenericsSpecRecurse(genericsSpec, type.getComponentType(), exclusions).makeArray();
    }
    if (type.isGenericsPlaceHolder() && !exclusions.contains(type.getUnresolvedName())) {
        String name = type.getGenericsTypes()[0].getName();
        exclusions = plus(exclusions, name); // GROOVY-7722
        type = genericsSpec.get(name);
        if (type != null && type.isGenericsPlaceHolder()) {
            if (type.getGenericsTypes() == null) {
                ClassNode placeholder = ClassHelper.makeWithoutCaching(type.getUnresolvedName());
                placeholder.setGenericsPlaceHolder(true);
                return makeClassSafeWithGenerics(type, new GenericsType(placeholder));
            } else if (!name.equals(type.getUnresolvedName())) {
                return correctToGenericsSpecRecurse(genericsSpec, type, exclusions);
            }
        }
    }
    if (type == null) type = ClassHelper.OBJECT_TYPE;
    GenericsType[] oldgTypes = type.getGenericsTypes();
    GenericsType[] newgTypes = EMPTY_GENERICS_ARRAY;
    if (oldgTypes != null) {
        newgTypes = new GenericsType[oldgTypes.length];
        for (int i = 0; i < newgTypes.length; i++) {
            GenericsType oldgType = oldgTypes[i];
            if (oldgType.isWildcard()) {
                ClassNode[] oldUpper = oldgType.getUpperBounds();
                ClassNode[] upper = null;
                if (oldUpper != null) {
                    // correct "? extends T" or "? extends T & I"
                    upper = new ClassNode[oldUpper.length];
                    for (int j = 0; j < oldUpper.length; j++) {
                        upper[j] = correctToGenericsSpecRecurse(genericsSpec, oldUpper[j], exclusions);
                    }
                }
                ClassNode oldLower = oldgType.getLowerBound();
                ClassNode lower = null;
                if (oldLower != null) {
                    // correct "? super T"
                    lower = correctToGenericsSpecRecurse(genericsSpec, oldLower, exclusions);
                }
                GenericsType fixed = new GenericsType(oldgType.getType(), upper, lower);
                fixed.setWildcard(true);
                newgTypes[i] = fixed;
            } else if (oldgType.isPlaceholder()) {
                // correct "T"
                newgTypes[i] = new GenericsType(genericsSpec.getOrDefault(oldgType.getName(), ClassHelper.OBJECT_TYPE));
            } else {
                // correct "List<T>", etc.
                newgTypes[i] = new GenericsType(correctToGenericsSpecRecurse(genericsSpec, correctToGenericsSpec(genericsSpec, oldgType), exclusions));
            }
        }
    }
    return makeClassSafeWithGenerics(type, newgTypes);
}
 
Example 20
Source File: StaticTypeCheckingSupport.java    From groovy with Apache License 2.0 4 votes vote down vote up
public static boolean checkCompatibleAssignmentTypes(final ClassNode left, final ClassNode right, final Expression rightExpression, final boolean allowConstructorCoercion) {
    ClassNode leftRedirect = left.redirect();
    ClassNode rightRedirect = right.redirect();
    if (leftRedirect == rightRedirect) return true;

    if (leftRedirect.isArray() && rightRedirect.isArray()) {
        return checkCompatibleAssignmentTypes(leftRedirect.getComponentType(), rightRedirect.getComponentType(), rightExpression, false);
    }

    if (right == VOID_TYPE || right == void_WRAPPER_TYPE) {
        return left == VOID_TYPE || left == void_WRAPPER_TYPE;
    }

    if ((isNumberType(rightRedirect) || WideningCategories.isNumberCategory(rightRedirect))) {
        if (BigDecimal_TYPE == leftRedirect) {
            // any number can be assigned to a big decimal
            return true;
        }
        if (BigInteger_TYPE == leftRedirect) {
            return WideningCategories.isBigIntCategory(getUnwrapper(rightRedirect)) || rightRedirect.isDerivedFrom(BigInteger_TYPE);
        }
    }

    // if rightExpression is null and leftExpression is not a primitive type, it's ok
    boolean rightExpressionIsNull = (rightExpression instanceof ConstantExpression && ((ConstantExpression) rightExpression).isNullExpression());
    if (rightExpressionIsNull && !isPrimitiveType(left)) {
        return true;
    }

    // on an assignment everything that can be done by a GroovyCast is allowed

    // anything can be assigned to an Object, String, Boolean or Class typed variable
    if (isWildcardLeftHandSide(leftRedirect) && !(boolean_TYPE.equals(left) && rightExpressionIsNull)) return true;

    // char as left expression
    if (leftRedirect == char_TYPE && rightRedirect == STRING_TYPE) {
        if (rightExpression instanceof ConstantExpression) {
            String value = rightExpression.getText();
            return value.length() == 1;
        }
    }
    if (leftRedirect == Character_TYPE && (rightRedirect == STRING_TYPE || rightExpressionIsNull)) {
        return rightExpressionIsNull || (rightExpression instanceof ConstantExpression && rightExpression.getText().length() == 1);
    }

    // if left is Enum and right is String or GString we do valueOf
    if (leftRedirect.isDerivedFrom(Enum_Type) && (rightRedirect == GSTRING_TYPE || rightRedirect == STRING_TYPE)) {
        return true;
    }

    // if right is array, map or collection we try invoking the constructor
    if (allowConstructorCoercion && isGroovyConstructorCompatible(rightExpression)) {
        // TODO: in case of the array we could maybe make a partial check
        if (leftRedirect.isArray() && rightRedirect.isArray()) {
            return checkCompatibleAssignmentTypes(leftRedirect.getComponentType(), rightRedirect.getComponentType());
        } else if (rightRedirect.isArray() && !leftRedirect.isArray()) {
            return false;
        }
        return true;
    }

    // simple check on being subclass
    if (right.isDerivedFrom(left) || (left.isInterface() && right.implementsInterface(left))) return true;

    // if left and right are primitives or numbers allow
    if (isPrimitiveType(leftRedirect) && isPrimitiveType(rightRedirect)) return true;
    if (isNumberType(leftRedirect) && isNumberType(rightRedirect)) return true;

    // left is a float/double and right is a BigDecimal
    if (WideningCategories.isFloatingCategory(leftRedirect) && BigDecimal_TYPE.equals(rightRedirect)) {
        return true;
    }

    if (GROOVY_OBJECT_TYPE.equals(leftRedirect) && isBeingCompiled(right)) {
        return true;
    }

    if (left.isGenericsPlaceHolder()) {
        // GROOVY-7307
        GenericsType[] genericsTypes = left.getGenericsTypes();
        if (genericsTypes != null && genericsTypes.length == 1) {
            // should always be the case, but safe guard is better
            return genericsTypes[0].isCompatibleWith(right);
        }
    }

    // GROOVY-7316: It is an apparently legal thing to allow this. It's not type safe, but it is allowed...
    return right.isGenericsPlaceHolder();
}