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/05/19 15:54:52 UTC

[groovy] branch master updated: GROOVY-10342: STC: type parameter can accept parameterized return values

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 006b5987a7 GROOVY-10342: STC: type parameter can accept parameterized return values
006b5987a7 is described below

commit 006b5987a7d91c4a4754d9e4a1191484e43f9868
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu May 19 10:41:43 2022 -0500

    GROOVY-10342: STC: type parameter can accept parameterized return values
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 38 +++++++++++++---------
 .../transform/stc/StaticTypeCheckingVisitor.java   | 11 +++++--
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 31 +++++++++++++++++-
 3 files changed, 60 insertions(+), 20 deletions(-)

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 b4a76624a8..819da3e79d 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -121,6 +121,7 @@ import static org.codehaus.groovy.ast.ClassHelper.make;
 import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
 import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
+import static org.codehaus.groovy.ast.tools.WideningCategories.implementsInterfaceOrSubclassOf;
 import static org.codehaus.groovy.ast.tools.WideningCategories.isFloatingCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
@@ -698,11 +699,21 @@ public abstract class StaticTypeCheckingSupport {
             if (isNumberType(rightRedirect) /*|| rightRedirect == char_TYPE*/) {
                 return true;
             }
+            if (leftRedirect == char_TYPE && rightRedirect == Character_TYPE) return true;
+            if (leftRedirect == Character_TYPE && rightRedirect == char_TYPE) return true;
+            if ((leftRedirect == char_TYPE || leftRedirect == Character_TYPE) && rightRedirect == STRING_TYPE) {
+                return rightExpression instanceof ConstantExpression && rightExpression.getText().length() == 1;
+            }
         } 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.getGenericsTypes()[0].isCompatibleWith(right) // GROOVY-7307, GROOVY-9952, et al.
+                    : implementsInterfaceOrSubclassOf(leftRedirect, rightRedirect); // GROOVY-10067, GROOVY-10342
+
         } else if (isBigDecimalType(leftRedirect) || Number_TYPE.equals(leftRedirect)) {
             // BigDecimal or Number can be assigned any derivitave of java.lang.Number
             if (isNumberType(rightRedirect) || rightRedirect.isDerivedFrom(Number_TYPE)) {
@@ -713,22 +724,16 @@ public abstract class StaticTypeCheckingSupport {
             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 anything (except null to boolean)
             return !(leftRedirect == boolean_TYPE && isNullConstant(rightExpression));
         }
 
-        if (leftRedirect == char_TYPE && rightRedirect == Character_TYPE) return true;
-        if (leftRedirect == Character_TYPE && rightRedirect == char_TYPE) return true;
-        if ((leftRedirect == char_TYPE || leftRedirect == Character_TYPE) && rightRedirect == STRING_TYPE) {
-            return rightExpression instanceof ConstantExpression && rightExpression.getText().length() == 1;
-        }
-
-        // if left is an enum and right is String or GString we do valueOf
-        if (leftRedirect.isDerivedFrom(Enum_Type) && (rightRedirect == STRING_TYPE || isGStringType(rightRedirect))) {
-            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
@@ -738,16 +743,14 @@ public abstract class StaticTypeCheckingSupport {
             return true;
         }
 
-        // simple sub-type check
-        if (!left.isInterface() ? right.isDerivedFrom(left) : GeneralUtils.isOrImplements(right, left)) return true;
+        if (implementsInterfaceOrSubclassOf(right, left)) {
+            return true;
+        }
 
         if (right.isDerivedFrom(CLOSURE_TYPE) && isSAMType(left)) {
             return true;
         }
 
-        if (left.isGenericsPlaceHolder()) {
-            return left.getGenericsTypes()[0].isCompatibleWith(right);
-        }
         // GROOVY-7316, GROOVY-10256: "Type x = m()" given "def <T> T m()"; T adapts to target
         return right.isGenericsPlaceHolder() && right.asGenericsType().isCompatibleWith(left);
     }
@@ -865,6 +868,9 @@ public abstract class StaticTypeCheckingSupport {
      * with trailing "[]".
      */
     static String prettyPrintType(final ClassNode type) {
+        if (type.getUnresolvedName().charAt(0) == '#') {
+            return type.redirect().toString(false);
+        }
         return type.toString(false);
     }
 
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 1b8fbb45ec..88df814abb 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -848,7 +848,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 if (rightExpression instanceof ConstructorCallExpression)
                     inferDiamondType((ConstructorCallExpression) rightExpression, lType);
 
-                if (lType.isUsingGenerics() && missesGenericsTypes(resultType)) {
+                if (lType.isUsingGenerics()
+                        && missesGenericsTypes(resultType)
+                        // GROOVY-10324, GROOVY-10342, et al.
+                        && !resultType.isGenericsPlaceHolder()) {
                     // unchecked assignment
                     // List<Type> list = new LinkedList()
                     // Iterable<Type> iter = new LinkedList()
@@ -858,8 +861,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     // the inferred type of the binary expression is the type of the RHS
                     // "completed" with generics type information available from the LHS
                     if (lType.equals(resultType)) {
+                        // GROOVY-6126, GROOVY-6558, GROOVY-6564, et al.
                         if (!lType.isGenericsPlaceHolder()) resultType = lType;
-                    } else if (!resultType.isGenericsPlaceHolder()) { // GROOVY-10324
+                    } else {
+                        // GROOVY-5640, GROOVY-9033, GROOVY-10220, GROOVY-10235, et al.
                         Map<GenericsTypeName, GenericsType> gt = new HashMap<>();
                         extractGenericsConnections(gt, resultType, resultType.redirect());
                         ClassNode sc = resultType;
@@ -867,7 +872,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         } while (sc != null && !sc.equals(lType));
                         extractGenericsConnections(gt, lType, sc);
 
-                        resultType = applyGenericsContext(gt, resultType.redirect());// GROOVY-10235, et al.
+                        resultType = applyGenericsContext(gt, resultType.redirect());
                     }
                 }
 
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index b330ad771d..7c579b4751 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -500,7 +500,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 chars()
             }
         ''',
-        'Cannot return value of type #T for method returning java.util.List'
+        'Cannot return value of type java.lang.CharSequence for method returning java.util.List'
     }
 
     // GROOVY-10098
@@ -1471,6 +1471,35 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-10342
+    void testAssignmentShouldWorkForParameterizedType2() {
+        assertScript '''
+            class C<T> {
+                T t
+            }
+            def <X> X m() {
+                123
+            }
+            def <N extends Number> void test() {
+                int x = m()
+                Integer y = m()
+                C<Integer> z = new C<>(); z.t = m()
+
+                C<N> c_of_n = new C<N>(); c_of_n.t = m() // Cannot assign value of type #X to variable of type N
+            }
+            test()
+        '''
+
+        shouldFailWithMessages '''
+            def <X extends CharSequence> X m() {
+            }
+            def <N extends Number> void test() {
+                N n = m()
+            }
+        ''',
+        'Cannot assign value of type java.lang.CharSequence to variable of type N'
+    }
+
     // GROOVY-9555
     void testAssignmentShouldWorkForProperUpperBound() {
         assertScript '''