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
})