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 2021/02/16 21:10:23 UTC
[groovy] 01/01: GROOVY-9945: findParameterizedType: fill type
parameters of super types
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY-9945
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 1d1696cad0303d0f4abe09fcd9b15ba88d5f5f87
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Feb 16 15:10:04 2021 -0600
GROOVY-9945: findParameterizedType: fill type parameters of super types
---
.../codehaus/groovy/ast/tools/GenericsUtils.java | 153 +++----
.../groovy/transform/stc/GenericsSTCTest.groovy | 17 +
.../groovy/ast/tools/GenericsUtilsTest.groovy | 443 +++++++++------------
3 files changed, 275 insertions(+), 338 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 bea8533..eddfb9d 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -43,11 +43,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -704,108 +706,78 @@ public class GenericsUtils {
/**
* Try to get the parameterized type from the cache.
- * If no cached item found, cache and return the result of {@link #findParameterizedType(ClassNode, ClassNode, boolean)}
+ * <p>
+ * If no cached item found, cache and return the result of {@link #findParameterizedType(ClassNode,ClassNode,boolean)}
*/
public static ClassNode findParameterizedTypeFromCache(final ClassNode genericsClass, final ClassNode actualType, boolean tryToFindExactType) {
if (!PARAMETERIZED_TYPE_CACHE_ENABLED) {
return findParameterizedType(genericsClass, actualType, tryToFindExactType);
}
- SoftReference<ClassNode> sr = PARAMETERIZED_TYPE_CACHE.getAndPut(new ParameterizedTypeCacheKey(genericsClass, actualType), key -> new SoftReference<>(findParameterizedType(key.getGenericsClass(), key.getActualType(), tryToFindExactType)));
+ SoftReference<ClassNode> sr = PARAMETERIZED_TYPE_CACHE.getAndPut(
+ new ParameterizedTypeCacheKey(genericsClass, actualType),
+ key -> new SoftReference<>(findParameterizedType(key.getGenericsClass(), key.getActualType(), tryToFindExactType)));
- return null == sr ? null : sr.get();
+ return sr == null ? null : sr.get();
}
/**
- * Convenience method for {@link #findParameterizedType(ClassNode, ClassNode, boolean)}
- * when the {@code tryToFindExactType} boolean is {@code false}.
+ * Convenience method for {@link #findParameterizedType(ClassNode,ClassNode,boolean)} when {@code tryToFindExactType} is {@code false}.
*/
public static ClassNode findParameterizedType(final ClassNode genericsClass, final ClassNode actualType) {
return findParameterizedType(genericsClass, actualType, false);
}
/**
- * Get the parameterized type by searching the whole class hierarchy according to generics class and actual receiver.
- * {@link #findParameterizedTypeFromCache(ClassNode, ClassNode, boolean)} is strongly recommended for better performance.
- *
- * @param genericsClass the generics class
- * @param actualType the actual type
- * @param tryToFindExactType whether to try to find exact type
- * @return the parameterized type
+ * Gets the parameterized type by searching the whole class hierarchy according to generics class and actual receiver.
+ * <p>
+ * {@link #findParameterizedTypeFromCache(ClassNode,ClassNode,boolean)} is strongly recommended for better performance.
*/
- public static ClassNode findParameterizedType(ClassNode genericsClass, ClassNode actualType, boolean tryToFindExactType) {
- ClassNode parameterizedType = null;
-
- if (null == genericsClass.getGenericsTypes()) {
- return parameterizedType;
+ public static ClassNode findParameterizedType(final ClassNode genericsClass, final ClassNode actualType, final boolean tryToFindExactType) {
+ final GenericsType[] genericsTypes = genericsClass.getGenericsTypes();
+ if (genericsTypes == null || genericsClass.isGenericsPlaceHolder()) {
+ return null;
}
- GenericsType[] declaringGenericsTypes = genericsClass.getGenericsTypes();
-
- List<ClassNode> classNodeList = new LinkedList<>(getAllSuperClassesAndInterfaces(actualType));
- classNodeList.add(0, actualType);
-
- LinkedList<ClassNode> parameterizedTypeCandidateList = new LinkedList<>();
-
- for (ClassNode cn : classNodeList) {
- if (cn == genericsClass) {
- continue;
- }
-
- if (tryToFindExactType && null != cn.getGenericsTypes() && hasNonPlaceHolders(cn)) {
- parameterizedTypeCandidateList.add(cn);
- }
+ if (actualType.equals(genericsClass)) {
+ return actualType;
+ }
- if (!(genericsClass.equals(cn.redirect()))) {
- continue;
- }
+ LinkedList<ClassNode> todo = new LinkedList<>();
+ Set<ClassNode> done = new HashSet<>();
+ todo.add(actualType);
+ ClassNode type;
- if (isGenericsTypeArraysLengthEqual(declaringGenericsTypes, cn.getGenericsTypes())) {
- parameterizedType = cn;
- break;
+ while ((type = todo.poll()) != null) {
+ if (type.equals(genericsClass)) {
+ return type;
}
- }
-
- if (null == parameterizedType) {
- if (!parameterizedTypeCandidateList.isEmpty()) {
- parameterizedType = parameterizedTypeCandidateList.getLast();
+ 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()) {
+ ClassNode cn = type.getUnresolvedSuperClass();
+ if (cn != null && cn.redirect() != ClassHelper.OBJECT_TYPE) {
+ if (parameterized)
+ cn = parameterizeType(type, cn);
+ todo.add(cn);
+ }
+ }
}
}
- return parameterizedType;
- }
-
- private static boolean isGenericsTypeArraysLengthEqual(GenericsType[] declaringGenericsTypes, GenericsType[] actualGenericsTypes) {
- return null != actualGenericsTypes && declaringGenericsTypes.length == actualGenericsTypes.length;
- }
-
- private static List<ClassNode> getAllSuperClassesAndInterfaces(ClassNode actualReceiver) {
- List<ClassNode> superClassAndInterfaceList = new LinkedList<>();
- List<ClassNode> allSuperClassNodeList = getAllUnresolvedSuperClasses(actualReceiver);
- superClassAndInterfaceList.addAll(allSuperClassNodeList);
- superClassAndInterfaceList.addAll(actualReceiver.getAllInterfaces());
-
- for (ClassNode superClassNode : allSuperClassNodeList) {
- superClassAndInterfaceList.addAll(superClassNode.getAllInterfaces());
- }
-
- return superClassAndInterfaceList;
- }
-
- private static List<ClassNode> getAllUnresolvedSuperClasses(ClassNode actualReceiver) {
- List<ClassNode> superClassNodeList = new LinkedList<>();
-
- for (ClassNode cn = actualReceiver.getUnresolvedSuperClass(); null != cn && ClassHelper.OBJECT_TYPE != cn; cn = cn.getUnresolvedSuperClass()) {
- superClassNodeList.add(cn);
- }
-
- return superClassNodeList;
+ return null;
}
private static final EvictableCache<ParameterizedTypeCacheKey, SoftReference<ClassNode>> PARAMETERIZED_TYPE_CACHE = new ConcurrentSoftCache<>(64);
/**
- * Clear the parameterized type cache
+ * 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() {
@@ -825,7 +797,7 @@ public class GenericsUtils {
* The resolved types can not help us to choose methods correctly if the argument is a string: T: Object, S: Serializable
* so we need actual types: T: String, S: Long
*/
- public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMap(ClassNode declaringClass, ClassNode actualReceiver) {
+ public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMap(final ClassNode declaringClass, final ClassNode actualReceiver) {
return doMakeDeclaringAndActualGenericsTypeMap(declaringClass, actualReceiver, false).getV1();
}
@@ -837,38 +809,33 @@ public class GenericsUtils {
* @param declaringClass the generics class node declaring the generics types
* @param actualReceiver the sub-class class node
* @return the placeholder-to-actualtype mapping
+ *
* @since 3.0.0
*/
- public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMapOfExactType(ClassNode declaringClass, ClassNode actualReceiver) {
- List<ClassNode> parameterizedTypeList = new LinkedList<>();
-
- Map<GenericsType, GenericsType> result = makeDeclaringAndActualGenericsTypeMapOfExactType(declaringClass, actualReceiver, parameterizedTypeList);
-
- return connectGenericsTypes(result);
+ public static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMapOfExactType(final ClassNode declaringClass, final ClassNode actualReceiver) {
+ return makeDeclaringAndActualGenericsTypeMapOfExactType(declaringClass, actualReceiver, new HashSet<>());
}
- private static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMapOfExactType(ClassNode declaringClass, ClassNode actualReceiver, List<ClassNode> parameterizedTypeList) {
- Tuple2<Map<GenericsType, GenericsType>, ClassNode> resultAndParameterizedTypeTuple = doMakeDeclaringAndActualGenericsTypeMap(declaringClass, actualReceiver, true);
- ClassNode parameterizedType = resultAndParameterizedTypeTuple.getV2();
- Map<GenericsType, GenericsType> result = resultAndParameterizedTypeTuple.getV1();
+ private static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMapOfExactType(final ClassNode declaringClass, final ClassNode actualReceiver, final Set<ClassNode> parameterizedTypes) {
+ Tuple2<Map<GenericsType, GenericsType>, ClassNode> tuple = doMakeDeclaringAndActualGenericsTypeMap(declaringClass, actualReceiver, true);
+ Map<GenericsType, GenericsType> result = tuple.getV1();
+ ClassNode parameterizedType = tuple.getV2();
- if (hasPlaceHolders(parameterizedType) && !parameterizedTypeList.contains(parameterizedType)) {
- parameterizedTypeList.add(parameterizedType);
- result.putAll(makeDeclaringAndActualGenericsTypeMapOfExactType(parameterizedType, actualReceiver, parameterizedTypeList));
+ if (parameterizedTypes.add(parameterizedType) && hasPlaceHolders(parameterizedType)) {
+ result.putAll(makeDeclaringAndActualGenericsTypeMapOfExactType(parameterizedType, actualReceiver, parameterizedTypes));
+ result = connectGenericsTypes(result);
}
- return connectGenericsTypes(result);
+ return result;
}
- private static Tuple2<Map<GenericsType, GenericsType>, ClassNode> doMakeDeclaringAndActualGenericsTypeMap(ClassNode declaringClass, ClassNode actualReceiver, boolean tryToFindExactType) {
+ private static Tuple2<Map<GenericsType, GenericsType>, ClassNode> doMakeDeclaringAndActualGenericsTypeMap(final ClassNode declaringClass, final ClassNode actualReceiver, final boolean tryToFindExactType) {
ClassNode parameterizedType = findParameterizedTypeFromCache(declaringClass, actualReceiver, tryToFindExactType);
-
- if (null == parameterizedType) {
+ if (parameterizedType == null) {
return tuple(Collections.emptyMap(), parameterizedType);
}
Map<GenericsType, GenericsType> result = new LinkedHashMap<>();
-
result.putAll(makePlaceholderAndParameterizedTypeMap(declaringClass));
result.putAll(makePlaceholderAndParameterizedTypeMap(parameterizedType));
@@ -877,7 +844,7 @@ public class GenericsUtils {
return tuple(result, parameterizedType);
}
- private static Map<GenericsType, GenericsType> makePlaceholderAndParameterizedTypeMap(ClassNode declaringClass) {
+ private static Map<GenericsType, GenericsType> makePlaceholderAndParameterizedTypeMap(final ClassNode declaringClass) {
if (null == declaringClass) {
return Collections.emptyMap();
}
@@ -897,7 +864,7 @@ public class GenericsUtils {
return result;
}
- private static Map<GenericsType, GenericsType> connectGenericsTypes(Map<GenericsType, GenericsType> genericsTypeMap) {
+ private static Map<GenericsType, GenericsType> connectGenericsTypes(final Map<GenericsType, GenericsType> genericsTypeMap) {
Map<GenericsType, GenericsType> result = new LinkedHashMap<>();
outer:
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 3e27826..2bab61d 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -807,6 +807,23 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-9945
+ void testShouldUseMethodGenericType9() {
+ assertScript '''
+ interface I<T> {
+ }
+ class A<T> implements I<String> {
+ def m(T t) { 'works' }
+ }
+ class B<T> extends A<T> {
+ }
+
+ def bee = new B<Float>()
+ def obj = bee.m(3.14f)
+ assert obj == 'works'
+ '''
+ }
+
// GROOVY-5516
void testAddAllWithCollectionShouldBeAllowed() {
assertScript '''import org.codehaus.groovy.transform.stc.ExtensionMethodNode
diff --git a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
index 598b5ce..1b30f71 100644
--- a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
@@ -19,307 +19,260 @@
package org.codehaus.groovy.ast.tools
-import groovy.test.GroovyTestCase
import org.codehaus.groovy.ast.ClassHelper
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.GenericsType
-import org.codehaus.groovy.control.CompilationUnit
-import org.codehaus.groovy.control.Phases
+import org.codehaus.groovy.control.CompilePhase
+import org.junit.Test
-import java.util.function.BiFunction
+final class GenericsUtilsTest {
-import static groovy.lang.Tuple.tuple
+ private static List<ClassNode> compile(String code) {
+ def compiler = new org.codehaus.groovy.ast.builder.AstStringCompiler()
+ compiler.compile(code, CompilePhase.SEMANTIC_ANALYSIS, false).tail()
+ }
+
+ private static ClassNode findClassNode(String name, List<ClassNode> list) {
+ list.find { it.name == name }
+ }
-class GenericsUtilsTest extends GroovyTestCase {
+ //--------------------------------------------------------------------------
+
+ @Test
void testFindParameterizedType1() {
- def code = '''
- class Base<T, S> {}
- class Derived extends Base<String, List> {}
+ def classNodeList = compile '''
+ class Base<T, S> {}
+ class Derived extends Base<String, List> {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
- assert parameterizedClass.isUsingGenerics()
- assert 'Base' == parameterizedClass.name
- GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
- assert 2 == genericsTypes.length
- assert 'java.lang.String' == genericsTypes[0].type.name
- assert 'java.util.List' == genericsTypes[1].type.name
- assert genericsClass.is(parameterizedClass.redirect())
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert 'Base' == result.name
+ 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()
}
+ @Test
void testFindParameterizedType2() {
- def code = '''
- class Base<T, S> {}
- class Derived2 extends Base<String, List> {}
- class Derived extends Derived2 {}
+ def classNodeList = compile '''
+ class Base<T, S> {}
+ class Derived2 extends Base<String, List> {}
+ class Derived extends Derived2 {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
- assert parameterizedClass.isUsingGenerics()
- assert 'Base' == parameterizedClass.name
- GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
- assert 2 == genericsTypes.length
- assert 'java.lang.String' == genericsTypes[0].type.name
- assert 'java.util.List' == genericsTypes[1].type.name
- assert genericsClass.is(parameterizedClass.redirect())
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert 'Base' == result.name
+ 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()
}
+ @Test
void testFindParameterizedType3() {
- def code = '''
- class Base0 {}
- class Base<T, S> extends Base0 {}
- class Derived2 extends Base<String, List> {}
- class Derived extends Derived2 {}
+ def classNodeList = compile '''
+ class Base0 {}
+ class Base<T, S> extends Base0 {}
+ class Derived2 extends Base<String, List> {}
+ class Derived extends Derived2 {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
- assert parameterizedClass.isUsingGenerics()
- assert 'Base' == parameterizedClass.name
- GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
- assert 2 == genericsTypes.length
- assert 'java.lang.String' == genericsTypes[0].type.name
- assert 'java.util.List' == genericsTypes[1].type.name
- assert genericsClass.is(parameterizedClass.redirect())
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert 'Base' == result.name
+ 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()
}
+ @Test
void testFindParameterizedType4() {
- def code = '''
- interface Base<T, S> {}
- class Derived2 implements Base<String, List> {}
- class Derived extends Derived2 {}
+ def classNodeList = compile '''
+ interface Base<T, S> {}
+ class Derived2 implements Base<String, List> {}
+ class Derived extends Derived2 {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
- assert parameterizedClass.isUsingGenerics()
- assert 'Base' == parameterizedClass.name
- GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
- assert 2 == genericsTypes.length
- assert 'java.lang.String' == genericsTypes[0].type.name
- assert 'java.util.List' == genericsTypes[1].type.name
- assert genericsClass.is(parameterizedClass.redirect())
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert 'Base' == result.name
+ 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()
}
+ @Test
void testFindParameterizedType5() {
- def code = '''
- interface Base<T, S> {}
- interface Base2 extends Base<String, List> {}
- class Derived2 implements Base2 {}
- class Derived extends Derived2 {}
+ def classNodeList = compile '''
+ interface Base<T, S> {}
+ interface Base2 extends Base<String, List> {}
+ class Derived2 implements Base2 {}
+ class Derived extends Derived2 {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
- assert parameterizedClass.isUsingGenerics()
- assert 'Base' == parameterizedClass.name
- GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
- assert 2 == genericsTypes.length
- assert 'java.lang.String' == genericsTypes[0].type.name
- assert 'java.util.List' == genericsTypes[1].type.name
- assert genericsClass.is(parameterizedClass.redirect())
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert 'Base' == result.name
+ 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()
}
+ @Test
void testFindParameterizedType6() {
- def code = '''
- interface Base<T, S> {}
- interface Base2 extends Base<String, List> {}
- class Derived2 implements Base2 {}
- class Derived3 extends Derived2 {}
- class Derived extends Derived3 {}
+ def classNodeList = compile '''
+ interface Base<T, S> {}
+ interface Base2 extends Base<String, List> {}
+ class Derived2 implements Base2 {}
+ class Derived3 extends Derived2 {}
+ class Derived extends Derived3 {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
- assert parameterizedClass.isUsingGenerics()
- assert 'Base' == parameterizedClass.name
- GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
- assert 2 == genericsTypes.length
- assert 'java.lang.String' == genericsTypes[0].type.name
- assert 'java.util.List' == genericsTypes[1].type.name
- assert genericsClass.is(parameterizedClass.redirect())
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert 'Base' == result.name
+ 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()
}
+ @Test
void testFindParameterizedType7() {
- def code = '''
- interface Base0 {}
- interface Base<T, S> extends Base0 {}
- interface Base2 extends Base<String, List> {}
- class Derived2 implements Base2 {}
- class Derived3 extends Derived2 {}
- class Derived extends Derived3 {}
+ def classNodeList = compile '''
+ interface Base0 {}
+ interface Base<T, S> extends Base0 {}
+ interface Base2 extends Base<String, List> {}
+ class Derived2 implements Base2 {}
+ class Derived3 extends Derived2 {}
+ class Derived extends Derived3 {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
- assert parameterizedClass.isUsingGenerics()
- assert 'Base' == parameterizedClass.name
- GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
- assert 2 == genericsTypes.length
- assert 'java.lang.String' == genericsTypes[0].type.name
- assert 'java.util.List' == genericsTypes[1].type.name
- assert genericsClass.is(parameterizedClass.redirect())
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert 'Base' == result.name
+ 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()
}
- void testMakeDeclaringAndActualGenericsTypeMapOfExactType() {
- def code = '''
- import java.util.function.*
- interface Derived extends BinaryOperator<Integer> {}
+ @Test // GROOVY-9945
+ void testFindParameterizedType8() {
+ def classNodeList = compile '''
+ interface I<T> {}
+ class A<T> implements I<String> {}
+ class B<T> extends A<T> {}
+ class C extends B<Number> {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
+ ClassNode target = findClassNode('A', classNodeList)
+ ClassNode source = findClassNode('C', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = ClassHelper.makeWithoutCaching(BiFunction.class)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
+ assert result.toString(false) == 'A <java.lang.Number>'
+ }
+
+ @Test
+ void testMakeDeclaringAndActualGenericsTypeMapOfExactType() {
+ def classNodeList = compile '''
+ import java.util.function.*
+ interface Derived extends BinaryOperator<Integer> {}
+ '''
+ ClassNode target = ClassHelper.makeWithoutCaching(java.util.function.BiFunction.class)
+ ClassNode source = findClassNode('Derived', classNodeList)
- Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(genericsClass, actualReceiver)
+ Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
- assert m.entrySet().every { it.value.type.getTypeClass() == Integer }
- assert m.entrySet().grep { it.key.name == 'T' }[0].value.type.getTypeClass() == Integer
- assert m.entrySet().grep { it.key.name == 'U' }[0].value.type.getTypeClass() == Integer
- assert m.entrySet().grep { it.key.name == 'R' }[0].value.type.getTypeClass() == Integer
+ 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.Integer'
+ assert m.entrySet().find { it.key.name == 'R' }.value.type.name == 'java.lang.Integer'
}
+ @Test
void testMakeDeclaringAndActualGenericsTypeMapOfExactType2() {
- def code = '''
- interface IBase<T, U> {}
- class Base<U> implements IBase<String, U> {}
- class Derived extends Base<Integer> {}
+ def classNodeList = compile '''
+ interface IBase<T, U> {}
+ class Base<U> implements IBase<String, U> {}
+ class Derived extends Base<Integer> {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
+ ClassNode target = findClassNode('IBase', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('IBase', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
+ Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
- Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(genericsClass, actualReceiver)
-
- assert m.entrySet().grep { it.key.name == 'T' }[0].value.type.getTypeClass() == String
- assert m.entrySet().grep { it.key.name == 'U' }[0].value.type.getTypeClass() == Integer
+ 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
void testMakeDeclaringAndActualGenericsTypeMapOfExactType3() {
- def code = '''
- interface IBase<T, U, R> {}
- class Base<X, Y> implements IBase<Y, String, X> {}
- class Derived extends Base<Boolean, Integer> {}
+ def classNodeList = compile '''
+ interface IBase<T, U, R> {}
+ class Base<X,Y> implements IBase<Y,String,X> {}
+ class Derived extends Base<Boolean, Integer> {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode genericsClass = findClassNode('IBase', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(genericsClass, actualReceiver)
- println m
-
- assert m.entrySet().grep { it.key.name == 'X' }[0].value.type.getTypeClass() == Boolean
- assert m.entrySet().grep { it.key.name == 'Y' }[0].value.type.getTypeClass() == Integer
- assert m.entrySet().grep { it.key.name == 'T' }[0].value.type.getTypeClass() == Integer
- assert m.entrySet().grep { it.key.name == 'U' }[0].value.type.getTypeClass() == String
- assert m.entrySet().grep { it.key.name == 'R' }[0].value.type.getTypeClass() == Boolean
- }
+ ClassNode target = findClassNode('IBase', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+
+ Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
- static ClassNode findClassNode(String name, List<ClassNode> classNodeList) {
- return classNodeList.find { it.name == name }
+ 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() {
- def code = '''
- import java.util.function.*
- interface T extends Function<String, Integer> {}
+ def classNodeList = compile '''
+ import java.util.function.*
+ interface T extends Function<String, Integer> {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode parameterizedClassNode = findClassNode('T', classNodeList).getAllInterfaces().find { it.name.equals('java.util.function.Function') }
-
- Tuple2<ClassNode[], ClassNode> typeInfo = GenericsUtils.parameterizeSAM(parameterizedClassNode)
- assert 1 == typeInfo.getV1().length
- assert ClassHelper.STRING_TYPE == typeInfo.getV1()[0]
- assert ClassHelper.Integer_TYPE == typeInfo.getV2()
+ ClassNode samType = findClassNode('T', classNodeList).interfaces.find { it.name == 'java.util.function.Function' }
+
+ def typeInfo = GenericsUtils.parameterizeSAM(samType)
+
+ assert 1 == typeInfo[0].length
+ assert ClassHelper.STRING_TYPE == typeInfo[0][0]
+
+ assert ClassHelper.Integer_TYPE == typeInfo[1]
}
+ @Test
void testParameterizeSAM2() {
- def code = '''
- import java.util.function.*
- interface T extends BinaryOperator<Integer> {}
+ def classNodeList = compile '''
+ import java.util.function.*
+ interface T extends BinaryOperator<Integer> {}
'''
- def ast = new CompilationUnit().tap {
- addSource 'hello.groovy', code
- compile Phases.SEMANTIC_ANALYSIS
- }.ast
-
- def classNodeList = ast.getModules()[0].getClasses()
- ClassNode parameterizedClassNode = findClassNode('T', classNodeList).getAllInterfaces().find { it.name.equals('java.util.function.BinaryOperator') }
-
- Tuple2<ClassNode[], ClassNode> typeInfo = GenericsUtils.parameterizeSAM(parameterizedClassNode)
- assert 2 == typeInfo.getV1().length
- assert ClassHelper.Integer_TYPE == typeInfo.getV1()[0]
- assert ClassHelper.Integer_TYPE == typeInfo.getV1()[1]
- assert ClassHelper.Integer_TYPE == typeInfo.getV2()
+ ClassNode samType = findClassNode('T', classNodeList).interfaces.find { it.name == 'java.util.function.BinaryOperator' }
+
+ 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 ClassHelper.Integer_TYPE == typeInfo[1]
}
}