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 22:21:06 UTC

[groovy] branch GROOVY_2_5_X updated (d01d59436e -> 500ebf9708)

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

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


    from d01d59436e Bump version on GROOVY_2_5_X branch
     new b8966bf226 GROOVY-10002: STC: fix checks for list literal assignment
     new 9ec8e12dc6 GROOVY-6912: STC: support "ArrayList a = [...]" and "HashSet b = [...]"
     new e935b5ca92 GROOVY-7128: STC: support "List<Number> list = [1,2,3]"
     new 500ebf9708 GROOVY-6802, GROOVY-6803, GROOVY-8136: STC: more list/map assign checks

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../typehandling/DefaultTypeTransformation.java    |  18 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 191 ++++++++++++---------
 src/spec/test/typing/TypeCheckingTest.groovy       |   2 +-
 .../stc/ArraysAndCollectionsSTCTest.groovy         | 171 +++++++++++++++++-
 .../groovy/transform/stc/CoercionSTCTest.groovy    |  88 +++++++++-
 .../transform/stc/ConstructorsSTCTest.groovy       |   6 +-
 .../stc/DefaultGroovyMethodsSTCTest.groovy         |  17 +-
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  58 +++----
 ...st.groovy => CoercionStaticCompileTests.groovy} |   8 +-
 .../InheritConstructorsTransformTest.groovy        | 119 ++++++-------
 10 files changed, 481 insertions(+), 197 deletions(-)
 copy src/test/org/codehaus/groovy/classgen/asm/sc/{WithStaticCompileTest.groovy => CoercionStaticCompileTests.groovy} (81%)


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

Posted by em...@apache.org.
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 9ec8e12dc6a8378a052b832bf7327dae9e471f3c
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
    
    2_5_X backport
---
 .../typehandling/DefaultTypeTransformation.java    | 18 ++---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 39 +++++++----
 src/spec/test/typing/TypeCheckingTest.groovy       |  2 +-
 .../stc/ArraysAndCollectionsSTCTest.groovy         | 80 +++++++++++++++++++++-
 .../transform/stc/ConstructorsSTCTest.groovy       |  2 +-
 .../stc/DefaultGroovyMethodsSTCTest.groovy         | 15 ++--
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 58 ++++++++--------
 7 files changed, 150 insertions(+), 64 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 81626c1aca..28d3a14c90 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/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index b4f21129b5..8132fa1d2c 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -293,9 +293,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     protected static final MethodNode GET_THISOBJECT = CLOSURE_TYPE.getGetterMethod("getThisObject");
     protected static final ClassNode DELEGATES_TO = ClassHelper.make(DelegatesTo.class);
     protected static final ClassNode DELEGATES_TO_TARGET = ClassHelper.make(DelegatesTo.Target.class);
-    protected static final ClassNode LINKEDHASHMAP_CLASSNODE = ClassHelper.make(LinkedHashMap.class);
     protected static final ClassNode CLOSUREPARAMS_CLASSNODE = ClassHelper.make(ClosureParams.class);
     protected static final ClassNode NAMED_PARAMS_CLASSNODE = ClassHelper.make(NamedParams.class);
+    private   static final ClassNode LinkedHashSet_TYPE = ClassHelper.make(LinkedHashSet.class);
+    private   static final ClassNode LinkedHashMap_TYPE = ClassHelper.make(LinkedHashMap.class);
+    protected static final ClassNode LINKEDHASHMAP_CLASSNODE = LinkedHashMap_TYPE; //@Deprecated
     protected static final ClassNode ENUMERATION_TYPE = ClassHelper.make(Enumeration.class);
     protected static final ClassNode MAP_ENTRY_TYPE = ClassHelper.make(Map.Entry.class);
     protected static final ClassNode ITERABLE_TYPE = ClassHelper.make(Iterable.class);
