This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
new 357a3020ba GROOVY-10067, GROOVY-11057: better `parameterizeType` result
357a3020ba is described below
commit 357a3020bac60dbc8015c251cc26b666459afbb0
Author: Eric Milles <[email protected]>
AuthorDate: Thu May 18 10:09:12 2023 -0500
GROOVY-10067, GROOVY-11057: better `parameterizeType` result
---
.../codehaus/groovy/ast/tools/GenericsUtils.java | 18 +++---
.../transform/stc/StaticTypeCheckingVisitor.java | 16 ++---
.../stc/ArraysAndCollectionsSTCTest.groovy | 5 +-
.../groovy/transform/stc/GenericsSTCTest.groovy | 65 ++++++++++---------
.../groovy/ast/tools/GenericsUtilsTest.groovy | 74 ++++++++++++++++++----
5 files changed, 118 insertions(+), 60 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index ba46f08a07..41b7f6cef8 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -443,11 +443,14 @@ public class GenericsUtils {
return makeClassSafeWithGenerics(type, newgTypes);
}
- public static ClassNode correctToGenericsSpec(Map<String, ClassNode>
genericsSpec, GenericsType type) {
- ClassNode cn = null; String name = type.getName();
- if (type.isPlaceholder() && name.charAt(0) != '#') {
- cn = genericsSpec.get(name);
- } else if (type.isWildcard()) {
+ public static ClassNode correctToGenericsSpec(final Map<String, ClassNode>
genericsSpec, final GenericsType type) {
+ ClassNode cn = null;
+ if (type.isPlaceholder()) {
+ String name = type.getName();
+ if (name.charAt(0) != '#') //
+ cn = genericsSpec.get(name);
+ }
+ else if (type.isWildcard()) {
if (type.getUpperBounds() != null)
cn = type.getUpperBounds()[0]; // GROOVY-9891
}
@@ -457,7 +460,7 @@ public class GenericsUtils {
return cn;
}
- public static ClassNode correctToGenericsSpec(Map<String, ClassNode>
genericsSpec, ClassNode type) {
+ public static ClassNode correctToGenericsSpec(final Map<String, ClassNode>
genericsSpec, ClassNode type) {
if (type.isArray()) {
return correctToGenericsSpec(genericsSpec,
type.getComponentType()).makeArray();
}
@@ -469,8 +472,7 @@ public class GenericsUtils {
return correctToGenericsSpec(genericsSpec, type);
}
}
- if (type == null) type =
ClassHelper.OBJECT_TYPE.getPlainNodeReference();
- return type;
+ return type != null ? type :
ClassHelper.OBJECT_TYPE.getPlainNodeReference();
}
public static Map<String, ClassNode> createGenericsSpec(final ClassNode
type) {
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 93c073a768..67b0e7d083 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -5437,14 +5437,14 @@ public class StaticTypeCheckingVisitor extends
ClassCodeVisitorSupport {
for (GenericsType gt : methodGenericTypes) {
// GROOVY-8409, GROOVY-10067, et al.: provide "no type witness"
mapping for param
resolvedPlaceholders.computeIfAbsent(new
GenericsTypeName(gt.getName()), gtn -> {
- GenericsType xxx = new
GenericsType(ClassHelper.makeWithoutCaching("#"),
- applyGenericsContext(resolvedPlaceholders,
gt.getUpperBounds()),
- applyGenericsContext(resolvedPlaceholders,
gt.getLowerBound()));
- xxx.getType().setRedirect(getCombinedBoundType(gt));
- xxx.putNodeMetaData(GenericsType.class, gt);
- xxx.setName("#" + gt.getName());
- xxx.setPlaceholder(true);
- return xxx;
+ ClassNode hash = ClassHelper.makeWithoutCaching("#" +
gt.getName());
+ hash.setRedirect(getCombinedBoundType(gt));
+ hash.setGenericsPlaceHolder(true);
+
+ GenericsType gtx = new GenericsType(hash,
applyGenericsContext(resolvedPlaceholders, gt.getUpperBounds()), null);
+ gtx.putNodeMetaData(GenericsType.class, gt);
+ gtx.setResolved(true);
+ return gtx;
});
}
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 9f9c14bc89..55eb99243e 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -1103,8 +1103,9 @@ class ArraysAndCollectionsSTCTest extends
StaticTypeCheckingTestCase {
'Cannot assign value of type
groovy.lang.ListWithDefault<java.lang.Integer> to variable of type
java.util.Set<java.lang.Integer>'
}
+ // GROOVY-8001, GROOVY-11028
void testMapWithTypeArgumentsInitializedByMapLiteral() {
- ['CharSequence,Integer', 'String,Number', 'CharSequence,Number'].each
{ spec ->
+ for (spec in ['CharSequence,Integer', 'String,Number',
'CharSequence,Number']) {
assertScript """
Map<$spec> map = [a:1,b:2,c:3]
assert map.size() == 3
@@ -1113,7 +1114,6 @@ class ArraysAndCollectionsSTCTest extends
StaticTypeCheckingTestCase {
"""
}
- // GROOVY-8001
assertScript '''
class C {
Map<String,Object> map
@@ -1124,7 +1124,6 @@ class ArraysAndCollectionsSTCTest extends
StaticTypeCheckingTestCase {
assert c.map['key'] == '42'
'''
- // GROOVY-11028
assertScript '''
Map<String,Integer> map = [:].withDefault { 0 }
assert map.size() == 0
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 8c12bd5226..414792cb1d 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2362,65 +2362,54 @@ class GenericsSTCTest extends
StaticTypeCheckingTestCase {
assertScript """
$mods void setX(List<String> strings) {
}
- void test() {
- x = Collections.emptyList()
- }
- test()
+ x = Collections.emptyList()
"""
assertScript """
$mods void setX(Collection<String> strings) {
}
- void test() {
- x = Collections.emptyList()
- }
- test()
+ x = Collections.emptyList()
"""
assertScript """
$mods void setX(Iterable<String> strings) {
}
- void test() {
- x = Collections.emptyList()
- }
- test()
+ x = Collections.emptyList()
"""
shouldFailWithMessages """
$mods void setX(List<String> strings) {
}
- void test() {
- x = Collections.<Integer>emptyList()
- }
+ x = Collections.<Integer>emptyList()
""",
'Incompatible generic argument types. Cannot assign
java.util.List<java.lang.Integer> to: java.util.List<java.lang.String>'
}
}
- // GROOVY-9734, GROOVY-9915
+ // GROOVY-9734, GROOVY-9915, GROOVY-11057
void testShouldUseMethodGenericType4() {
for (mods in ['', 'static']) {
assertScript """
$mods void m(List<String> strings) {
}
- void test() {
- m(Collections.emptyList())
- }
- test()
+ m(Collections.emptyList())
+ m([])
"""
assertScript """
$mods void m(Collection<String> strings) {
}
- void test() {
- m(Collections.emptyList())
- }
- test()
+ m(Collections.emptyList())
+ m([])
"""
assertScript """
$mods void m(Iterable<String> strings) {
}
- void test() {
- m(Collections.emptyList())
+ m(Collections.emptyList())
+ m([])
+ """
+ assertScript """
+ $mods void m(Map<String,Long> mapping) {
}
- test()
+ m(Collections.emptyMap())
+ m([:])
"""
}
}
@@ -5261,10 +5250,10 @@ class GenericsSTCTest extends
StaticTypeCheckingTestCase {
'''
}
- // GROOVY-10673
+ // GROOVY-10673, GROOVY-11057
void testMockito() {
assertScript '''
- @Grab('org.mockito:mockito-core:4.5.1')
+ @Grab('org.mockito:mockito-core:4.11.0')
import static org.mockito.Mockito.*
class C {
@@ -5276,13 +5265,29 @@ class GenericsSTCTest extends
StaticTypeCheckingTestCase {
def i = mock(I)
when(i.f(anyString())).thenAnswer { /*InvocationOnMock*/ iom ->
- //new C(string: iom.arguments[0]) works
+ new C(string: iom.arguments[0])
new C().tap {
string = iom.arguments[0]
}
}
assert i.f('x') instanceof C
'''
+
+ for (map in ['Collections.emptyMap()', '[:]']) {
+ assertScript """
+ @Grab('org.mockito:mockito-core:4.11.0')
+ import static org.mockito.Mockito.*
+
+ interface Configuration {
+ Map<String,Object> getSettings()
+ }
+
+ def configuration = mock(Configuration).tap {
+ when(it.getSettings()).thenReturn($map)
+ }
+ assert configuration.settings.isEmpty()
+ """
+ }
}
//--------------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
index 3c72244f59..a1ef582a85 100644
--- a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
@@ -23,17 +23,18 @@ import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.GenericsType
import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor
import org.junit.Test
final class GenericsUtilsTest {
private static List<ClassNode> compile(String code) {
def compiler = new org.codehaus.groovy.ast.builder.AstStringCompiler()
- compiler.compile(code, CompilePhase.SEMANTIC_ANALYSIS, false).tail()
+ compiler.compile(code, CompilePhase.INSTRUCTION_SELECTION,
false).tail()
}
- private static ClassNode findClassNode(String name, List<ClassNode> list) {
- list.find { it.name == name }
+ private static ClassNode findClassNode(String name, classNodes) {
+ classNodes.find { it.name == name }
}
//--------------------------------------------------------------------------
@@ -177,7 +178,8 @@ final class GenericsUtilsTest {
assert result.redirect() === target
}
- @Test // GROOVY-9945
+ // GROOVY-9945
+ @Test
void testFindParameterizedType8() {
def classNodeList = compile '''
interface I<T> {}
@@ -198,7 +200,7 @@ final class GenericsUtilsTest {
import java.util.function.*
interface Derived extends BinaryOperator<Integer> {}
'''
- ClassNode target =
ClassHelper.makeWithoutCaching(java.util.function.BiFunction.class)
+ ClassNode target =
ClassHelper.makeWithoutCaching(java.util.function.BiFunction)
ClassNode source = findClassNode('Derived', classNodeList)
Map<GenericsType, GenericsType> m =
GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
@@ -249,7 +251,7 @@ final class GenericsUtilsTest {
import java.util.function.*
interface T extends Function<String, Integer> {}
'''
- ClassNode samType = findClassNode('T', classNodeList).interfaces.find
{ it.name == 'java.util.function.Function' }
+ ClassNode samType = findClassNode('java.util.function.Function',
findClassNode('T', classNodeList).interfaces)
def typeInfo = GenericsUtils.parameterizeSAM(samType)
@@ -265,7 +267,7 @@ final class GenericsUtilsTest {
import java.util.function.*
interface T extends BinaryOperator<Integer> {}
'''
- ClassNode samType = findClassNode('T', classNodeList).interfaces.find
{ it.name == 'java.util.function.BinaryOperator' }
+ ClassNode samType = findClassNode('java.util.function.BinaryOperator',
findClassNode('T', classNodeList).interfaces)
def typeInfo = GenericsUtils.parameterizeSAM(samType)
@@ -276,13 +278,14 @@ final class GenericsUtilsTest {
assert typeInfo.v2 == ClassHelper.Integer_TYPE
}
- @Test // GROOVY-10813
+ // GROOVY-10813
+ @Test
void testParameterizeSAMWithRawType() {
def classNodeList = compile '''
interface I extends java.util.function.BinaryOperator {
}
'''
- ClassNode samType = findClassNode('I', classNodeList).interfaces.find
{ it.name == 'java.util.function.BinaryOperator' }
+ ClassNode samType = findClassNode('java.util.function.BinaryOperator',
findClassNode('I', classNodeList).interfaces)
def typeInfo = GenericsUtils.parameterizeSAM(samType)
@@ -302,7 +305,7 @@ final class GenericsUtilsTest {
abstract class A implements I {
}
'''
- ClassNode samType = findClassNode('A', classNodeList).interfaces.find
{ it.name == 'I' }
+ ClassNode samType = findClassNode('I', findClassNode('A',
classNodeList).interfaces)
def typeInfo = GenericsUtils.parameterizeSAM(samType)
@@ -321,7 +324,7 @@ final class GenericsUtilsTest {
abstract class A implements I {
}
'''
- ClassNode samType = findClassNode('A', classNodeList).interfaces.find
{ it.name == 'I' }
+ ClassNode samType = findClassNode('I', findClassNode('A',
classNodeList).interfaces)
def typeInfo = GenericsUtils.parameterizeSAM(samType)
@@ -330,4 +333,53 @@ final class GenericsUtilsTest {
assert typeInfo.v2.toString(false) == 'java.lang.CharSequence'
}
+
+ // GROOVY-10067, GROOVY-11057
+ @Test
+ void testParameterizeType1() {
+ def classNodeList = compile '''
+ @groovy.transform.CompileStatic
+ void test() {
+ def map = [:]
+ }
+ '''
+ // get the intermediate type of the map literal (LinkedHashMap<#K,#V>)
+ def node =
classNodeList[0].getDeclaredMethod('test').code.statements[0]
+ def type = new
StaticTypeCheckingVisitor(classNodeList[0].module.context,
+
classNodeList[0]).getType(node.expression.rightExpression)
+
+ ClassNode mapType = GenericsUtils.parameterizeType(type,
ClassHelper.MAP_TYPE)
+
+ assert mapType == ClassHelper.MAP_TYPE
+ assert mapType.genericsTypes.length == 2
+ assert mapType.genericsTypes[0].name == '#K'
+ assert mapType.genericsTypes[1].name == '#V'
+ assert mapType.genericsTypes[0].type.unresolvedName == '#K'
+ assert mapType.genericsTypes[1].type.unresolvedName == '#V'
+ assert mapType.genericsTypes[0].type.name == 'java.lang.Object'
+ assert mapType.genericsTypes[1].type.name == 'java.lang.Object'
+ }
+
+ // GROOVY-10067, GROOVY-11057
+ @Test
+ void testParameterizeType2() {
+ def classNodeList = compile '''
+ @groovy.transform.CompileStatic
+ void test() {
+ def list = []
+ }
+ '''
+ // get the intermediate type of the list literal (ArrayList<#E>)
+ def node =
classNodeList[0].getDeclaredMethod('test').code.statements[0]
+ def type = new
StaticTypeCheckingVisitor(classNodeList[0].module.context,
+
classNodeList[0]).getType(node.expression.rightExpression)
+
+ ClassNode listType = GenericsUtils.parameterizeType(type,
ClassHelper.LIST_TYPE)
+
+ assert listType == ClassHelper.LIST_TYPE
+ assert listType.genericsTypes.length == 1
+ assert listType.genericsTypes[0].name == '#E'
+ assert listType.genericsTypes[0].type.unresolvedName == '#E'
+ assert listType.genericsTypes[0].type.name == 'java.lang.Object'
+ }
}