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/11/18 15:55:25 UTC
[groovy] branch GROOVY_4_0_X updated: GROOVY-10813: `parameterizeSAM`: no type parameters from method context
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 64cf479a9a GROOVY-10813: `parameterizeSAM`: no type parameters from method context
64cf479a9a is described below
commit 64cf479a9a4a7069132adfe2f8c45fffafcfc705
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Nov 18 09:26:57 2022 -0600
GROOVY-10813: `parameterizeSAM`: no type parameters from method context
---
.../codehaus/groovy/ast/tools/GenericsUtils.java | 91 ++++++++-----
src/test/groovy/transform/stc/LambdaTest.groovy | 25 ++++
.../groovy/ast/tools/GenericsUtilsTest.groovy | 145 ++++++++++++++-------
3 files changed, 181 insertions(+), 80 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 215b3d5992..f8bcc8c6b1 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -709,6 +709,17 @@ public class GenericsUtils {
private static final boolean PARAMETERIZED_TYPE_CACHE_ENABLED = Boolean.parseBoolean(getSystemPropertySafe("groovy.enable.parameterized.type.cache", "true"));
+ private static final EvictableCache<ParameterizedTypeCacheKey, SoftReference<ClassNode>> PARAMETERIZED_TYPE_CACHE = new ConcurrentSoftCache<>(64);
+
+ /**
+ * Clears the parameterized type cache.
+ * <p>
+ * It is useful to IDE as the type being compiled are continuously being edited/altered, see GROOVY-8675
+ */
+ public static void clearParameterizedTypeCache() {
+ PARAMETERIZED_TYPE_CACHE.clearAll();
+ }
+
/**
* Convenience method for {@link #findParameterizedTypeFromCache(ClassNode, ClassNode, boolean)}
* when the {@code tryToFindExactType} boolean is {@code false}.
@@ -731,7 +742,7 @@ public class GenericsUtils {
new ParameterizedTypeCacheKey(genericsClass, actualType),
key -> new SoftReference<>(findParameterizedType(key.getGenericsClass(), key.getActualType(), tryToFindExactType)));
- return sr == null ? null : sr.get();
+ return sr != null ? sr.get() : null;
}
/**
@@ -762,41 +773,34 @@ public class GenericsUtils {
ClassNode type;
while ((type = todo.poll()) != null) {
- if (type.equals(genericsClass)) {
- return type;
- }
if (done.add(type)) {
- boolean parameterized = (type.getGenericsTypes() != null);
- for (ClassNode cn : type.getInterfaces()) {
- if (parameterized)
- cn = parameterizeType(type, cn);
- todo.add(cn);
- }
- if (!actualType.isInterface()) {
+ if (!type.isInterface()) {
ClassNode cn = type.getUnresolvedSuperClass();
if (cn != null && cn.redirect() != ClassHelper.OBJECT_TYPE) {
- if (parameterized)
+ if (hasUnresolvedGenerics(cn)) {
cn = parameterizeType(type, cn);
+ }
+ if (cn.equals(genericsClass)) {
+ return cn;
+ }
todo.add(cn);
}
}
+ for (ClassNode cn : type.getInterfaces()) {
+ if (hasUnresolvedGenerics(cn)) {
+ cn = parameterizeType(type, cn);
+ }
+ if (cn.equals(genericsClass)) {
+ return cn;
+ }
+ todo.add(cn);
+ }
}
}
return null;
}
- private static final EvictableCache<ParameterizedTypeCacheKey, SoftReference<ClassNode>> PARAMETERIZED_TYPE_CACHE = new ConcurrentSoftCache<>(64);
-
- /**
- * Clears the parameterized type cache.
- * <p>
- * It is useful to IDE as the type being compiled are continuously being edited/altered, see GROOVY-8675
- */
- public static void clearParameterizedTypeCache() {
- PARAMETERIZED_TYPE_CACHE.clearAll();
- }
-
/**
* map declaring generics type to actual generics type, e.g. GROOVY-7204:
* declaring generics types: T, S extends Serializable
@@ -811,7 +815,7 @@ public class GenericsUtils {
* so we need actual types: T: String, S: Long
*/
public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMap(final ClassNode declaringClass, final ClassNode actualReceiver) {
- return doMakeDeclaringAndActualGenericsTypeMap(declaringClass, actualReceiver, false);
+ return correlateTypeParametersAndTypeArguments(declaringClass, actualReceiver, false);
}
/**
@@ -826,25 +830,42 @@ public class GenericsUtils {
* @since 3.0.0
*/
public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMapOfExactType(final ClassNode declaringClass, final ClassNode actualReceiver) {
- return doMakeDeclaringAndActualGenericsTypeMap(declaringClass, actualReceiver, true);
+ return correlateTypeParametersAndTypeArguments(declaringClass, actualReceiver, true);
}
- private static Map<GenericsType, GenericsType> doMakeDeclaringAndActualGenericsTypeMap(final ClassNode declaringClass, final ClassNode actualReceiver, final boolean tryToFindExactType) {
- Map<GenericsType, GenericsType> map = Collections.emptyMap();
+ private static Map<GenericsType, GenericsType> correlateTypeParametersAndTypeArguments(final ClassNode declaringClass, final ClassNode actualReceiver, final boolean tryToFindExactType) {
ClassNode parameterizedType = findParameterizedTypeFromCache(declaringClass, actualReceiver, tryToFindExactType);
if (parameterizedType != null && parameterizedType.isRedirectNode() && !parameterizedType.isGenericsPlaceHolder()) { // GROOVY-10166
// declaringClass may be "List<T> -> List<E>" and parameterizedType may be "List<String> -> List<E>" or "List<> -> List<E>"
- GenericsType[] targetGenericsTypes = parameterizedType.redirect().getGenericsTypes();
- if (targetGenericsTypes != null) {
- GenericsType[] sourceGenericsTypes = parameterizedType.getGenericsTypes();
- if (sourceGenericsTypes == null) sourceGenericsTypes = EMPTY_GENERICS_ARRAY;
- map = new LinkedHashMap<>();
- for (int i = 0, m = sourceGenericsTypes.length, n = targetGenericsTypes.length; i < n; i += 1) {
- map.put(targetGenericsTypes[i], i < m ? sourceGenericsTypes[i] : targetGenericsTypes[i]);
+ final GenericsType[] typeParameters = parameterizedType.redirect().getGenericsTypes();
+ if (typeParameters != null) {
+ final GenericsType[] typeArguments = parameterizedType.getGenericsTypes();
+ final int m = typeArguments == null ? 0 : typeArguments.length;
+ final int n = typeParameters.length;
+
+ Map<GenericsType, GenericsType> map = new LinkedHashMap<>();
+ for (int i = 0; i < n; i += 1) {
+ map.put(typeParameters[i], i < m ? typeArguments[i] : erasure(typeParameters[i]));
}
+ return map;
}
}
- return map;
+ return Collections.emptyMap();
+ }
+
+ /**
+ * @see org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport#extractType(GenericsType)
+ */
+ private static GenericsType erasure(GenericsType gt) {
+ ClassNode cn = gt.getType().redirect(); // discard the placeholder
+
+ if (gt.getType().getGenericsTypes() != null)
+ gt = gt.getType().getGenericsTypes()[0];
+
+ if (gt.getUpperBounds() != null)
+ cn = gt.getUpperBounds()[0]; // TODO: if length > 1 then union type?
+
+ return cn.asGenericsType();
}
/**
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index bf802bfa31..c117fbac24 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -689,6 +689,31 @@ final class LambdaTest {
}
}
+ @Test // GROOVY-10813
+ void testConsumer11() {
+ ['CompileStatic', 'TypeChecked'].each { xform ->
+ assertScript """
+ @groovy.transform.${xform}
+ void test() {
+ java.util.function.Consumer c = x -> print(x)
+ c.accept('works')
+ }
+ test()
+ """
+ assertScript """
+ interface I<T extends CharSequence> {
+ void accept(T t)
+ }
+ @groovy.transform.${xform}
+ void test() {
+ I i = x -> print(x)
+ i.accept('works')
+ }
+ test()
+ """
+ }
+ }
+
@Test
void testFunctionalInterface1() {
assertScript shell, '''
diff --git a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
index d4b9bbfbcc..3c72244f59 100644
--- a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
@@ -48,12 +48,12 @@ final class GenericsUtilsTest {
ClassNode source = findClassNode('Derived', classNodeList)
ClassNode result = GenericsUtils.findParameterizedType(target, source)
- assert 'Base' == result.name
+ assert result.name == 'Base'
assert result.isUsingGenerics()
- assert 2 == result.genericsTypes.length
- assert 'java.lang.String' == result.genericsTypes[0].type.name
- assert 'java.util.List' == result.genericsTypes[1].type.name
- assert target === result.redirect()
+ assert result.genericsTypes.length == 2
+ assert result.genericsTypes[0].type.name == 'java.lang.String'
+ assert result.genericsTypes[1].type.name == 'java.util.List'
+ assert result.redirect() === target
}
@Test
@@ -67,12 +67,12 @@ final class GenericsUtilsTest {
ClassNode source = findClassNode('Derived', classNodeList)
ClassNode result = GenericsUtils.findParameterizedType(target, source)
- assert 'Base' == result.name
+ assert result.name == 'Base'
assert result.isUsingGenerics()
- assert 2 == result.genericsTypes.length
- assert 'java.lang.String' == result.genericsTypes[0].type.name
- assert 'java.util.List' == result.genericsTypes[1].type.name
- assert target === result.redirect()
+ assert result.genericsTypes.length == 2
+ assert result.genericsTypes[0].type.name == 'java.lang.String'
+ assert result.genericsTypes[1].type.name == 'java.util.List'
+ assert result.redirect() === target
}
@Test
@@ -87,12 +87,12 @@ final class GenericsUtilsTest {
ClassNode source = findClassNode('Derived', classNodeList)
ClassNode result = GenericsUtils.findParameterizedType(target, source)
- assert 'Base' == result.name
+ assert result.name == 'Base'
assert result.isUsingGenerics()
- assert 2 == result.genericsTypes.length
- assert 'java.lang.String' == result.genericsTypes[0].type.name
- assert 'java.util.List' == result.genericsTypes[1].type.name
- assert target === result.redirect()
+ assert result.genericsTypes.length == 2
+ assert result.genericsTypes[0].type.name == 'java.lang.String'
+ assert result.genericsTypes[1].type.name == 'java.util.List'
+ assert result.redirect() === target
}
@Test
@@ -106,12 +106,12 @@ final class GenericsUtilsTest {
ClassNode source = findClassNode('Derived', classNodeList)
ClassNode result = GenericsUtils.findParameterizedType(target, source)
- assert 'Base' == result.name
+ assert result.name == 'Base'
assert result.isUsingGenerics()
- assert 2 == result.genericsTypes.length
- assert 'java.lang.String' == result.genericsTypes[0].type.name
- assert 'java.util.List' == result.genericsTypes[1].type.name
- assert target === result.redirect()
+ assert result.genericsTypes.length == 2
+ assert result.genericsTypes[0].type.name == 'java.lang.String'
+ assert result.genericsTypes[1].type.name == 'java.util.List'
+ assert result.redirect() === target
}
@Test
@@ -126,12 +126,12 @@ final class GenericsUtilsTest {
ClassNode source = findClassNode('Derived', classNodeList)
ClassNode result = GenericsUtils.findParameterizedType(target, source)
- assert 'Base' == result.name
+ assert result.name == 'Base'
assert result.isUsingGenerics()
- assert 2 == result.genericsTypes.length
- assert 'java.lang.String' == result.genericsTypes[0].type.name
- assert 'java.util.List' == result.genericsTypes[1].type.name
- assert target === result.redirect()
+ assert result.genericsTypes.length == 2
+ assert result.genericsTypes[0].type.name == 'java.lang.String'
+ assert result.genericsTypes[1].type.name == 'java.util.List'
+ assert result.redirect() === target
}
@Test
@@ -147,12 +147,12 @@ final class GenericsUtilsTest {
ClassNode source = findClassNode('Derived', classNodeList)
ClassNode result = GenericsUtils.findParameterizedType(target, source)
- assert 'Base' == result.name
+ assert result.name == 'Base'
assert result.isUsingGenerics()
- assert 2 == result.genericsTypes.length
- assert 'java.lang.String' == result.genericsTypes[0].type.name
- assert 'java.util.List' == result.genericsTypes[1].type.name
- assert target === result.redirect()
+ assert result.genericsTypes.length == 2
+ assert result.genericsTypes[0].type.name == 'java.lang.String'
+ assert result.genericsTypes[1].type.name == 'java.util.List'
+ assert result.redirect() === target
}
@Test
@@ -169,12 +169,12 @@ final class GenericsUtilsTest {
ClassNode source = findClassNode('Derived', classNodeList)
ClassNode result = GenericsUtils.findParameterizedType(target, source)
- assert 'Base' == result.name
+ assert result.name == 'Base'
assert result.isUsingGenerics()
- assert 2 == result.genericsTypes.length
- assert 'java.lang.String' == result.genericsTypes[0].type.name
- assert 'java.util.List' == result.genericsTypes[1].type.name
- assert target === result.redirect()
+ assert result.genericsTypes.length == 2
+ assert result.genericsTypes[0].type.name == 'java.lang.String'
+ assert result.genericsTypes[1].type.name == 'java.util.List'
+ assert result.redirect() === target
}
@Test // GROOVY-9945
@@ -220,9 +220,9 @@ final class GenericsUtilsTest {
Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
+ assert m.size() == 2
assert m.entrySet().find { it.key.name == 'T' }.value.type.name == 'java.lang.String'
assert m.entrySet().find { it.key.name == 'U' }.value.type.name == 'java.lang.Integer'
- assert m.size() == 2
}
@Test
@@ -237,14 +237,14 @@ final class GenericsUtilsTest {
Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
+ assert m.size() == 3
assert m.entrySet().find { it.key.name == 'R' }.value.type.name == 'java.lang.Boolean'
assert m.entrySet().find { it.key.name == 'T' }.value.type.name == 'java.lang.Integer'
assert m.entrySet().find { it.key.name == 'U' }.value.type.name == 'java.lang.String'
- assert m.size() == 3
}
@Test
- void testParameterizeSAM() {
+ void testParameterizeSAM1() {
def classNodeList = compile '''
import java.util.function.*
interface T extends Function<String, Integer> {}
@@ -253,10 +253,10 @@ final class GenericsUtilsTest {
def typeInfo = GenericsUtils.parameterizeSAM(samType)
- assert 1 == typeInfo[0].length
- assert ClassHelper.STRING_TYPE == typeInfo[0][0]
+ assert typeInfo[0].length == 1
+ assert typeInfo[0][0] == ClassHelper.STRING_TYPE
- assert ClassHelper.Integer_TYPE == typeInfo[1]
+ assert typeInfo[1] == ClassHelper.Integer_TYPE
}
@Test
@@ -269,10 +269,65 @@ final class GenericsUtilsTest {
def typeInfo = GenericsUtils.parameterizeSAM(samType)
- assert 2 == typeInfo[0].length
- assert ClassHelper.Integer_TYPE == typeInfo[0][0]
- assert ClassHelper.Integer_TYPE == typeInfo[0][1]
+ assert typeInfo.v1.length == 2
+ assert typeInfo.v1[0] == ClassHelper.Integer_TYPE
+ assert typeInfo.v1[1] == ClassHelper.Integer_TYPE
+
+ assert typeInfo.v2 == ClassHelper.Integer_TYPE
+ }
+
+ @Test // GROOVY-10813
+ 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' }
+
+ def typeInfo = GenericsUtils.parameterizeSAM(samType)
+
+ assert typeInfo.v1.length == 2
+ assert typeInfo.v1[0].toString(false) == 'java.lang.Object'
+ assert typeInfo.v1[1].toString(false) == 'java.lang.Object'
+
+ assert typeInfo.v2.toString(false) == 'java.lang.Object'
+ }
+
+ @Test
+ void testParameterizeSAMWithRawTypeWithUpperBound() {
+ def classNodeList = compile '''
+ interface I<T extends CharSequence> {
+ T apply(T input);
+ }
+ abstract class A implements I {
+ }
+ '''
+ ClassNode samType = findClassNode('A', classNodeList).interfaces.find { it.name == 'I' }
+
+ def typeInfo = GenericsUtils.parameterizeSAM(samType)
+
+ assert typeInfo.v1.length == 1
+ assert typeInfo.v1[0].toString(false) == 'java.lang.CharSequence'
+
+ assert typeInfo.v2.toString(false) == 'java.lang.CharSequence'
+ }
+
+ @Test
+ void testParameterizeSAMWithRawTypeWithUpperBounds() {
+ def classNodeList = compile '''
+ interface I<T extends CharSequence & Serializable> {
+ T apply(T input);
+ }
+ abstract class A implements I {
+ }
+ '''
+ ClassNode samType = findClassNode('A', classNodeList).interfaces.find { it.name == 'I' }
+
+ def typeInfo = GenericsUtils.parameterizeSAM(samType)
+
+ assert typeInfo.v1.length == 1
+ assert typeInfo.v1[0].toString(false) == 'java.lang.CharSequence'
- assert ClassHelper.Integer_TYPE == typeInfo[1]
+ assert typeInfo.v2.toString(false) == 'java.lang.CharSequence'
}
}