You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/08/23 20:48:35 UTC
[groovy] branch master updated: GROOVY-8788: STC: prefer closer parameter match over receiver-type match
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 14946ff27a GROOVY-8788: STC: prefer closer parameter match over receiver-type match
14946ff27a is described below
commit 14946ff27a60d74b5d07c1b9a63f02624283c863
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Aug 21 19:36:17 2022 -0500
GROOVY-8788: STC: prefer closer parameter match over receiver-type match
---
.../groovy/runtime/DefaultGroovyMethods.java | 4 +-
.../transform/stc/StaticTypeCheckingSupport.java | 106 +++++++-------
.../transform/stc/StaticTypeCheckingVisitor.java | 13 +-
.../org.codehaus.groovy.runtime.ExtensionModule | 2 +-
src/test/groovy/bugs/Groovy8629Bug.groovy | 5 +-
.../stc/ArraysAndCollectionsSTCTest.groovy | 12 +-
.../stc/FieldsAndPropertiesSTCTest.groovy | 76 +++++++---
.../groovy/transform/stc/GenericsSTCTest.groovy | 35 +++--
.../groovy/transform/stc/STCAssignmentTest.groovy | 15 +-
.../transform/stc/STCExtensionMethodsTest.groovy | 157 +++++++++++++++++++--
.../transform/stc/TypeInferenceSTCTest.groovy | 12 --
.../sc/FieldsAndPropertiesStaticCompileTest.groovy | 12 +-
.../classgen/asm/sc/bugs/Groovy7325Bug.groovy | 41 +++---
.../groovy/transform/DelegateTransformTest.groovy | 6 +-
14 files changed, 325 insertions(+), 171 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 6695419a25..4029823165 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -8290,8 +8290,8 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
/**
* Support the subscript operator for a Map.
- * <pre class="groovyTestCase">def map = [a:10]
- * assert map["a"] == 10</pre>
+ * <pre class="groovyTestCase">def map = [1:10]
+ * assert map[1] == 10</pre>
*
* @param self a Map
* @param key an Object as a key for the map
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 9e929b55b0..847f481294 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1031,47 +1031,38 @@ public abstract class StaticTypeCheckingSupport {
*
* @return zero or more results
*/
- public static List<MethodNode> chooseBestMethod(final ClassNode receiver, final Collection<MethodNode> methods, final ClassNode... argumentTypes) {
+ public static List<MethodNode> chooseBestMethod(final ClassNode receiver, Collection<MethodNode> methods, final ClassNode... argumentTypes) {
if (!asBoolean(methods)) {
return Collections.emptyList();
}
+ // GROOVY-8965: type disjunction
+ boolean duckType = receiver instanceof UnionTypeClassNode;
+ if (methods.size() > 1 && !methods.iterator().next().isConstructor())
+ methods = removeCovariantsAndInterfaceEquivalents(methods, duckType);
+
+ Set<MethodNode> bestMethods = new HashSet<>(); // choose best method(s) for each possible receiver
+ for (ClassNode rcvr : duckType ? ((UnionTypeClassNode) receiver).getDelegates() : new ClassNode[]{receiver}) {
+ bestMethods.addAll(chooseBestMethods(rcvr, methods, argumentTypes));
+ }
+ return new LinkedList<>(bestMethods); // assumes caller wants remove to be inexpensive
+ }
+
+ private static List<MethodNode> chooseBestMethods(final ClassNode receiver, Collection<MethodNode> methods, final ClassNode[] argumentTypes) {
int bestDist = Integer.MAX_VALUE;
- List<MethodNode> bestChoices = new LinkedList<>();
- boolean duckType = receiver instanceof UnionTypeClassNode; // GROOVY-8965: type disjunction
- boolean noCulling = methods.size() <= 1 || "<init>".equals(methods.iterator().next().getName());
- Iterable<MethodNode> candidates = noCulling ? methods : removeCovariantsAndInterfaceEquivalents(methods, duckType);
-
- for (MethodNode candidate : candidates) {
- MethodNode safeNode = candidate;
- ClassNode[] safeArgs = argumentTypes;
- boolean isExtensionMethod = candidate instanceof ExtensionMethodNode;
- if (isExtensionMethod) {
- int nArgs = argumentTypes.length;
- safeArgs = new ClassNode[nArgs + 1];
- System.arraycopy(argumentTypes, 0, safeArgs, 1, nArgs);
- safeArgs[0] = receiver; // prepend self-type as first argument
- safeNode = ((ExtensionMethodNode) candidate).getExtensionMethodNode();
- }
-
- /* TODO: corner case
- class B extends A {}
- Animal foo(A a) {}
- Person foo(B b) {}
-
- B b = new B()
- Person p = foo(b)
- */
-
- ClassNode declaringClass = candidate.getDeclaringClass();
+ List<MethodNode> bestMethods = new ArrayList<>();
+
+ // phase 1: argument-parameter distance classifier
+ for (MethodNode method : methods) {
+ ClassNode declaringClass = method.getDeclaringClass();
ClassNode actualReceiver = receiver != null ? receiver : declaringClass;
Map<GenericsType, GenericsType> spec;
- if (candidate.isStatic()) {
+ if (method.isStatic()) {
spec = Collections.emptyMap(); // none visible
} else {
spec = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(declaringClass, actualReceiver);
- GenericsType[] methodGenerics = candidate.getGenericsTypes();
+ GenericsType[] methodGenerics = method.getGenericsTypes();
if (methodGenerics != null) { // GROOVY-10322: remove hidden type parameters
for (int i = 0, n = methodGenerics.length; i < n && !spec.isEmpty(); i += 1) {
for (Iterator<GenericsType> it = spec.keySet().iterator(); it.hasNext(); ) {
@@ -1080,34 +1071,49 @@ public abstract class StaticTypeCheckingSupport {
}
}
}
+ Parameter[] parameters = makeRawTypes(method.getParameters(), spec);
- Parameter[] params = makeRawTypes(safeNode.getParameters(), spec);
- int dist = measureParametersAndArgumentsDistance(params, safeArgs);
- if (dist >= 0) {
- dist += getClassDistance(declaringClass, actualReceiver);
- dist += getExtensionDistance(isExtensionMethod);
+ int dist = measureParametersAndArgumentsDistance(parameters, argumentTypes);
+ if (dist >= 0 && dist <= bestDist) {
if (dist < bestDist) {
bestDist = dist;
- bestChoices.clear();
- bestChoices.add(candidate);
- } else if (dist == bestDist) {
- bestChoices.add(candidate);
+ bestMethods.clear();
}
+ bestMethods.add(method);
}
}
- if (bestChoices.size() > 1 && !duckType) {
- // GROOVY-6849: prefer extension method in case of ambiguity
- List<MethodNode> onlyExtensionMethods = new LinkedList<>();
- for (MethodNode choice : bestChoices) {
- if (choice instanceof ExtensionMethodNode) {
- onlyExtensionMethods.add(choice);
+
+ // phase 2: receiver-provider distance classifier
+ if (bestMethods.size() > 1 && receiver != null) {
+ methods = bestMethods;
+ bestDist = Integer.MAX_VALUE;
+ bestMethods = new ArrayList<>();
+ for (MethodNode method : methods) {
+ int dist = getClassDistance(method.getDeclaringClass(), receiver);
+ if (dist <= bestDist) {
+ if (dist < bestDist) {
+ bestDist = dist;
+ bestMethods.clear();
+ }
+ bestMethods.add(method);
}
}
- if (onlyExtensionMethods.size() == 1) {
- return onlyExtensionMethods;
+ }
+
+ // phase 3: prefer extension method in case of tie
+ if (bestMethods.size() > 1) {
+ List<MethodNode> extensionMethods = new ArrayList<>();
+ for (MethodNode method : bestMethods) {
+ if (method instanceof ExtensionMethodNode) {
+ extensionMethods.add(method);
+ }
+ }
+ if (extensionMethods.size() == 1) {
+ bestMethods = extensionMethods;
}
}
- return bestChoices;
+
+ return bestMethods;
}
private static int measureParametersAndArgumentsDistance(final Parameter[] parameters, final ClassNode[] argumentTypes) {
@@ -1168,10 +1174,6 @@ public abstract class StaticTypeCheckingSupport {
return getDistance(actualReceiverForDistance, declaringClassForDistance);
}
- private static int getExtensionDistance(final boolean isExtensionMethodNode) {
- return isExtensionMethodNode ? 0 : 1;
- }
-
private static Parameter[] makeRawTypes(final Parameter[] parameters, final Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) {
return Arrays.stream(parameters).map(param -> {
String name = param.getType().getUnresolvedName();
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 35efb8b896..d791fddc1c 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -827,11 +827,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
enclosingExpressionRHS.visit(this);
}
ClassNode[] arguments = {rType, getType(enclosingExpressionRHS)};
- List<MethodNode> nodes = findMethod(lType.redirect(), "putAt", arguments);
- if (nodes.size() == 1) {
- typeCheckMethodsWithGenericsOrFail(lType, arguments, nodes.get(0), enclosingExpressionRHS);
- } else if (nodes.isEmpty()) {
+ List<MethodNode> methods = findMethod(lType, "putAt", arguments);
+ if (methods.isEmpty()) {
addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
+ } else if (methods.size() == 1) {
+ typeCheckMethodsWithGenericsOrFail(lType, arguments, methods.get(0), enclosingExpressionRHS);
}
}
}
@@ -4520,6 +4520,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
if (isArrayOp(op)) {
+ if (isOrImplements(left, MAP_TYPE) && isStringType(right)) { // GROOVY-5700, GROOVY-8788
+ PropertyExpression prop = propX(leftExpression, rightExpression); // m['xx'] -> m.xx
+ return existsProperty(prop, !typeCheckingContext.isTargetOfEnclosingAssignment(expr))
+ ? getType(prop) : getTypeForMapPropertyExpression(left, prop);
+ }
Expression copy = binX(leftExpression, expr.getOperation(), rightExpression);
copy.setSourcePosition(expr); // do not propagate BINARY_EXP_TARGET, etc.
MethodNode method = findMethodOrFail(copy, left, "getAt", rightRedirect);
diff --git a/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule b/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
index 4272f3f83b..93aff1ffec 100644
--- a/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
+++ b/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
@@ -21,5 +21,5 @@
// TODO remove dup with src/spec/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
moduleName=Test module
moduleVersion=1.0-test
-extensionClasses=support.MaxRetriesExtension,org.codehaus.groovy.runtime.m12n.TestStringExtension,org.codehaus.groovy.runtime.m12n.Groovy6496Extension,org.codehaus.groovy.runtime.m12n.TestPrimitiveWrapperExtension,org.codehaus.groovy.runtime.m12n.TestLocalDateTimeExtension
+extensionClasses=groovy.transform.stc.STCExtensionMethodsTest$Groovy8788,support.MaxRetriesExtension,org.codehaus.groovy.runtime.m12n.TestStringExtension,org.codehaus.groovy.runtime.m12n.Groovy6496Extension,org.codehaus.groovy.runtime.m12n.TestPrimitiveWrapperExtension,org.codehaus.groovy.runtime.m12n.TestLocalDateTimeExtension
staticExtensionClasses=support.StaticStringExtension,org.codehaus.groovy.runtime.m12n.TestStaticStringExtension
diff --git a/src/test/groovy/bugs/Groovy8629Bug.groovy b/src/test/groovy/bugs/Groovy8629Bug.groovy
index 3b56d81bc1..643cca4219 100644
--- a/src/test/groovy/bugs/Groovy8629Bug.groovy
+++ b/src/test/groovy/bugs/Groovy8629Bug.groovy
@@ -62,8 +62,8 @@ public class Groovy8629Bug extends GroovyTestCase {
@Override
IntegerPair next() {
String key = keyIterator.next()
- IntegerPair comp = new IntegerPair(m1[key], m2[key])
- return comp
+ IntegerPair pair = new IntegerPair(m1.get(key), m2.get(key))
+ return pair
}
@Override
@@ -87,5 +87,4 @@ public class Groovy8629Bug extends GroovyTestCase {
'''
}
-
}
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index c39f0df451..d80b4559ad 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -803,14 +803,12 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
}
// GROOVY-6131
- void testArraySetShouldGenerateBytecode() {
+ void testCollectionPutAt() {
shouldFailWithMessages '''
- void addToCollection(Collection coll, int index, val) {
- coll[index] = val
+ void addToCollection(Collection coll, int index, value) {
+ coll[index] = value
}
- def list = ['a']
- addToCollection(list, 0, 'b')
- assert list == ['b']
+ addToCollection(['a'], 0, 'b')
''',
'Cannot find matching method java.util.Collection#putAt(int, java.lang.Object)'
}
@@ -818,7 +816,7 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
// GROOVY-6266
void testMapKeyGenerics() {
assertScript '''
- HashMap<String,List<List>> map = new HashMap<String,List<List>>()
+ Map<String,List<List>> map = new HashMap<String,List<List>>()
map.get('key',[['val1'],['val2']])
assert map.'key'[0] == ['val1']
'''
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index ed471189ce..4f8c904d36 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -133,16 +133,6 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
'Cannot find matching method C#setX(<unknown parameter type>).'
}
- void testMapDotPropertySyntax() {
- assertScript '''
- HashMap map = [:]
- map['a'] = 1
- map.b = 2
- assert map.get('a') == 1
- assert map.get('b') == 2
- '''
- }
-
void testInferenceFromFieldType() {
assertScript '''
class A {
@@ -576,33 +566,73 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
'''
}
- // GROOVY-5700
- void testInferenceOfMapDotProperty() {
+ // GROOVY-5001, GROOVY-5491, GROOVY-6144, GROOVY-8788
+ void testReadMapProperty() {
+ assertScript '''
+ class A { }
+ class B { }
+ class HM extends HashMap<String,A> {
+ B b = new B()
+ }
+
+ def map = new HM()
+ map.put('a', new A())
+ assert map.get('a') != null
+ assert map.get('b') == null
+
+ A a = map.a
+ B b = map.b
+ a = map['a']
+ b = map['b']
+ assert a instanceof A
+ assert b instanceof B
+ '''
+ }
+
+ void testWriteMapProperty() {
+ assertScript '''
+ def map = [:]
+ map['a'] = 1
+ map.b = 2
+ assert map.get('a') == 1
+ assert map.get('b') == 2
+ '''
+ }
+
+ // GROOVY-5700, GROOVY-8788
+ void testInferenceOfMapSubProperty() {
assertScript '''
- def m = [retries: 10]
+ def map = [key: 123]
@ASTTest(phase=INSTRUCTION_SELECTION, value={
assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
})
- def r1 = m['retries']
+ def val = map['key']
+ assert val == 123
+ '''
+ }
+ // GROOVY-5700
+ void testInferenceOfMapDotProperty() {
+ assertScript '''
+ def map = [key: 123]
@ASTTest(phase=INSTRUCTION_SELECTION, value={
assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
})
- def r2 = m.retries
+ def val = map.key
+ assert val == 123
'''
}
void testInferenceOfListDotProperty() {
- assertScript '''class Foo { int x }
- def list = [new Foo(x:1), new Foo(x:2)]
+ assertScript '''
+ class C { int x }
+ def list = [new C(x:1), new C(x:2)]
@ASTTest(phase=INSTRUCTION_SELECTION, value={
- def iType = node.getNodeMetaData(INFERRED_TYPE)
- assert iType == make(List)
- assert iType.isUsingGenerics()
- assert iType.genericsTypes[0].type == Integer_TYPE
+ def type = node.getNodeMetaData(INFERRED_TYPE)
+ assert type.toString(false) == 'java.util.List<java.lang.Integer>'
})
- def r2 = list.x
- assert r2 == [ 1,2 ]
+ def x = list.x
+ assert x == [1,2]
'''
}
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index d6c7788f1c..9727aa89e5 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2004,18 +2004,26 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
void testPutAtWithWrongValueType() {
+ shouldFailWithMessages '''
+ def map = new HashMap<Integer, Integer>()
+ map[123] = new Object()
+ ''',
+ 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.lang.Integer, java.lang.Integer>, int, java.lang.Object]'
+ }
+
+ // GROOVY-8788
+ void testPutAtWithWrongValueType2() {
shouldFailWithMessages '''
def map = new HashMap<String, Integer>()
- map['hello'] = new Object()
+ map['x'] = new Object()
''',
- 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.lang.String, java.lang.Integer>, java.lang.String, java.lang.Object]'
+ 'Cannot assign value of type java.lang.Object to variable of type java.lang.Integer'
}
// GROOVY-9069
- void testPutAtWithWrongValueType2() {
+ void testPutAtWithWrongValueType3() {
shouldFailWithMessages '''
- class ConfigAttribute {
- }
+ class ConfigAttribute { }
void test(Map<String, Map<String, List<String>>> maps) {
maps.each { String key, Map<String, List<String>> map ->
Map<String, List<ConfigAttribute>> value = [:]
@@ -2024,18 +2032,15 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
''',
- 'Cannot find matching method java.util.Map#put(java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>). Please check if the declared type is correct and if the method exists.',
- 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.Map<java.lang.String, java.util.Map<java.lang.String, java.util.List<java.lang.String>>>, java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>]'
- }
+ 'Cannot find matching method java.util.Map#put(java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>)',
+ 'Cannot assign java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>> to: java.util.Map<java.lang.String, java.util.List<java.lang.String>>'
- void testPutAtWithWrongValueType3() {
assertScript '''
void test(Map<String, Map<String, List<String>>> maps) {
maps.each { String key, Map<String, List<String>> map ->
Map<String, List<String>> value = [:]
- // populate value
- maps[key] = value
maps.put(key, value)
+ maps[key] = value
}
}
'''
@@ -2802,9 +2807,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
assert type.genericsTypes[1].type == DATE
})
Map<Date, Date> map = new HashMap<>()
- map.put('foo', new Date())
+ map.put(123, new Date())
''',
- 'Cannot find matching method java.util.HashMap#put(java.lang.String, java.util.Date). Please check if the declared type is correct and if the method exists.'
+ 'Cannot find matching method java.util.HashMap#put(int, java.util.Date). Please check if the declared type is correct and if the method exists.'
}
void testInferDiamondForAssignmentWithDatesAndIllegalKeyUsingSquareBracket() {
shouldFailWithMessages '''
@@ -2822,9 +2827,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
assert type.genericsTypes[1].type == DATE
})
Map<Date, Date> map = new HashMap<>()
- map['foo'] = new Date()
+ map[123] = new Date()
''',
- 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, java.lang.String, java.util.Date]'
+ 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, int, java.util.Date]'
}
void testInferDiamondForAssignmentWithDatesAndIllegalValueUsingPut() {
shouldFailWithMessages '''
diff --git a/src/test/groovy/transform/stc/STCAssignmentTest.groovy b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
index c573582bcc..9df4022ee1 100644
--- a/src/test/groovy/transform/stc/STCAssignmentTest.groovy
+++ b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
@@ -1241,21 +1241,14 @@ class STCAssignmentTest extends StaticTypeCheckingTestCase {
void setProperty(String name, value) {
if (value instanceof File) {
value = new File(value, 'bar.txt')
- }
- else if (value instanceof URL) {
+ } else if (value instanceof URL) {
value = value.toURI()
- }
- else if (value instanceof InputStream) {
+ } else if (value instanceof InputStream) {
value = new BufferedInputStream(value)
- }
- else if (value instanceof GString) {
+ } else if (value instanceof GString) {
value = value.toString()
}
- if (mvm[name]) {
- mvm[name].add value
- } else {
- mvm.put(name, [value])
- }
+ mvm.computeIfAbsent(name, k -> []).add(value)
}
}
new M().setProperty('foo', 'bar')
diff --git a/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy b/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy
index fa538aa799..3254e09df1 100644
--- a/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy
+++ b/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy
@@ -25,33 +25,29 @@ import static org.codehaus.groovy.runtime.m12n.ExtensionModuleHelperForTests.doI
*/
class STCExtensionMethodsTest extends StaticTypeCheckingTestCase {
+ /**
+ * @see org.codehaus.groovy.runtime.m12n.TestStaticStringExtension
+ */
void testStaticExtensionMethod() {
assertScript '''
assert String.answer() == 42
'''
}
+ /**
+ * @see org.codehaus.groovy.runtime.m12n.TestStringExtension
+ */
void testNonStaticExtensionMethod() {
assertScript '''
def str = 'This is a string'
- // reverseToUpperCase is a usnit test extension method
assert str.reverseToUpperCase() == str.toUpperCase().reverse()
'''
}
- // GROOVY-7953
- void testExtensionPropertyWithPrimitiveReceiver() {
- assertScript '''
- assert 4.even
- '''
- }
-
- void testExtensionMethodWithGenericsAndPrimitiveReceiver() {
- assertScript '''
- assert 2d.groovy6496(2d) == 2d
- '''
- }
-
+ /**
+ * @see org.codehaus.groovy.runtime.m12n.TestStringExtension
+ * @see org.codehaus.groovy.runtime.m12n.TestStaticStringExtension
+ */
void testShouldFindExtensionMethodWithGrab() {
doInFork 'groovy.transform.stc.StaticTypeCheckingTestCase', '''
def impl = new MetaClassImpl(String)
@@ -79,4 +75,137 @@ class STCExtensionMethodsTest extends StaticTypeCheckingTestCase {
"""
'''
}
+
+ /**
+ * GROOVY-7953
+ *
+ * @see org.codehaus.groovy.runtime.m12n.TestPrimitiveWrapperExtension
+ */
+ void testExtensionPropertyWithPrimitiveReceiver() {
+ assertScript '''
+ assert 4.even
+ assert 4.isEven()
+ assert !5.isEven()
+ '''
+ }
+
+ /**
+ * GROOVY-6496
+ *
+ * @see org.codehaus.groovy.runtime.m12n.Groovy6496Extension
+ */
+ void testExtensionMethodWithGenericsAndPrimitiveReceiver() {
+ assertScript '''
+ assert 2d.groovy6496(2d) == 2d
+ '''
+ }
+
+ //--------------------------------------------------------------------------
+
+ static class Groovy8788 {
+ static class A {
+ def m1(Object o) {1}
+ def m2(String s) {1}
+ def m4(String s) {4}
+ def m5(String s) {4}
+ def m6(String s) {4}
+ }
+ static class B extends A {
+ def m1(String s) {2}
+ def m2(Object o) {2}
+ }
+ static class C extends B {
+ }
+
+ static m3(A self, String s) {1}
+ static m3(B self, Object o) {2}
+ static m3(B self, String s) {3}
+
+ static m4(A self, String s) {1}
+ static m4(B self, Object o) {2}
+
+ static m5(A self, String s) {1}
+ static m5(B self, Object o) {2}
+
+ static m6(B self, Object o) {2}
+ }
+
+ // GROOVY-8788
+ void testMethodSelection1() {
+ assertScript """import ${Groovy8788.name}.*
+ def a = new A()
+ assert a.m1(new Object()) == 1
+ assert a.m1(new String()) == 1
+ def b = new B()
+ assert b.m1(new Object()) == 1
+ assert b.m1(new String()) == 2
+ """
+ }
+
+ // GROOVY-8788
+ void testMethodSelection2() {
+ assertScript """import ${Groovy8788.name}.*
+ def a = new A()
+ assert a.m2(new String()) == 1
+ def b = new B()
+ assert b.m2(new Object()) == 2
+ assert b.m2(new String()) == 1
+ """
+
+ shouldFailWithMessages """import ${Groovy8788.name}.*
+ def a = new A()
+ a.m2(new Object())
+ """,
+ 'Cannot find matching method','A#m2(java.lang.Object)'
+ }
+
+ // GROOVY-8788
+ void testMethodSelection3() {
+ assertScript """import ${Groovy8788.name}.*
+ def a = new A()
+ assert a.m3(new String()) == 1
+ def b = new B()
+ assert b.m3(new Object()) == 2
+ assert b.m3(new String()) == 3
+ """
+ }
+
+ // GROOVY-8788
+ void testMethodSelection4() {
+ assertScript """import ${Groovy8788.name}.*
+ def a = new A()
+ assert a.m4(new String()) == 1
+ def b = new B()
+ assert b.m4(new Object()) == 2
+ assert b.m4(new String()) == 1
+ """
+ }
+
+ // GROOVY-8788
+ void testMethodSelection5() {
+ assertScript """import ${Groovy8788.name}.*
+ def a = new A()
+ assert a.m5(new String()) == 1
+ def b = new B()
+ assert b.m5(new Object()) == 2
+ assert b.m5(new String()) == 1
+ def c = new C()
+ assert c.m5(new Object()) == 2
+ assert c.m5(new String()) == 1
+ """
+ }
+
+ // GROOVY-8788
+ void testMethodSelection6() {
+ assertScript """import ${Groovy8788.name}.*
+ def a = new A()
+ assert a.m6(new String()) == 4
+ def b = new B()
+ assert b.m6(new Object()) == 2
+ assert b.m6(new String()) == 4
+ def c = new C()
+ assert c.m6(new Object()) == 2
+ assert c.m6(new String()) == 4
+ """
+ }
}
diff --git a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
index af7526fcb9..ef48abf961 100644
--- a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -1018,18 +1018,6 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
'''
}
- void testInferMapValueType() {
- assertScript '''
- Map<String, Integer> map = new HashMap<String,Integer>()
- map['foo'] = 123
- map['bar'] = 246
- Integer foo = map['foo']
- assert foo == 123
- Integer bar = map.get('bar')
- assert bar == 246
- '''
- }
-
// GROOVY-5522
void testTypeInferenceWithArrayAndFind() {
assertScript '''
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
index af1dc40472..b15d7890cd 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.classgen.asm.sc
+import groovy.test.NotYetImplemented
import groovy.transform.stc.FieldsAndPropertiesSTCTest
final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCTest implements StaticCompilationTestSupport {
@@ -31,16 +32,21 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT
'''
}
- void testGetAtFromStaticMap() {
+ void testStaticMapGetAt() {
assertScript '''
class Foo {
- public static Map CLASSES = [:]
+ public static Map CLASSES = [key:'value']
}
String foo = 'key'
- Foo.CLASSES[foo]
+ assert Foo.CLASSES[foo] == 'value'
'''
}
+ @Override @NotYetImplemented
+ void testReadMapProperty() {
+ super.testReadMapProperty() // Access to HM#a is forbidden
+ }
+
// GROOVY-5561
void testShouldNotThrowAccessForbidden() {
assertScript '''
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy
index 49ea6ed365..d12cdd665a 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy
@@ -16,43 +16,42 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-
package org.codehaus.groovy.classgen.asm.sc.bugs
import groovy.transform.stc.StaticTypeCheckingTestCase
import org.codehaus.groovy.classgen.asm.sc.StaticCompilationTestSupport
-class Groovy7325Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
+final class Groovy7325Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
void testGenericIdentityWithClosure() {
assertScript '''
-public static <T> T identity(T self) { self }
-
-@ASTTest(phase=INSTRUCTION_SELECTION,value={
- assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
-})
-Integer i = identity(2)
-
-@ASTTest(phase=INSTRUCTION_SELECTION,value={
- assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == CLOSURE_TYPE
-})
-Closure c = identity {'foo'}
-'''
+ static <T> T itself(T self) { self }
+
+ @ASTTest(phase=INSTRUCTION_SELECTION,value={
+ assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
+ })
+ Integer i = itself(2)
+
+ @ASTTest(phase=INSTRUCTION_SELECTION,value={
+ assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == CLOSURE_TYPE
+ })
+ Closure c = itself {'foo'}
+ '''
}
void testShouldNotThrowIllegalAccessToProtectedData() {
- shouldFailWithMessages('''
+ shouldFailWithMessages '''
class Test {
- final Set<String> HISTORY = [] as HashSet
+ final Set<String> HISTORY = [] as HashSet
- Set<String> getHistory() {
- return HISTORY.clone() as HashSet<String>
- }
+ Set<String> getHistory() {
+ return HISTORY.clone() as HashSet<String>
+ }
}
Test test = new Test()
println test.history
- ''', 'Method clone is protected in java.lang.Object')
+ ''',
+ 'Method clone is protected in java.lang.Object'
}
}
diff --git a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
index 7ad281e6ce..1e20c733f9 100644
--- a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
@@ -112,12 +112,12 @@ final class DelegateTransformTest {
def ms = new MapSet()
assert ms.size() == 1
- assert ms.toString() == '{a=1} [2, 3, 4]'
+ assert ms.toString() == '[a:1] [2, 3, 4]'
ms.remove(3)
assert ms.size() == 1
- assert ms.toString() == '{a=1} [2, 4]'
+ assert ms.toString() == '[a:1] [2, 4]'
ms.clear()
- assert ms.toString() == '{a=1} []'
+ assert ms.toString() == '[a:1] []'
'''
}