You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/07/18 07:18:54 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-7848: retain generics of list or map elements (port to 2_5_X)

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

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 7f43e9e  GROOVY-7848: retain generics of list or map elements (port to 2_5_X)
7f43e9e is described below

commit 7f43e9eb71850c53a4dd61ac86e7e004eda1bb20
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Jul 13 09:52:56 2020 -0500

    GROOVY-7848: retain generics of list or map elements (port to 2_5_X)
    
        [[1],[2]] should infer as List<List<Integer>>
        [1:['a'],2:['b']] should infer as Map<Integer,List<String>>
---
 .../java/org/codehaus/groovy/ast/GenericsType.java |  4 +--
 .../transform/stc/StaticTypeCheckingVisitor.java   | 38 ++++++++++------------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 20 +++++++++---
 3 files changed, 36 insertions(+), 26 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index 0f4d301..e68dfe0 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -62,8 +62,8 @@ public class GenericsType extends ASTNode {
         return type;
     }
 
-    public void setType(ClassNode type) {
-        this.type = type;
+    public void setType(final ClassNode type) {
+            this.type = type;
     }
 
     public String toString() {
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 6317fa5..1234ce1 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -4889,18 +4889,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     protected ClassNode inferListExpressionType(final ListExpression list) {
         List<Expression> expressions = list.getExpressions();
-        if (expressions.isEmpty()) {
-            // cannot infer, return list type
+        int nExpressions = expressions.size();
+        if (nExpressions == 0) {
             return list.getType();
         }
         ClassNode listType = list.getType();
         GenericsType[] genericsTypes = listType.getGenericsTypes();
         if ((genericsTypes == null
                 || genericsTypes.length == 0
-                || (genericsTypes.length == 1 && OBJECT_TYPE.equals(genericsTypes[0].getType())))
-                && (!expressions.isEmpty())) {
+                || (genericsTypes.length == 1 && OBJECT_TYPE.equals(genericsTypes[0].getType())))) {
             // maybe we can infer the component type
-            List<ClassNode> nodes = new LinkedList<ClassNode>();
+            List<ClassNode> nodes = new ArrayList<>(nExpressions);
             for (Expression expression : expressions) {
                 if (isNullConstant(expression)) {
                     // a null element is found in the list, skip it because we'll use the other elements from the list
@@ -4908,14 +4907,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     nodes.add(getType(expression));
                 }
             }
-            if (nodes.isEmpty()) {
-                // every element was the null constant
-                return listType;
+            if (!nodes.isEmpty()) {
+                ClassNode itemType = lowestUpperBound(nodes);
+
+                listType = listType.getPlainNodeReference();
+                listType.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(itemType))});
             }
-            ClassNode superType = getWrapper(lowestUpperBound(nodes)); // to be used in generics, type must be boxed
-            ClassNode inferred = listType.getPlainNodeReference();
-            inferred.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(superType))});
-            return inferred;
         }
         return listType;
     }
@@ -4927,23 +4924,24 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     protected ClassNode inferMapExpressionType(final MapExpression map) {
         ClassNode mapType = LINKEDHASHMAP_CLASSNODE.getPlainNodeReference();
         List<MapEntryExpression> entryExpressions = map.getMapEntryExpressions();
-        if (entryExpressions.isEmpty()) return mapType;
+        int nExpressions = entryExpressions.size();
+        if (nExpressions == 0) return mapType;
+
         GenericsType[] genericsTypes = mapType.getGenericsTypes();
         if (genericsTypes == null
                 || genericsTypes.length < 2
                 || (genericsTypes.length == 2 && OBJECT_TYPE.equals(genericsTypes[0].getType()) && OBJECT_TYPE.equals(genericsTypes[1].getType()))) {
-            List<ClassNode> keyTypes = new LinkedList<ClassNode>();
-            List<ClassNode> valueTypes = new LinkedList<ClassNode>();
+            List<ClassNode> keyTypes = new ArrayList<>(nExpressions);
+            List<ClassNode> valueTypes = new ArrayList<>(nExpressions);
             for (MapEntryExpression entryExpression : entryExpressions) {
                 keyTypes.add(getType(entryExpression.getKeyExpression()));
                 valueTypes.add(getType(entryExpression.getValueExpression()));
             }
-            ClassNode keyType = getWrapper(lowestUpperBound(keyTypes));  // to be used in generics, type must be boxed
-            ClassNode valueType = getWrapper(lowestUpperBound(valueTypes));  // to be used in generics, type must be boxed
+            ClassNode keyType = lowestUpperBound(keyTypes);
+            ClassNode valueType = lowestUpperBound(valueTypes);
             if (!OBJECT_TYPE.equals(keyType) || !OBJECT_TYPE.equals(valueType)) {
-                ClassNode inferred = mapType.getPlainNodeReference();
-                inferred.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(keyType)), new GenericsType(wrapTypeIfNecessary(valueType))});
-                return inferred;
+                mapType = mapType.getPlainNodeReference();
+                mapType.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(keyType)), new GenericsType(wrapTypeIfNecessary(valueType))});
             }
         }
         return mapType;
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 861a8b1..f2d87e7 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1304,8 +1304,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             bar()
         ''', '[Static type checking] - Cannot find matching method Foo#<init>(java.lang.String, int)'
     }
-    
-    // Groovy-5742
+
+    // GROOVY-5742
     void testNestedGenerics() {
         assertScript '''
             import static Next.*
@@ -1322,6 +1322,18 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-7848
+    void testNestedGenerics2() {
+        assertScript '''
+            List<Integer> test() {
+              def listOfLists = [[1,2], [3,4]]
+              listOfLists.collect { pair -> pair[0] + pair[1] }
+            }
+            def result = test()
+            assert result == [3,7]
+        '''
+    }
+
     void testMethodLevelGenericsFromInterface() {
         assertScript '''
             interface A {
@@ -1337,8 +1349,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             true
         '''
     }
-        
-    // Groovy-5610
+
+    // GROOVY-5610
     void testMethodWithDefaultArgument() {
         assertScript '''
             class A{}