public static boolean checkCompatibleAssignmentTypes()

in src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java [649:747]


    public static boolean checkCompatibleAssignmentTypes(final ClassNode left, final ClassNode right, final Expression rightExpression, final boolean allowConstructorCoercion) {
        if (isNullConstant(rightExpression)) { // TODO: left is @NonNull
            return (!isPrimitiveType(left) || isPrimitiveBoolean(left)); // GROOVY-6577
        }

        if (left.isArray()) {
            ClassNode leftItemType = left.getComponentType();
            if (right.isArray()) {
                ClassNode rightItemType = right.getComponentType();
                return (isPrimitiveType(leftItemType) && !isPrimitiveBoolean(leftItemType))
                        ? isPrimitiveType(rightItemType) // GROOVY-11371: primitive array only
                        : checkCompatibleAssignmentTypes(leftItemType, rightItemType, rightExpression, false);
            }
            if (rightExpression instanceof ListExpression) {
                return true; // addPrecisionErrors checks values
            }
            if (GeneralUtils.isOrImplements(right, Collection_TYPE)) {
                var elementType = GenericsUtils.parameterizeType(right, Collection_TYPE).getGenericsTypes()[0];
                return isObjectType(leftItemType) // Object[] can accept any collection element type(s)
                    || (elementType.getLowerBound() == null && isCovariant(extractType(elementType), leftItemType));
                    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GROOVY-8984: "? super T" is only compatible with an Object[] target
            }
            if (GeneralUtils.isOrImplements(right, BaseStream_TYPE)) {
                var elementType = GenericsUtils.parameterizeType(right, BaseStream_TYPE).getGenericsTypes()[0];
                return isObjectType(leftItemType) // Object[] can accept any stream API element type(s)
                    || (elementType.getLowerBound() == null && isCovariant(extractType(elementType), getWrapper(leftItemType)));
            }
        }

        ClassNode leftRedirect = left.redirect();
        ClassNode rightRedirect = right.redirect();
        if (leftRedirect == rightRedirect) return true;

        if (leftRedirect == VOID_TYPE) return rightRedirect == void_WRAPPER_TYPE;
        if (leftRedirect == void_WRAPPER_TYPE) return rightRedirect == VOID_TYPE;

        if (leftRedirect == char_TYPE || leftRedirect == Character_TYPE) {
            // GROOVY-10079, GROOVY-10095, GROOVY-10359: assign Character, char or int to char or Character
            if (rightRedirect == Character_TYPE || rightRedirect == char_TYPE || rightRedirect == int_TYPE) {
                return true;
            }
            if (rightRedirect == STRING_TYPE) {
                return rightExpression instanceof ConstantExpression && rightExpression.getText().length() == 1;
            }
        } else if (isLongCategory(getUnwrapper(leftRedirect))) {
            // byte, int, long or short can be assigned any base number type
            if (isNumberType(rightRedirect)) {
                return true;
            }
        } else if (isFloatingCategory(getUnwrapper(leftRedirect))) {
            // float or double can be assigned any base number type or BigDecimal
            if (isNumberType(rightRedirect) || isBigDecimalType(rightRedirect)) {
                return true;
            }
        } else if (left.isGenericsPlaceHolder()) { // must precede non-final types
            return right.getUnresolvedName().charAt(0) != '#' // RHS not adaptable
                    ? left.asGenericsType().isCompatibleWith(right) // GROOVY-7307, GROOVY-9952, GROOVY-11026
                    : implementsInterfaceOrSubclassOf(leftRedirect, rightRedirect); // GROOVY-10067, GROOVY-10342

        } else if (isBigDecimalType(leftRedirect) || Number_TYPE.equals(leftRedirect)) {
            // BigDecimal or Number can be assigned any derivative of java.lang.Number
            if (isNumberType(rightRedirect) || rightRedirect.isDerivedFrom(Number_TYPE)) {
                return true;
            }
        } else if (isBigIntegerType(leftRedirect)) {
            // BigInteger can be assigned byte, char, int, long, short or BigInteger
            if (isLongCategory(getUnwrapper(rightRedirect)) || rightRedirect.isDerivedFrom(BigInteger_TYPE)) {
                return true;
            }
        } else if (leftRedirect.isDerivedFrom(Enum_Type)) {
            // Enum types can be assigned String or GString (triggers `valueOf` call)
            if (rightRedirect == STRING_TYPE || isGStringOrGStringStringLUB(rightRedirect)) {
                return true;
            }
        } else if (isWildcardLeftHandSide(leftRedirect)) {
            // Object, String, [Bb]oolean or Class can be assigned any non-null value
            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 (rightRedirect.isArray() && !leftRedirect.isArray()) {
                return false;
            }
            return true;
        }

        if (implementsInterfaceOrSubclassOf(getWrapper(right), left)) {
            return true;
        }

        if (right.isDerivedFrom(CLOSURE_TYPE) && isSAMType(left)) {
            return true;
        }

        // GROOVY-7316, GROOVY-10256: "Type x = m()" given "def <T> T m()"; T adapts to target
        return right.isGenericsPlaceHolder() && right.asGenericsType().isCompatibleWith(left);
    }