@@ -1264,7 +1266,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[] args = getArgumentTypes(args(((ListExpression) rightExpression).getExpressions()));
             MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, args, assignmentExpression);
             if (methodNode != null) {
@@ -1311,8 +1314,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) {
@@ -1423,12 +1429,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
         List<MethodNode> constructorList = findMethod(node, "<init>", arguments);
         if (constructorList.isEmpty()) {
-            if (isBeingCompiled(node) && arguments.length == 1 && LINKEDHASHMAP_CLASSNODE.equals(arguments[0])) {
-                // there will be a default hash map constructor added later
-                ConstructorNode cn = new ConstructorNode(Opcodes.ACC_PUBLIC, new Parameter[]{
-                        new Parameter(LINKEDHASHMAP_CLASSNODE, "args")
-                }, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
-                return cn;
+            if (isBeingCompiled(node) && arguments.length == 1 && LinkedHashMap_TYPE.equals(arguments[0])) {
+                // there will be a default map constructor added later
+                Parameter[] params = {new Parameter(LinkedHashMap_TYPE, "args")};
+                return new ConstructorNode(Opcodes.ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
             } else {
                 addStaticTypeError("No matching constructor found: " + node + toMethodParametersString("<init>", arguments), source);
                 return null;
@@ -4420,8 +4424,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;
@@ -5201,7 +5214,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     protected ClassNode inferMapExpressionType(final MapExpression map) {
-        ClassNode mapType = LINKEDHASHMAP_CLASSNODE.getPlainNodeReference();
+        ClassNode mapType = LinkedHashMap_TYPE.getPlainNodeReference();
         List<MapEntryExpression> entryExpressions = map.getMapEntryExpressions();
         int nExpressions = entryExpressions.size();
         if (nExpressions == 0) return mapType;
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 ac76263eb3..c20dc9bb9c 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -650,6 +650,62 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
         '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() {
         assertScript '''
             Set<String> set = []
@@ -658,6 +714,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
@@ -667,6 +731,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)
         '''
@@ -677,17 +746,22 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
         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 = [""]
diff --git a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
index 285b312cb8..dacfebd309 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 3e271b0ee7..e1b2746e24 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
@@ -171,11 +171,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 f00d829e74..a15cdcd5e8 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() {
@@ -896,7 +896,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-9984
-    @NotYetImplemented
     void testDiamondInferenceFromConstructor7a() {
         assertScript '''
             @groovy.transform.TupleConstructor(defaults=false)
@@ -905,7 +904,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             }
 
             C<Integer> c = new C<>(null)
-            assert c.p === null
+            assert c.p == null
         '''
     }
 
@@ -1716,7 +1715,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() {
@@ -1746,7 +1745,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>'
     }
 
     void testGroovy5154() {
@@ -2532,9 +2531,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             Collection<String> e = list.findAll { it }
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def dmt = node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
-                assert dmt instanceof ExtensionMethodNode == false
+                assert dmt.declaringClass.implementsInterface(LIST_TYPE)
+                assert !(dmt instanceof ExtensionMethodNode)
                 assert dmt.name == 'addAll'
-                assert dmt.declaringClass == make(List)
             })
             boolean r = list.addAll(e)
         '''
@@ -2546,7 +2545,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             Collection<Integer> e = (Collection<Integer>) [1,2,3]
             boolean r = list.addAll(e)
         ''',
-        '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
@@ -2893,26 +2892,27 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-5617
     void testIntermediateListAssignmentOfGStrings() {
         assertScript '''
-        def test() {
-            @ASTTest(phase=INSTRUCTION_SELECTION, value={
-                def type = node.getNodeMetaData(INFERRED_TYPE)
-                assert type == make(List)
-                assert type.genericsTypes.length==1
-                assert type.genericsTypes[0].type == GSTRING_TYPE
-            })
-            List<GString> dates = ["${new Date()-1}", "${new Date()}", "${new Date()+1}"]
-            dates*.toUpperCase()
-            @ASTTest(phase=INSTRUCTION_SELECTION, value={
-                def type = node.getNodeMetaData(INFERRED_TYPE)
-                assert type == make(List)
-                assert type.genericsTypes.length==1
-                assert type.genericsTypes[0].type == GSTRING_TYPE
-            })
-            List<GString> copied = []
-            copied.addAll(dates)
-            List<String> upper = copied*.toUpperCase()
-        }
-        test()
+            def test() {
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    def type = node.getNodeMetaData(INFERRED_TYPE)
+                    assert type.implementsInterface(LIST_TYPE)
+                    assert type.genericsTypes.length == 1
+                    assert type.genericsTypes[0].type == GSTRING_TYPE
+                })
+                List<GString> dates = ["${new Date()-1}", "${new Date()}", "${new Date()+1}"]
+                dates*.toUpperCase()
+
+                @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                    def type = node.getNodeMetaData(INFERRED_TYPE)
+                    assert type.implementsInterface(LIST_TYPE)
+                    assert type.genericsTypes.length == 1
+                    assert type.genericsTypes[0].type == GSTRING_TYPE
+                })
+                List<GString> copied = []
+                copied.addAll(dates)
+                List<String> upper = copied*.toUpperCase()
+            }
+            test()
         '''
     }
 


[groovy] 03/04: GROOVY-7128: STC: support "List list = [1,2,3]"

Posted by em...@apache.org.
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 e935b5ca9211cad8f9445e75c444fdb1809a762e
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Jan 26 15:12:28 2023 -0600

    GROOVY-7128: STC: support "List<Number> list = [1,2,3]"
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 27 +++++++++++++++++++---
 .../stc/ArraysAndCollectionsSTCTest.groovy         | 21 +++++++++++++++++
 .../stc/DefaultGroovyMethodsSTCTest.groovy         |  2 +-
 3 files changed, 46 insertions(+), 4 deletions(-)

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 8132fa1d2c..29f1a86174 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -4429,11 +4429,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         || ITERABLE_TYPE.equals(leftRedirect)
                         || Collection_TYPE.equals(leftRedirect)
                         || ArrayList_TYPE.isDerivedFrom(leftRedirect)) { // GROOVY-6912
-                    return GenericsUtils.parameterizeType(right, ArrayList_TYPE.getPlainNodeReference());
+                    return getLiteralResultType(left, right, ArrayList_TYPE); // GROOVY-7128
                 }
                 if (SET_TYPE.equals(leftRedirect)
                         || LinkedHashSet_TYPE.isDerivedFrom(leftRedirect)) { // GROOVY-6912
-                    return GenericsUtils.parameterizeType(right, LinkedHashSet_TYPE.getPlainNodeReference());
+                    return getLiteralResultType(left, right, LinkedHashSet_TYPE); // GROOVY-7128
                 }
             }
 
@@ -4494,7 +4494,28 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return null;
     }
 
-    private static ClassNode getMathResultType(int op, ClassNode leftRedirect, ClassNode rightRedirect, String operationName) {
+    /**
+     * For "{@code List<Type> x = [...]}" or "{@code Set<Type> y = [...]}", etc.
+     * the literal may be composed of sub-types of {@code Type}. In these cases,
+     * {@code ArrayList<Type>} is an appropriate result type for the expression.
+     */
+    private static ClassNode getLiteralResultType(final ClassNode targetType, final ClassNode sourceType, final ClassNode baseType) {
+        ClassNode resultType = sourceType.equals(baseType) ? sourceType
+                : GenericsUtils.parameterizeType(sourceType, baseType.getPlainNodeReference());
+
+        if (targetType.getGenericsTypes() != null
+                && !GenericsUtils.buildWildcardType(targetType).isCompatibleWith(resultType)) {
+            GenericsType[] lgt = targetType.getGenericsTypes(), rgt = resultType.getGenericsTypes();
+            if (!lgt[0].isPlaceholder() && !lgt[0].isWildcard() && GenericsUtils.buildWildcardType(
+                    getCombinedBoundType(lgt[0])).isCompatibleWith(getCombinedBoundType(rgt[0]))) {
+                resultType = GenericsUtils.parameterizeType(targetType, baseType.getPlainNodeReference());
+            }
+        }
+
+        return resultType;
+    }
+
+    private static ClassNode getMathResultType(final int op, final ClassNode leftRedirect, final ClassNode rightRedirect, final String operationName) {
         if (isNumberType(leftRedirect) && isNumberType(rightRedirect)) {
             if (isOperationInGroup(op)) {
                 if (isIntCategory(leftRedirect) && isIntCategory(rightRedirect)) return int_TYPE;
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index c20dc9bb9c..608fa048b1 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -778,4 +778,25 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
         ''',
         'Cannot assign value of type java.util.List', ' to variable of type java.util.Queue <String>'
     }
+
+    // GROOVY-7128
+    void testCollectionTypesInitializedByListLiteral4() {
+        assertScript '''
+            Collection<Number> collection = [1,2,3]
+            assert collection.size() == 3
+            assert collection.last() == 3
+        '''
+
+        assertScript '''
+            List<Number> list = [1,2,3]
+            assert list.size() == 3
+            assert list.last() == 3
+        '''
+
+        assertScript '''
+            Set<Number> set = [1,2,3,3]
+            assert set.size() == 3
+            assert set.last() == 3
+        '''
+    }
 }
diff --git a/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy b/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy
index e1b2746e24..44b45f4370 100644
--- a/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/DefaultGroovyMethodsSTCTest.groovy
@@ -108,7 +108,7 @@ class DefaultGroovyMethodsSTCTest extends StaticTypeCheckingTestCase {
         '''
 
         shouldFailWithMessages '''
-            List<Number> numbers = [(Number)1, 2, 3]
+            List<Number> numbers = [1, 2, 3]
             numbers.getSequence()
             numbers.getString()
             numbers.sequence


[groovy] 01/04: GROOVY-10002: STC: fix checks for list literal assignment

Posted by em...@apache.org.
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 b8966bf22677a2eb963f6aedfcd0f33dbef9df43
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Mar 27 00:29:54 2021 -0500

    GROOVY-10002: STC: fix checks for list literal assignment
    
    - empty and non-empty list literals can initialize Set variables
    - list literal generics checking skipped when constructor may be used
    - list literal as constructor arguments does not apply to abstract types
    
    2_5_X backport
---
 .../transform/stc/StaticTypeCheckingVisitor.java   |  83 +++++++-------
 .../stc/ArraysAndCollectionsSTCTest.groovy         |  67 +++++++++++-
 .../InheritConstructorsTransformTest.groovy        | 119 +++++++++++----------
 3 files changed, 168 insertions(+), 101 deletions(-)

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 86fecfbf20..b4f21129b5 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -205,7 +205,6 @@ import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.init;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
 import static org.codehaus.groovy.syntax.Types.ASSIGN;
-import static org.codehaus.groovy.syntax.Types.ASSIGNMENT_OPERATOR;
 import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
 import static org.codehaus.groovy.syntax.Types.COMPARE_IDENTICAL;
 import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
@@ -282,7 +281,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     private static final AtomicLong UNIQUE_LONG = new AtomicLong();
 
     protected static final Object ERROR_COLLECTOR = ErrorCollector.class;
-    protected static final ClassNode ITERABLE_TYPE = ClassHelper.make(Iterable.class);
     protected static final List<MethodNode> EMPTY_METHODNODE_LIST = Collections.emptyList();
     protected static final ClassNode TYPECHECKED_CLASSNODE = ClassHelper.make(TypeChecked.class);
     protected static final ClassNode[] TYPECHECKING_ANNOTATIONS = new ClassNode[]{TYPECHECKED_CLASSNODE};
@@ -298,8 +296,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     protected static final ClassNode LINKEDHASHMAP_CLASSNODE = ClassHelper.make(LinkedHashMap.class);
     protected static final ClassNode CLOSUREPARAMS_CLASSNODE = ClassHelper.make(ClosureParams.class);
     protected static final ClassNode NAMED_PARAMS_CLASSNODE = ClassHelper.make(NamedParams.class);
-    protected static final ClassNode MAP_ENTRY_TYPE = ClassHelper.make(Map.Entry.class);
     protected static final ClassNode ENUMERATION_TYPE = ClassHelper.make(Enumeration.class);
+    protected static final ClassNode MAP_ENTRY_TYPE = ClassHelper.make(Map.Entry.class);
+    protected static final ClassNode ITERABLE_TYPE = ClassHelper.make(Iterable.class);
+    private   static final ClassNode SET_TYPE = ClassHelper.make(Set.class);
 
     public static final Statement GENERATED_EMPTY_STATEMENT = new EmptyStatement();
 
@@ -1259,16 +1259,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private void addListAssignmentConstructorErrors(
-            ClassNode leftRedirect, ClassNode leftExpressionType,
-            ClassNode inferredRightExpressionType, Expression rightExpression,
-            Expression assignmentExpression) {
+    private void addListAssignmentConstructorErrors(final ClassNode leftRedirect, final ClassNode leftExpressionType, final ClassNode inferredRightExpressionType, final Expression rightExpression, final Expression assignmentExpression) {
         // if left type is not a list but right type is a list, then we're in the case of a groovy
         // constructor type : Dimension d = [100,200]
         // In that case, more checks can be performed
-        if (rightExpression instanceof ListExpression && !implementsInterfaceOrIsSubclassOf(LIST_TYPE, leftRedirect)) {
-            ArgumentListExpression argList = args(((ListExpression) rightExpression).getExpressions());
-            ClassNode[] args = getArgumentTypes(argList);
+        if (!implementsInterfaceOrIsSubclassOf(LIST_TYPE, leftRedirect)
+                && (!leftRedirect.isAbstract() || leftRedirect.isArray())) {
+            ClassNode[] args = getArgumentTypes(args(((ListExpression) rightExpression).getExpressions()));
             MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, args, assignmentExpression);
             if (methodNode != null) {
                 rightExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, methodNode);
@@ -1283,7 +1280,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private void addMapAssignmentConstructorErrors(final ClassNode leftRedirect, final Expression leftExpression, final Expression rightExpression) {
-        if (!(rightExpression instanceof MapExpression) || (leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isDynamicTyped())
+        if ((leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isDynamicTyped())
                 || leftRedirect.equals(OBJECT_TYPE) || implementsInterfaceOrIsSubclassOf(leftRedirect, MAP_TYPE)) {
             return;
         }
@@ -1313,6 +1310,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return false;
     }
 
+    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)));
+    }
+
     protected void typeCheckAssignment(final BinaryExpression assignmentExpression, final Expression leftExpression, final ClassNode leftExpressionType, final Expression rightExpression, final ClassNode rightExpressionType) {
         if (!typeCheckMultipleAssignmentAndContinue(leftExpression, rightExpression)) return;
 
@@ -1334,9 +1336,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         } else {
             ClassNode lTypeRedirect = leftExpressionType.redirect();
             addPrecisionErrors(lTypeRedirect, leftExpressionType, rTypeAdjusted, rightExpression);
-            addListAssignmentConstructorErrors(lTypeRedirect, leftExpressionType, rTypeInferred, rightExpression, assignmentExpression);
-            addMapAssignmentConstructorErrors(lTypeRedirect, leftExpression, rightExpression);
-            if (!hasGStringStringError(leftExpressionType, rTypeAdjusted, rightExpression)) {
+            if (rightExpression instanceof ListExpression) {
+                addListAssignmentConstructorErrors(lTypeRedirect, leftExpressionType, rTypeInferred, rightExpression, assignmentExpression);
+            } else if (rightExpression instanceof MapExpression) {
+                addMapAssignmentConstructorErrors(lTypeRedirect, leftExpression, rightExpression);
+            }
+            if (!hasGStringStringError(leftExpressionType, rTypeAdjusted, rightExpression)
+                    && !isConstructorAbbreviation(leftExpressionType, rightExpression)) {
                 checkTypeGenerics(leftExpressionType, rTypeAdjusted, rightExpression);
             }
         }
@@ -4378,32 +4384,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    protected ClassNode getResultType(ClassNode left, int op, ClassNode right, BinaryExpression expr) {
+    protected ClassNode getResultType(ClassNode left, final int op, final ClassNode right, final BinaryExpression expr) {
         ClassNode leftRedirect = left.redirect();
         ClassNode rightRedirect = right.redirect();
 
         Expression leftExpression = expr.getLeftExpression();
         Expression rightExpression = expr.getRightExpression();
 
-        if (op == ASSIGN || op == ASSIGNMENT_OPERATOR) {
-            if (leftRedirect.isArray() && implementsInterfaceOrIsSubclassOf(rightRedirect, Collection_TYPE))
-                return leftRedirect;
-            if (leftRedirect.implementsInterface(Collection_TYPE) && rightRedirect.implementsInterface(Collection_TYPE)) {
-                // because of type inferrence, we must perform an additional check if the right expression
-                // is an empty list expression ([]). In that case and only in that case, the inferred type
-                // will be wrong, so we will prefer the left type
-                if (rightExpression instanceof ListExpression) {
-                    List<Expression> list = ((ListExpression) rightExpression).getExpressions();
-                    if (list.isEmpty()) return left;
-                }
-                return right;
-            }
-            if (rightRedirect.implementsInterface(Collection_TYPE) && rightRedirect.isDerivedFrom(leftRedirect)) {
-                // ex : def foos = ['a','b','c']
-                return right;
+        if (op == ASSIGN) {
+            if (leftRedirect.isArray() && rightRedirect.implementsInterface(Collection_TYPE)) {
+                return left;
             }
-            if (rightExpression instanceof ClosureExpression && rightRedirect.isDerivedFrom(CLOSURE_TYPE) && isSAMType(leftRedirect)) {
-                return inferSAMTypeGenericsInAssignment(left, findSAM(left), right, (ClosureExpression) rightExpression);
+
+            if (rightExpression instanceof ClosureExpression && rightRedirect.isDerivedFrom(CLOSURE_TYPE)) {
+                MethodNode abstractMethod = findSAM(left);
+                if (abstractMethod != null) {
+                    return inferSAMTypeGenericsInAssignment(left, abstractMethod, right, (ClosureExpression) rightExpression);
+                }
             }
 
             if (leftExpression instanceof VariableExpression) {
@@ -4418,23 +4415,27 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
 
                 // as anything can be assigned to a String, Class or [Bb]oolean, return the left type instead
-                if (STRING_TYPE.equals(initialType)
-                        || CLASS_Type.equals(initialType)
-                        || Boolean_TYPE.equals(initialType)
-                        || boolean_TYPE.equals(initialType)) {
+                if (isWildcardLeftHandSide(initialType) && !initialType.equals(OBJECT_TYPE)) {
                     return initialType;
                 }
             }
+
+            if (rightExpression instanceof ListExpression && leftRedirect.equals(SET_TYPE)) {
+                return GenericsUtils.parameterizeType(right, leftRedirect.getPlainNodeReference());
+            }
+
             return right;
         }
-        if (isBoolIntrinsicOp(op)) {
-            return boolean_TYPE;
-        }
+
         if (op == FIND_REGEX) {
             // this case always succeeds the result is a Matcher
             return Matcher_TYPE;
         }
 
+        if (isBoolIntrinsicOp(op)) {
+            return boolean_TYPE;
+        }
+
         if (isArrayOp(op)) {
             // using getPNR() to ignore generics at this point
             // and a different binary expression not to pollute the AST
@@ -4480,7 +4481,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return null;
     }
 
-    private ClassNode getMathResultType(int op, ClassNode leftRedirect, ClassNode rightRedirect, String operationName) {
+    private static ClassNode getMathResultType(int op, ClassNode leftRedirect, ClassNode rightRedirect, String operationName) {
         if (isNumberType(leftRedirect) && isNumberType(rightRedirect)) {
             if (isOperationInGroup(op)) {
                 if (isIntCategory(leftRedirect) && isIntCategory(rightRedirect)) return int_TYPE;
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 95264a33ac..ac76263eb3 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -208,7 +208,6 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     void testInlineMap() {
-
         assertScript '''
             Map map = [a:1, b:2]
         '''
@@ -324,7 +323,6 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
                 }
             }
             class FooAnother {
-
             }
         ''',
         'Cannot assign value of type Foo[] to variable of type FooAnother'
@@ -641,4 +639,69 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
             assert meth().toSet() == ['pls', 'bar'].toSet()
         '''
     }
+
+    void testAbstractTypeInitializedByListLiteral() {
+        shouldFailWithMessages '''
+            abstract class A {
+                A(int n) {}
+            }
+            A a = [1]
+        ''',
+        'Cannot assign value of type java.util.List <java.lang.Integer> to variable of type A'
+    }
+
+    void testCollectionTypesInitializedByListLiteral1() {
+        assertScript '''
+            Set<String> set = []
+            set << 'foo'
+            set << 'bar'
+            set << 'foo'
+            assert set.size() == 2
+        '''
+    }
+
+    // GROOVY-10002
+    void testCollectionTypesInitializedByListLiteral2() {
+        assertScript '''
+            Set<String> set = ['foo', 'bar', 'foo']
+            assert set.size() == 2
+        '''
+
+        assertScript '''
+            ArrayDeque<String> deque = [123] // ArrayDeque(int numElements)
+        '''
+    }
+
+    // GROOVY-10002
+    void testCollectionTypesInitializedByListLiteral3() {
+        shouldFailWithMessages '''
+            Set<String> set = [1,2,3]
+        ''',
+        'Cannot assign java.util.Set <java.lang.Integer> to: java.util.Set <String>'
+
+        shouldFailWithMessages '''
+            Iterable<String> iter = [1,2,3]
+        ''',
+        'Cannot assign java.util.List <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>'
+
+        shouldFailWithMessages '''
+            Deque<String> deque = [""]
+        ''',
+        '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>'
+
+        shouldFailWithMessages '''
+            Queue<String> queue = []
+        ''',
+        'Cannot assign value of type java.util.List', ' to variable of type java.util.Queue <String>'
+    }
 }
diff --git a/src/test/org/codehaus/groovy/transform/InheritConstructorsTransformTest.groovy b/src/test/org/codehaus/groovy/transform/InheritConstructorsTransformTest.groovy
index b96be1db94..54a3841805 100644
--- a/src/test/org/codehaus/groovy/transform/InheritConstructorsTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/InheritConstructorsTransformTest.groovy
@@ -21,16 +21,16 @@ package org.codehaus.groovy.transform
 class InheritConstructorsTransformTest extends GroovyShellTestCase {
 
     void testStandardCase() {
-        assertScript """
+        assertScript '''
             import groovy.transform.InheritConstructors
             @InheritConstructors class CustomException extends RuntimeException { }
             def ce = new CustomException('foo')
             assert ce.message == 'foo'
-        """
+        '''
     }
 
     void testOverrideCase() {
-        assertScript """
+        assertScript '''
             import groovy.transform.InheritConstructors
             @InheritConstructors
             class CustomException2 extends RuntimeException {
@@ -40,11 +40,11 @@ class InheritConstructorsTransformTest extends GroovyShellTestCase {
             assert ce.message == 'bar'
             ce = new CustomException2('foo')
             assert ce.message == 'foo'
-        """
+        '''
     }
 
     void testChainedCase() {
-        assertScript """
+        assertScript '''
             import groovy.transform.InheritConstructors
             @InheritConstructors
             class CustomException5 extends CustomException4 {}
@@ -54,11 +54,12 @@ class InheritConstructorsTransformTest extends GroovyShellTestCase {
             class CustomException4 extends CustomException3 {}
             def ce = new CustomException5('baz')
             assert ce.message == 'baz'
-        """
+        '''
     }
 
-    void testCopyAnnotations_groovy7059() {
-        assertScript """
+    // GROOVY-7059
+    void testCopyAnnotations() {
+        assertScript '''
             import java.lang.annotation.*
             import groovy.transform.InheritConstructors
 
@@ -107,11 +108,11 @@ class InheritConstructorsTransformTest extends GroovyShellTestCase {
                         break
                 }
             }
-        """
+        '''
     }
 
     void testInnerClassUsage() {
-        assertScript """
+        assertScript '''
             import groovy.transform.InheritConstructors
             @InheritConstructors
             class Outer extends RuntimeException {
@@ -136,88 +137,90 @@ class InheritConstructorsTransformTest extends GroovyShellTestCase {
             assert o.message == 'baz'
             o.test()
             new Outer2().test()
-        """
+        '''
     }
 
-    void testParametersWithGenericsAndCompileStatic_GROOVY6874() {
-        assertScript """
+    // GROOVY-6874
+    void testParametersWithGenericsAndCompileStatic() {
+        assertScript '''
             import groovy.transform.*
             import java.math.RoundingMode
 
             @CompileStatic
             abstract class BasePublisher<T, U> {
-               final Deque<T> items
-               private U mode
-               BasePublisher(Deque<T> items) { this.items = items }
-               BasePublisher(U mode) {
-                  this.mode = mode
-                  this.items = []
-               }
-               BasePublisher(Set<U> modes) { this(modes[0]) }
-               void publish(T item) { items.addFirst(item) }
-               void init(U mode) { this.mode = mode }
-               String toString() { items.join('|') + "|" + mode.toString() }
+                final Deque<T> items
+                private U mode
+                BasePublisher(Deque<T> items) { this.items = items }
+                BasePublisher(U mode) {
+                    this.mode = mode
+                    this.items = new ArrayDeque<>()
+                }
+                BasePublisher(Set<U> modes) { this(modes[0]) }
+                void publish(T item) { items.addFirst(item) }
+                void init(U mode) { this.mode = mode }
+                String toString() { items.join('|') + "|" + mode.toString() }
             }
 
             @CompileStatic @InheritConstructors
             class OrderPublisher<V> extends BasePublisher<Integer, V> {
-              static OrderPublisher make() {
-                new OrderPublisher<RoundingMode>(new LinkedList<Integer>())
-              }
-              void foo() { publish(3) }
-              void bar(V mode) { init(mode) }
-              void baz() {
-                new OrderPublisher<RoundingMode>(RoundingMode.UP)
-                new OrderPublisher<RoundingMode>(new HashSet<RoundingMode>())
-              }
+                static OrderPublisher make() {
+                    new OrderPublisher<RoundingMode>(new LinkedList<Integer>())
+                }
+                void foo() { publish(3) }
+                void bar(V mode) { init(mode) }
+                void baz() {
+                    new OrderPublisher<RoundingMode>(RoundingMode.UP)
+                    new OrderPublisher<RoundingMode>(new HashSet<RoundingMode>())
+                }
             }
 
             def op = OrderPublisher.make()
             op.foo()
             op.bar(RoundingMode.DOWN)
+            op.baz()
             assert op.toString() == '3|DOWN'
-        """
+        '''
     }
 
-    void testParametersWithGenericsAndCompileStatic_errors_GROOVY6874() {
-        def message = shouldFail """
+    // GROOVY-6874
+    void testParametersWithGenericsAndCompileStatic_errors() {
+        def message = shouldFail '''
             import groovy.transform.*
             import java.math.RoundingMode
 
             @CompileStatic
             abstract class BasePublisher<T, U> {
-               final Deque<T> items
-               private U mode
-               BasePublisher(Deque<T> items) { this.items = items }
-               BasePublisher(U mode) {
-                  this.mode = mode
-                  this.items = []
-               }
-               BasePublisher(Set<U> modes) { this(modes[0]) }
-               void publish(T item) { items.addFirst(item) }
-               void init(U mode) { this.mode = mode }
-               String toString() { items.join('|') + "|" + mode.toString() }
+                final Deque<T> items
+                private U mode
+                BasePublisher(Deque<T> items) { this.items = items }
+                BasePublisher(U mode) {
+                    this.mode = mode
+                    this.items = new ArrayDeque<>()
+                }
+                BasePublisher(Set<U> modes) { this(modes[0]) }
+                void publish(T item) { items.addFirst(item) }
+                void init(U mode) { this.mode = mode }
+                String toString() { items.join('|') + "|" + mode.toString() }
             }
 
             @CompileStatic @InheritConstructors
             class OrderPublisher<V> extends BasePublisher<Integer, V> {
-              static OrderPublisher make() {
-                new OrderPublisher<RoundingMode>(new LinkedList<String>())
-              }
-              void foo() { publish(3) }
-              void bar(V mode) { init(mode) }
-              void baz() {
-                new OrderPublisher<RoundingMode>(new Date())
-                new OrderPublisher<RoundingMode>(new HashSet<Date>())
-              }
+                static OrderPublisher make() {
+                    new OrderPublisher<RoundingMode>(new LinkedList<String>())
+                }
+                void foo() { publish(3) }
+                void bar(V mode) { init(mode) }
+                void baz() {
+                    new OrderPublisher<RoundingMode>(new Date())
+                    new OrderPublisher<RoundingMode>(new HashSet<Date>())
+                }
             }
 
             def op = OrderPublisher.make()
             op.foo()
             op.bar(RoundingMode.DOWN)
             assert op.toString() == '3|DOWN'
-        """
-        assert message.contains('Cannot call OrderPublisher <RoundingMode>#<init>(java.util.Deque <java.lang.Integer>) with arguments [java.util.LinkedList <String>]')
+        '''
         assert message.contains('Cannot find matching method OrderPublisher#<init>(java.util.Date)')
         assert message.contains('Cannot call OrderPublisher <RoundingMode>#<init>(java.util.Set <RoundingMode>) with arguments [java.util.HashSet <Date>]')
     }


[groovy] 04/04: GROOVY-6802, GROOVY-6803, GROOVY-8136: STC: more list/map assign checks

Posted by em...@apache.org.
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 500ebf9708381a5b697fa6fa8005ea272f6645fd
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Jan 26 15:46:23 2023 -0600

    GROOVY-6802, GROOVY-6803, GROOVY-8136: STC: more list/map assign checks
    
    2_5_X backport
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 68 +++++++++--------
 .../stc/ArraysAndCollectionsSTCTest.groovy         |  9 +++
 .../groovy/transform/stc/CoercionSTCTest.groovy    | 88 +++++++++++++++++++++-
 .../transform/stc/ConstructorsSTCTest.groovy       |  4 +-
 .../asm/sc/CoercionStaticCompileTests.groovy       | 27 +++++++
 5 files changed, 157 insertions(+), 39 deletions(-)

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 29f1a86174..e560554a7a 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1262,38 +1262,38 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private void addListAssignmentConstructorErrors(final ClassNode leftRedirect, final ClassNode leftExpressionType, final ClassNode inferredRightExpressionType, final Expression rightExpression, final Expression assignmentExpression) {
+        if (isWildcardLeftHandSide(leftRedirect) && !leftRedirect.equals(CLASS_Type)) return; // GROOVY-6802, GROOVY-6803
         // if left type is not a list but right type is a list, then we're in the case of a groovy
         // constructor type : Dimension d = [100,200]
         // In that case, more checks can be performed
         if (!implementsInterfaceOrIsSubclassOf(LIST_TYPE, leftRedirect)
                 && (!leftRedirect.isAbstract() || leftRedirect.isArray())
                 && !ArrayList_TYPE.isDerivedFrom(leftRedirect) && !LinkedHashSet_TYPE.isDerivedFrom(leftRedirect)) {
-            ClassNode[] args = getArgumentTypes(args(((ListExpression) rightExpression).getExpressions()));
-            MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, args, assignmentExpression);
+            ClassNode[] types = getArgumentTypes(args(((ListExpression)rightExpression).getExpressions()));
+            MethodNode methodNode = checkGroovyStyleConstructor(leftRedirect, types, assignmentExpression);
             if (methodNode != null) {
                 rightExpression.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, methodNode);
             }
-        } else if (!implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, leftRedirect)
-                && implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, LIST_TYPE)
-                && !isWildcardLeftHandSide(leftExpressionType)) {
+        } else if (implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, LIST_TYPE)
+                && !implementsInterfaceOrIsSubclassOf(inferredRightExpressionType, leftRedirect)) {
             if (!extension.handleIncompatibleAssignment(leftExpressionType, inferredRightExpressionType, assignmentExpression)) {
                 addAssignmentError(leftExpressionType, inferredRightExpressionType, assignmentExpression);
             }
         }
     }
 
-    private void addMapAssignmentConstructorErrors(final ClassNode leftRedirect, final Expression leftExpression, final Expression rightExpression) {
-        if ((leftExpression instanceof VariableExpression && ((VariableExpression) leftExpression).isDynamicTyped())
-                || leftRedirect.equals(OBJECT_TYPE) || implementsInterfaceOrIsSubclassOf(leftRedirect, MAP_TYPE)) {
+    private void addMapAssignmentConstructorErrors(final ClassNode leftRedirect, final Expression leftExpression, final MapExpression rightExpression) {
+        if (!isConstructorAbbreviation(leftRedirect, rightExpression)
+                // GROOVY-6802, GROOVY-6803: Object, String or [Bb]oolean target
+                || (isWildcardLeftHandSide(leftRedirect) && !leftRedirect.equals(CLASS_Type))) {
             return;
         }
 
         // groovy constructor shorthand: A a = [x:2, y:3]
-        ClassNode[] argTypes = getArgumentTypes(args(rightExpression));
+        ClassNode[] argTypes = {getType(rightExpression)};
         checkGroovyStyleConstructor(leftRedirect, argTypes, rightExpression);
         // perform additional type checking on arguments
-        MapExpression mapExpression = (MapExpression) rightExpression;
-        checkGroovyConstructorMap(leftExpression, leftRedirect, mapExpression);
+        checkGroovyConstructorMap(leftExpression, leftRedirect, rightExpression);
     }
 
     private void checkTypeGenerics(final ClassNode leftExpressionType, final ClassNode rightExpressionType, final Expression rightExpression) {
@@ -1316,7 +1316,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     private static boolean isConstructorAbbreviation(final ClassNode leftType, final Expression rightExpression) {
         if (rightExpression instanceof ListExpression) {
             return !(ArrayList_TYPE.isDerivedFrom(leftType) || ArrayList_TYPE.implementsInterface(leftType)
-                    || LinkedHashSet_TYPE.isDerivedFrom(leftType) || LinkedHashSet_TYPE.implementsInterface(leftType));
+                || LinkedHashSet_TYPE.isDerivedFrom(leftType) || LinkedHashSet_TYPE.implementsInterface(leftType));
+        }
+        if (rightExpression instanceof MapExpression) {
+            return !(LinkedHashMap_TYPE.isDerivedFrom(leftType) || LinkedHashMap_TYPE.implementsInterface(leftType));
         }
         return false;
     }
@@ -1345,7 +1348,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (rightExpression instanceof ListExpression) {
                 addListAssignmentConstructorErrors(lTypeRedirect, leftExpressionType, rTypeInferred, rightExpression, assignmentExpression);
             } else if (rightExpression instanceof MapExpression) {
-                addMapAssignmentConstructorErrors(lTypeRedirect, leftExpression, rightExpression);
+                addMapAssignmentConstructorErrors(lTypeRedirect, leftExpression, (MapExpression)rightExpression);
             }
             if (!hasGStringStringError(leftExpressionType, rTypeAdjusted, rightExpression)
                     && !isConstructorAbbreviation(leftExpressionType, rightExpression)) {
@@ -1419,29 +1422,28 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      * @param arguments the constructor arguments
      */
     protected MethodNode checkGroovyStyleConstructor(final ClassNode node, final ClassNode[] arguments, final ASTNode source) {
-        if (node.equals(ClassHelper.OBJECT_TYPE) || node.equals(ClassHelper.DYNAMIC_TYPE)) {
-            // in that case, we are facing a list constructor assigned to a def or object
-            return null;
+        if (node.equals(DYNAMIC_TYPE) || node.equals(OBJECT_TYPE)) {
+            return null; // list or map assigned to def or Object
         }
-        List<ConstructorNode> constructors = node.getDeclaredConstructors();
+        List<? extends MethodNode> constructors = node.getDeclaredConstructors();
         if (constructors.isEmpty() && arguments.length == 0) {
             return null;
         }
-        List<MethodNode> constructorList = findMethod(node, "<init>", arguments);
-        if (constructorList.isEmpty()) {
-            if (isBeingCompiled(node) && arguments.length == 1 && LinkedHashMap_TYPE.equals(arguments[0])) {
+        constructors = findMethod(node, "<init>", arguments);
+        if (constructors.isEmpty()) {
+            if (isBeingCompiled(node) && !node.isInterface() && arguments.length == 1 && arguments[0].equals(LinkedHashMap_TYPE)) {
                 // there will be a default map constructor added later
                 Parameter[] params = {new Parameter(LinkedHashMap_TYPE, "args")};
                 return new ConstructorNode(Opcodes.ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
             } else {
-                addStaticTypeError("No matching constructor found: " + node + toMethodParametersString("<init>", arguments), source);
+                addStaticTypeError("No matching constructor found: " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
                 return null;
             }
-        } else if (constructorList.size() > 1) {
-            addStaticTypeError("Ambiguous constructor call " + node + toMethodParametersString("<init>", arguments), source);
+        } else if (constructors.size() > 1) {
+            addStaticTypeError("Ambiguous constructor call " + prettyPrintTypeName(node) + toMethodParametersString("", arguments), source);
             return null;
         }
-        return constructorList.get(0);
+        return constructors.get(0);
     }
 
     /**
@@ -1992,13 +1994,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
             ClassNode forLoopVariableType = forLoop.getVariableType();
             ClassNode componentType;
-            if (Character_TYPE.equals(ClassHelper.getWrapper(forLoopVariableType)) && STRING_TYPE.equals(collectionType)) {
+            if (Character_TYPE.equals(getWrapper(forLoopVariableType)) && STRING_TYPE.equals(collectionType)) {
                 // we allow auto-coercion here
                 componentType = forLoopVariableType;
             } else {
                 componentType = inferLoopElementType(collectionType);
             }
-            if (ClassHelper.getUnwrapper(componentType) == forLoopVariableType) {
+            if (getUnwrapper(componentType) == forLoopVariableType) {
                 // prefer primitive type over boxed type
                 componentType = forLoopVariableType;
             }
@@ -2214,7 +2216,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         ClassNode type = getType(expression);
         ClassNode typeRe = type.redirect();
         ClassNode resultType;
-        if (isDoubleCategory(ClassHelper.getUnwrapper(typeRe))) {
+        if (isDoubleCategory(getUnwrapper(typeRe))) {
             resultType = type;
         } else if (typeRe == ArrayList_TYPE) {
             resultType = ArrayList_TYPE;
@@ -2446,7 +2448,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             List<ClassNode> tempTypes = getTemporaryTypesForExpression(expression);
             if (tempTypes != null && !tempTypes.isEmpty()) {
                 List<ClassNode> types = new ArrayList<>(tempTypes.size() + 1);
-                if (expressionType != null && !expressionType.equals(ClassHelper.OBJECT_TYPE) // GROOVY-7333
+                if (expressionType != null && !expressionType.equals(OBJECT_TYPE) // GROOVY-7333
                         && noneMatch(tempTypes, expressionType)) { // GROOVY-9769
                     types.add(expressionType);
                 }
@@ -2506,7 +2508,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
         super.visitClosureExpression(expression);
         typeCheckingContext.delegationMetadata = typeCheckingContext.delegationMetadata.getParent();
-        returnAdder.visitMethod(new MethodNode("dummy", 0, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, expression.getCode()));
+        returnAdder.visitMethod(new MethodNode("dummy", 0, OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, expression.getCode()));
 
         TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
         if (!enclosingClosure.getReturnTypes().isEmpty()) {
@@ -2933,7 +2935,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private void checkNamedParamsAnnotation(Parameter param, MapExpression args) {
-        if (!param.getType().isDerivedFrom(ClassHelper.MAP_TYPE)) return;
+        if (!param.getType().isDerivedFrom(MAP_TYPE)) return;
         List<MapEntryExpression> entryExpressions = args.getMapEntryExpressions();
         Map<Object, Expression> entries = new LinkedHashMap<Object, Expression>();
         for (MapEntryExpression entry : entryExpressions) {
@@ -3339,7 +3341,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 Expression type = annotation.getMember("type");
                 Integer stInt = Closure.OWNER_FIRST;
                 if (strategy != null) {
-                    stInt = (Integer) evaluateExpression(castX(ClassHelper.Integer_TYPE, strategy), typeCheckingContext.source.getConfiguration());
+                    stInt = (Integer) evaluateExpression(castX(Integer_TYPE, strategy), typeCheckingContext.source.getConfiguration());
                 }
                 if (value instanceof ClassExpression && !value.getType().equals(DELEGATES_TO_TARGET)) {
                     if (genericTypeIndex != null) {
@@ -4696,7 +4698,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     protected boolean areCategoryMethodCalls(final List<MethodNode> foundMethods, final String name, final ClassNode[] args) {
         boolean category = false;
-        if ("use".equals(name) && args != null && args.length == 2 && args[1].equals(ClassHelper.CLOSURE_TYPE)) {
+        if ("use".equals(name) && args != null && args.length == 2 && args[1].equals(CLOSURE_TYPE)) {
             category = true;
             for (MethodNode method : foundMethods) {
                 if (!(method instanceof ExtensionMethodNode) || !((ExtensionMethodNode) method).getExtensionMethodNode().getDeclaringClass().equals(DGM_CLASSNODE)) {
@@ -5087,7 +5089,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             return ((Variable) exp).getOriginType();
         }
         if (exp instanceof RangeExpression) {
-            ClassNode plain = ClassHelper.RANGE_TYPE.getPlainNodeReference();
+            ClassNode plain = RANGE_TYPE.getPlainNodeReference();
             RangeExpression re = (RangeExpression) exp;
             ClassNode fromType = getType(re.getFrom());
             ClassNode toType = getType(re.getTo());
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 608fa048b1..4bf8c427df 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -799,4 +799,13 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
             assert set.last() == 3
         '''
     }
+
+    // GROOVY-8136
+    void testInterfaceThatExtendsMapInitializedByMapLiteral() {
+        shouldFailWithMessages '''
+            interface MVM<K, V> extends Map<K, List<V>> { }
+            MVM map = [:] // no STC error; fails at runtime
+        ''',
+        'No matching constructor found'
+    }
 }
diff --git a/src/test/groovy/transform/stc/CoercionSTCTest.groovy b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
index 0bbad5657d..a8fef776f7 100644
--- a/src/test/groovy/transform/stc/CoercionSTCTest.groovy
+++ b/src/test/groovy/transform/stc/CoercionSTCTest.groovy
@@ -22,9 +22,9 @@ package groovy.transform.stc
  * Unit tests for static type checking : coercions.
  */
 class CoercionSTCTest extends StaticTypeCheckingTestCase {
+
     void testCoerceToArray() {
         assertScript '''
-        def m() {
             try {
                 throw new Exception()
             } catch (Throwable t) {
@@ -38,10 +38,90 @@ class CoercionSTCTest extends StaticTypeCheckingTestCase {
                 // ...
                 clean = newTrace.toArray(newTrace as StackTraceElement[])
             }
-        }
+        '''
+    }
 
-        m()
+    // GROOVY-6802
+    void testCoerceToBool1() {
+        assertScript '''
+            boolean b = [new Object()]
+            assert b
+        '''
+        assertScript '''
+            boolean b = [false]
+            assert b
+        '''
+        assertScript '''
+            boolean b = [true]
+            assert b
+        '''
+        assertScript '''
+            boolean b = ['x']
+            assert b
+        '''
+        assertScript '''
+            boolean b = [:]
+            assert !b
+        '''
+        assertScript '''
+            boolean b = []
+            assert !b
         '''
     }
-}
 
+    void testCoerceToBool2() {
+        assertScript '''
+            Boolean b = [new Object()]
+            assert b
+        '''
+        assertScript '''
+            Boolean b = [false]
+            assert b
+        '''
+        assertScript '''
+            Boolean b = [true]
+            assert b
+        '''
+        assertScript '''
+            Boolean b = ['x']
+            assert b
+        '''
+        assertScript '''
+            Boolean b = [:]
+            assert !b
+        '''
+        assertScript '''
+            Boolean b = []
+            assert !b
+        '''
+    }
+
+    void testCoerceToClass() {
+        assertScript '''
+            Class c = 'java.lang.String'
+            assert String.class
+        '''
+        shouldFailWithMessages '''
+            Class c = []
+        ''', 'No matching constructor found: java.lang.Class()'
+        shouldFailWithMessages '''
+            Class c = [:]
+        ''', 'No matching constructor found: java.lang.Class(java.util.LinkedHashMap)'
+    }
+
+    // GROOVY-6803
+    void testCoerceToString() {
+        assertScript '''
+            String s = ['x']
+            assert s == '[x]'
+        '''
+        assertScript '''
+            String s = [:]
+            assert s == '{}' || s == '[:]'
+        '''
+        assertScript '''
+            String s = []
+            assert s == '[]'
+        '''
+    }
+}
diff --git a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
index dacfebd309..2b4056de5e 100644
--- a/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ConstructorsSTCTest.groovy
@@ -37,7 +37,7 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             import java.awt.Dimension
             Dimension d = [100]
-        ''', 'No matching constructor found: java.awt.Dimension<init>(int)'
+        ''', 'No matching constructor found: java.awt.Dimension(int)'
     }
 
     void testWrongNumberOfArgumentsWithDefaultConstructor() {
@@ -62,7 +62,7 @@ class ConstructorsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             import java.awt.Dimension
             Dimension d = ['100','200']
-        ''', 'No matching constructor found: java.awt.Dimension<init>(java.lang.String, java.lang.String)'
+        ''', 'No matching constructor found: java.awt.Dimension(java.lang.String, java.lang.String)'
     }
 
     void testConstructFromListAndVariables() {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/CoercionStaticCompileTests.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/CoercionStaticCompileTests.groovy
new file mode 100644
index 0000000000..7506ceb415
--- /dev/null
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/CoercionStaticCompileTests.groovy
@@ -0,0 +1,27 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.classgen.asm.sc
+
+import groovy.transform.stc.CoercionSTCTest
+
+/**
+ * Unit tests for static compilation : coercions.
+ */
+final class CoercionStaticCompileTests extends CoercionSTCTest implements StaticCompilationTestSupport {
+}