This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_2_5_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit fa0628603cbbced140e49e064a6dee8da9c85a29 Author: Eric Milles <[email protected]> AuthorDate: Sun Sep 11 10:41:39 2022 -0500 GROOVY-10107: STC: check assign null before type parameter compatibility --- .../java/org/codehaus/groovy/ast/GenericsType.java | 2 +- .../codehaus/groovy/ast/tools/GenericsUtils.java | 2 +- .../transform/stc/StaticTypeCheckingSupport.java | 58 ++++++++++------------ .../groovy/transform/stc/GenericsSTCTest.groovy | 12 +++++ 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java index cd123a1a44..75cd9f1458 100644 --- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java +++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java @@ -437,7 +437,7 @@ public class GenericsType extends ASTNode { match = gt.checkGenerics(classNodeType.getLowerBound()); } else if (classNodeType.getUpperBounds() != null) { match = gt.checkGenerics(classNodeType.getUpperBounds()[0]); - } else { // GROOVY-10576: "?" vs "? extends Object" (citation required) or no match + } else { // GROOVY-10267, GROOVY-10576: "?" vs "? extends Object" (citation required) or no match match = (!gt.isPlaceholder() && !gt.isWildcard() && ClassHelper.OBJECT_TYPE.equals(gt.getType())); } } else { diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java index db1a12b1b7..25a4205a19 100644 --- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -173,7 +173,7 @@ public class GenericsUtils { GenericsType[] parameterized = type.getGenericsTypes(); int n; if (parameterized == null || (n = parameterized.length) == 0) return; - // GROOVY-8609, GROOVY-10067, etc. + // GROOVY-8609 if (type.isGenericsPlaceHolder()) { GenericsType gt = parameterized[0]; GenericsType.GenericsTypeName name = new GenericsType.GenericsTypeName(gt.getName()); diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java index a1abf7fc8e..10121386a6 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -680,44 +680,38 @@ public abstract class StaticTypeCheckingSupport { } public static boolean checkCompatibleAssignmentTypes(ClassNode left, ClassNode right, Expression rightExpression, boolean allowConstructorCoercion) { - ClassNode leftRedirect = left.redirect(); - ClassNode rightRedirect = right.redirect(); - if (leftRedirect == rightRedirect) return true; + boolean rightExpressionIsNull = isNullConstant(rightExpression); + if (rightExpressionIsNull && !isPrimitiveType(left)) { + return true; + } - if (leftRedirect.isArray() && rightRedirect.isArray()) { - ClassNode leftComponent = leftRedirect.getComponentType(); - ClassNode rightComponent = rightRedirect.getComponentType(); + if (left.isArray() && right.isArray()) { + ClassNode leftComponent = left.getComponentType(); + ClassNode rightComponent = right.getComponentType(); if (isPrimitiveType(leftComponent) != isPrimitiveType(rightComponent)) return false; return checkCompatibleAssignmentTypes(leftComponent, rightComponent, rightExpression, false); } - if (right == VOID_TYPE || right == void_WRAPPER_TYPE) { - return left == VOID_TYPE || left == void_WRAPPER_TYPE; - } + ClassNode leftRedirect = left.redirect(); + ClassNode rightRedirect = right.redirect(); + if (leftRedirect == rightRedirect) return true; + + if (rightRedirect == void_WRAPPER_TYPE) return leftRedirect == VOID_TYPE; + if (rightRedirect == VOID_TYPE) return leftRedirect == void_WRAPPER_TYPE; if (isNumberType(rightRedirect) || WideningCategories.isNumberCategory(rightRedirect)) { if (leftRedirect.equals(BigDecimal_TYPE) || leftRedirect.equals(Number_TYPE)) { - // any number can be assigned to BigDecimal or Number - return true; + return true; // any number can be assigned to BigDecimal or Number } if (leftRedirect.equals(BigInteger_TYPE)) { - return WideningCategories.isBigIntCategory(getUnwrapper(rightRedirect)) || - rightRedirect.isDerivedFrom(BigInteger_TYPE); + 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 = isNullConstant(rightExpression); - 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) - && !(left.equals(boolean_TYPE) && rightExpressionIsNull)) return true; + // anything can be assigned to an Object, String, [Bb]oolean or Class receiver; except null to boolean + if (isWildcardLeftHandSide(left) && !(leftRedirect == boolean_TYPE && rightExpressionIsNull)) return true; // char as left expression if (leftRedirect == char_TYPE && rightRedirect == STRING_TYPE) { @@ -1829,25 +1823,25 @@ public abstract class StaticTypeCheckingSupport { private static void extractGenericsConnections(Map<GenericsTypeName, 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; + final int n; if ((n = usage.length) != declaration.length) return; // both have generics - for (int i = 0; i < usage.length; i++) { + for (int i = 0; i < n; i += 1) { GenericsType ui = usage[i]; GenericsType di = declaration[i]; if (di.isPlaceholder()) { connections.put(new GenericsTypeName(di.getName()), ui); } else if (di.isWildcard()) { + ClassNode lowerBound = di.getLowerBound(), upperBounds[] = di.getUpperBounds(); if (ui.isWildcard()) { - extractGenericsConnections(connections, ui.getLowerBound(), di.getLowerBound()); - extractGenericsConnections(connections, ui.getUpperBounds(), di.getUpperBounds()); + extractGenericsConnections(connections, ui.getLowerBound(), lowerBound); + extractGenericsConnections(connections, ui.getUpperBounds(), upperBounds); } else { - ClassNode cu = ui.getType(); - extractGenericsConnections(connections, cu, di.getLowerBound()); - ClassNode[] upperBounds = di.getUpperBounds(); + ClassNode ui_type = ui.getType(); + extractGenericsConnections(connections, ui_type, lowerBound); if (upperBounds != null) { - for (ClassNode cn : upperBounds) { - extractGenericsConnections(connections, cu, cn); + for (ClassNode ub : upperBounds) { + extractGenericsConnections(connections, ui_type, ub); } } } diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy index 119e4a0c9b..165ca7fd41 100644 --- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy +++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy @@ -2078,6 +2078,18 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-10107 + void testAssignNullTypeParameterWithUpperBounds() { + assertScript ''' + class C<T extends Number> { + void m() { + T n = null + } + } + new C<Long>().m() + ''' + } + void testMethodCallWithArgumentUsingNestedGenerics() { assertScript ''' ThreadLocal<Map<Integer, String>> cachedConfigs = new ThreadLocal<Map<Integer, String>>()
