You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/09/11 16:24:58 UTC
[groovy] 01/02: GROOVY-10107: STC: check assign null before type parameter compatibility
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 <er...@thomsonreuters.com>
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>>()