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 2023/01/26 18:07:03 UTC

[groovy] 02/04: GROOVY-6912: STC: support "ArrayList a = [...]" and "HashSet b = [...]"

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

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

commit c7b0d43293e2b36267195a47c323ae1f35b71ddc
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Mar 27 10:41:21 2021 -0500

    GROOVY-6912: STC: support "ArrayList a = [...]" and "HashSet b = [...]"
    
    - ArrayList and super types are the natural types of a list literal
    - LinkedHashSet and super types via castToType/continueCastOnCollection
---
 .../typehandling/DefaultTypeTransformation.java    | 18 ++--
 .../transform/stc/StaticTypeCheckingSupport.java   |  4 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 24 ++++--
 src/spec/test/typing/TypeCheckingTest.groovy       |  2 +-
 .../stc/ArraysAndCollectionsSTCTest.groovy         | 95 ++++++++++++++++++++--
 .../transform/stc/ConstructorsSTCTest.groovy       |  2 +-
 .../stc/DefaultGroovyMethodsSTCTest.groovy         | 15 ++--
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 16 ++--
 8 files changed, 136 insertions(+), 40 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
index aa65f334da..5601d98f3c 100644
--- a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
+++ b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
@@ -244,20 +244,17 @@ public class DefaultTypeTransformation {
     }
 
     private static Object continueCastOnCollection(Object object, Class type) {
-        int modifiers = type.getModifiers();
-        Collection answer;
-        if (object instanceof Collection && type.isAssignableFrom(LinkedHashSet.class) &&
-                (type == LinkedHashSet.class || Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
+        if (object instanceof Collection && type.isAssignableFrom(LinkedHashSet.class)) {
             return new LinkedHashSet((Collection) object);
         }
+
         if (object.getClass().isArray()) {
-            if (type.isAssignableFrom(ArrayList.class) && (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
+            Collection answer;
+            if (type.isAssignableFrom(ArrayList.class) && Modifier.isAbstract(type.getModifiers())) {
                 answer = new ArrayList();
-            } else if (type.isAssignableFrom(LinkedHashSet.class) && (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
+            } else if (type.isAssignableFrom(LinkedHashSet.class) && Modifier.isAbstract(type.getModifiers())) {
                 answer = new LinkedHashSet();
             } else {
-                // let's call the collections constructor
-                // passing in the list wrapper
                 try {
                     answer = (Collection) type.getDeclaredConstructor().newInstance();
                 } catch (Exception e) {
@@ -267,9 +264,8 @@ public class DefaultTypeTransformation {
 
             // we cannot just wrap in a List as we support primitive type arrays
             int length = Array.getLength(object);
-            for (int i = 0; i < length; i++) {
-                Object element = Array.get(object, i);
-                answer.add(element);
+            for (int i = 0; i < length; i += 1) {
+                answer.add(Array.get(object, i));
             }
             return answer;
         }
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 a09d9ede3e..795bf4e5c4 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -163,7 +163,7 @@ public abstract class StaticTypeCheckingSupport {
     protected static final ClassNode ArrayList_TYPE = makeWithoutCaching(ArrayList.class);
     protected static final ClassNode Collection_TYPE = makeWithoutCaching(Collection.class);
     protected static final ClassNode Deprecated_TYPE = makeWithoutCaching(Deprecated.class);
-    protected static final ExtensionMethodCache EXTENSION_METHOD_CACHE = ExtensionMethodCache.INSTANCE;
+    protected static final ClassNode LinkedHashSet_TYPE = makeWithoutCaching(LinkedHashSet.class);
 
     protected static final Map<ClassNode, Integer> NUMBER_TYPES = Maps.of(
             byte_TYPE,    0,
@@ -238,6 +238,8 @@ public abstract class StaticTypeCheckingSupport {
         }
     };
 
+    protected static final ExtensionMethodCache EXTENSION_METHOD_CACHE = ExtensionMethodCache.INSTANCE;
+
     public static void clearExtensionMethodCache() {
         EXTENSION_METHOD_CACHE.cache.clearAll();
     }
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 bfa0849d30..3affb67462 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -236,6 +236,7 @@ import static org.codehaus.groovy.syntax.Types.MOD_EQUAL;
 import static org.codehaus.groovy.syntax.Types.PLUS_PLUS;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.ArrayList_TYPE;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Collection_TYPE;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.LinkedHashSet_TYPE;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Matcher_TYPE;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.NUMBER_OPS;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.UNKNOWN_PARAMETER_TYPE;
@@ -1274,7 +1275,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         // constructor type : Dimension d = [100,200]
         // In that case, more checks can be performed
         if (!implementsInterfaceOrIsSubclassOf(LIST_TYPE, leftRedirect)
-                && (!leftRedirect.isAbstract() || leftRedirect.isArray())) {
+                && (!leftRedirect.isAbstract() || leftRedirect.isArray())
+                && !ArrayList_TYPE.isDerivedFrom(leftRedirect) && !LinkedHashSet_TYPE.isDerivedFrom(leftRedirect)) {
             ClassNode[] types = getArgumentTypes(args(((ListExpression) rightExpression).getExpressions()));
             MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, types, assignmentExpression);
             if (methodNode != null) {
@@ -1321,8 +1323,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private static boolean isConstructorAbbreviation(final ClassNode leftType, final Expression rightExpression) {
-        return (rightExpression instanceof ListExpression && !(leftType.equals(LIST_TYPE) || leftType.equals(SET_TYPE)
-                || leftType.equals(ITERABLE_TYPE) || leftType.equals(Collection_TYPE)));
+        if (rightExpression instanceof ListExpression) {
+            return !(ArrayList_TYPE.isDerivedFrom(leftType) || ArrayList_TYPE.implementsInterface(leftType)
+                    || LinkedHashSet_TYPE.isDerivedFrom(leftType) || LinkedHashSet_TYPE.implementsInterface(leftType));
+        }
+        return false;
     }
 
     protected void typeCheckAssignment(final BinaryExpression assignmentExpression, final Expression leftExpression, final ClassNode leftExpressionType, final Expression rightExpression, final ClassNode rightExpressionType) {
@@ -4514,8 +4519,17 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
             }
 
-            if (rightExpression instanceof ListExpression && leftRedirect.equals(SET_TYPE)) {
-                return GenericsUtils.parameterizeType(right, leftRedirect.getPlainNodeReference());
+            if (rightExpression instanceof ListExpression && !leftRedirect.equals(OBJECT_TYPE)) {
+                if (LIST_TYPE.equals(leftRedirect)
+                        || ITERABLE_TYPE.equals(leftRedirect)
+                        || Collection_TYPE.equals(leftRedirect)
+                        || ArrayList_TYPE.isDerivedFrom(leftRedirect)) { // GROOVY-6912
+                    return GenericsUtils.parameterizeType(right, ArrayList_TYPE.getPlainNodeReference());
+                }
+                if (SET_TYPE.equals(leftRedirect)
+                        || LinkedHashSet_TYPE.isDerivedFrom(leftRedirect)) { // GROOVY-6912
+                    return GenericsUtils.parameterizeType(right, LinkedHashSet_TYPE.getPlainNodeReference());
+                }
             }
 
             return right;
diff --git a/src/spec/test/typing/TypeCheckingTest.groovy b/src/spec/test/typing/TypeCheckingTest.groovy
index 6d2018bca3..1dc45860a6 100644
--- a/src/spec/test/typing/TypeCheckingTest.groovy
+++ b/src/spec/test/typing/TypeCheckingTest.groovy
@@ -686,7 +686,7 @@ import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound
             }
             // end::flowtyping_typeconstraints_failure[]
             flowTypingWithExplicitType()
-        ''', '[Static type checking] - Cannot find matching method java.util.List#add(int)'
+        ''', '[Static type checking] - Cannot find matching method java.util.ArrayList#add(int)'
 
         assertScript '''
             // tag::flowtyping_typeconstraints_fixed[]
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 3d7381db7d..c7a467a19c 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -755,7 +755,64 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
                 A(int n) {}
             }
             A a = [1]
-        ''', 'Cannot assign value of type java.util.List <java.lang.Integer> to variable of type A'
+        ''',
+        'Cannot assign value of type java.util.List <java.lang.Integer> to variable of type A'
+    }
+
+    // GROOVY-6912
+    void testArrayListTypeInitializedByListLiteral() {
+        assertScript '''
+            ArrayList list = [1,2,3]
+            assert list.size() == 3
+            assert list.last() == 3
+        '''
+
+        assertScript '''
+            ArrayList list = [[1,2,3]]
+            assert list.size() == 1
+        '''
+
+        assertScript '''
+            ArrayList<Integer> list = [1,2,3]
+            assert list.size() == 3
+            assert list.last() == 3
+        '''
+
+        shouldFailWithMessages '''
+            ArrayList<String> strings = [1,2,3]
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.ArrayList <java.lang.Integer> to: java.util.ArrayList <String>'
+    }
+
+    // GROOVY-6912
+    void testSetDerivativesInitializedByListLiteral() {
+        assertScript '''
+            LinkedHashSet set = [1,2,3]
+            assert set.size() == 3
+            assert set.contains(3)
+        '''
+
+        assertScript '''
+            HashSet set = [1,2,3]
+            assert set.size() == 3
+            assert set.contains(3)
+        '''
+
+        assertScript '''
+            LinkedHashSet set = [[1,2,3]]
+            assert set.size() == 1
+        '''
+
+        assertScript '''
+            LinkedHashSet<Integer> set = [1,2,3]
+            assert set.size() == 3
+            assert set.contains(3)
+        '''
+
+        shouldFailWithMessages '''
+            LinkedHashSet<String> strings = [1,2,3]
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.LinkedHashSet <java.lang.Integer> to: java.util.LinkedHashSet <String>'
     }
 
     void testCollectionTypesInitializedByListLiteral1() {
@@ -766,6 +823,14 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
             set << 'foo'
             assert set.size() == 2
         '''
+
+        assertScript '''
+            AbstractSet<String> set = []
+            set << 'foo'
+            set << 'bar'
+            set << 'foo'
+            assert set.size() == 2
+        '''
     }
 
     // GROOVY-10002
@@ -775,6 +840,11 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
             assert set.size() == 2
         '''
 
+        assertScript '''
+            AbstractList<String> list = ['foo', 'bar', 'foo']
+            assert list.size() == 3
+        '''
+
         assertScript '''
             ArrayDeque<String> deque = [123] // ArrayDeque(int numElements)
         '''
@@ -784,26 +854,37 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
     void testCollectionTypesInitializedByListLiteral3() {
         shouldFailWithMessages '''
             Set<String> set = [1,2,3]
-        ''', 'Cannot assign java.util.Set <java.lang.Integer> to: java.util.Set <String>'
+        ''',
+        'Cannot assign java.util.LinkedHashSet <java.lang.Integer> to: java.util.Set <String>'
+
+        shouldFailWithMessages '''
+            List<String> list = ['a','b',3]
+        ''',
+        'Cannot assign java.util.ArrayList <java.io.Serializable> to: java.util.List <String>'
 
         shouldFailWithMessages '''
             Iterable<String> iter = [1,2,3]
-        ''', 'Cannot assign java.util.List <java.lang.Integer> to: java.lang.Iterable <String>'
+        ''',
+        'Cannot assign java.util.ArrayList <java.lang.Integer> to: java.lang.Iterable <String>'
 
         shouldFailWithMessages '''
             Collection<String> coll = [1,2,3]
-        ''', 'Cannot assign java.util.List <java.lang.Integer> to: java.util.Collection <String>'
+        ''',
+        'Cannot assign java.util.ArrayList <java.lang.Integer> to: java.util.Collection <String>'
 
         shouldFailWithMessages '''
             Deque<String> deque = [""]
-        ''', 'Cannot assign value of type java.util.List', ' to variable of type java.util.Deque <String>'
+        ''',
+        'Cannot assign value of type java.util.List', ' to variable of type java.util.Deque <String>'
 
         shouldFailWithMessages '''
             Deque<String> deque = []
-        ''', 'Cannot assign value of type java.util.List', ' to variable of type java.util.Deque <String>'
+        ''',
+        'Cannot assign value of type java.util.List', ' to variable of type java.util.Deque <String>'
 
         shouldFailWithMessages '''
             Queue<String> queue = []
-        ''', 'Cannot assign value of type java.util.List', ' to variable of type java.util.Queue <String>'
+        ''',
+        'Cannot assign value of type java.util.List', ' to variable of type java.util.Queue <String>'
     }
 }
diff --git a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
index bc13920cb0..1669a475d8 100644
--- a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
@@ -91,7 +91,7 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
             import java.awt.Dimension
             List args = [100,200]
             Dimension d = args // not supported
-        ''', 'Cannot assign value of type java.util.List <java.lang.Integer> to variable of type java.awt.Dimension'
+        ''', 'Cannot assign value of type java.util.ArrayList <java.lang.Integer> to variable of type java.awt.Dimension'
     }
 
     void testConstructFromMap() {
diff --git a/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy b/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy
index 23e3aef9e0..f9401f7957 100644
--- a/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy
@@ -114,10 +114,10 @@ class DefaultGroovyMethodsSTCTest extends StaticTypeCheckingTestCase {
             numbers.sequence
             numbers.string
         ''',
-        'Cannot call <CS extends java.lang.CharSequence> java.util.List <java.lang.Number>#getSequence() with arguments []',
-        'Cannot call java.util.List <java.lang.Number>#getString() with arguments []',
-        'No such property: sequence for class: java.util.List <java.lang.Number>',
-        'No such property: string for class: java.util.List <java.lang.Number>'
+        'Cannot call <CS extends java.lang.CharSequence> java.util.ArrayList <java.lang.Number>#getSequence() with arguments []',
+        'Cannot call java.util.ArrayList <java.lang.Number>#getString() with arguments []',
+        'No such property: sequence for class: java.util.ArrayList <java.lang.Number>',
+        'No such property: string for class: java.util.ArrayList <java.lang.Number>'
     }
 
     // GROOVY-5584
@@ -172,11 +172,14 @@ class DefaultGroovyMethodsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-7283
-    void testListWithDefaultInfersInt() {
+    void testListWithDefault() {
         assertScript '''
             def list = [].withDefault{ it.longValue() }
+            //                         ^^ int parameter
             list[0] = list[3]
-            assert list[0] == 3 && list[0].class == Long
+
+            assert list[0].class == Long
+            assert list[0] === 3L
         '''
     }
 
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 458fd9e5c0..fa1e949a10 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -59,7 +59,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             List<String> list = []
             list.add(1)
         ''',
-        'Cannot find matching method java.util.List#add(int)'
+        'Cannot find matching method java.util.ArrayList#add(int)'
     }
 
     void testAddOnList2() {
@@ -86,7 +86,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             List<String> list = []
             list << 1
         ''',
-        'Cannot call <T> java.util.List <String>#leftShift(T) with arguments [int]'
+        'Cannot call <T> java.util.ArrayList <String>#leftShift(T) with arguments [int]'
     }
 
     void testAddOnList2UsingLeftShift() {
@@ -1745,7 +1745,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<String> elements = ['a','b', 1]
         ''',
-        'Cannot assign java.util.List <java.io.Serializable> to: java.util.List <String>'
+        'Cannot assign java.util.ArrayList <java.io.Serializable> to: java.util.List <String>'
     }
 
     void testGenericAssignmentWithSubClass() {
@@ -1775,7 +1775,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<? super Number> list = ['string']
         ''',
-        'Cannot assign java.util.List <java.lang.String> to: java.util.List <? super java.lang.Number>'
+        'Cannot assign java.util.ArrayList <java.lang.String> to: java.util.List <? super java.lang.Number>'
     }
 
     // GROOVY-9914, GROOVY-10036
@@ -2662,8 +2662,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             Collection<String> strings = list.findAll { it }
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def dmt = node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
+                assert dmt.declaringClass.implementsInterface(LIST_TYPE)
                 assert dmt !instanceof ExtensionMethodNode
-                assert dmt.declaringClass == LIST_TYPE
                 assert dmt.name == 'addAll'
             })
             boolean r = list.addAll(strings)
@@ -2676,7 +2676,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             Collection<Integer> numbers = (Collection<Integer>) [1,2,3]
             boolean r = list.addAll(numbers)
         ''',
-        'Cannot call java.util.List <java.lang.String>#addAll(java.util.Collection <? extends java.lang.String>) with arguments [java.util.Collection <Integer>]'
+        'Cannot call java.util.ArrayList <java.lang.String>#addAll(java.util.Collection <? extends java.lang.String>) with arguments [java.util.Collection <Integer>]'
     }
 
     // GROOVY-5528
@@ -3059,7 +3059,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             def test() {
                 @ASTTest(phase=INSTRUCTION_SELECTION, value={
                     def type = node.getNodeMetaData(INFERRED_TYPE)
-                    assert type.equals(LIST_TYPE)
+                    assert type.implementsInterface(LIST_TYPE)
                     assert type.genericsTypes.length == 1
                     assert type.genericsTypes[0].type == GSTRING_TYPE
                 })
@@ -3068,7 +3068,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
                 @ASTTest(phase=INSTRUCTION_SELECTION, value={
                     def type = node.getNodeMetaData(INFERRED_TYPE)
-                    assert type.equals(LIST_TYPE)
+                    assert type.implementsInterface(LIST_TYPE)
                     assert type.genericsTypes.length == 1
                     assert type.genericsTypes[0].type == GSTRING_TYPE
                 })