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 17:51:07 UTC
[groovy] branch GROOVY_2_5_X updated: GROOVY-10813: `makeDeclaringAndActualGenericsTypeMapOfExactType` erasure
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
new 46013ada78 GROOVY-10813: `makeDeclaringAndActualGenericsTypeMapOfExactType` erasure
46013ada78 is described below
commit 46013ada78ba8c18b33908c843c6a2206eb052aa
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Nov 18 09:26:57 2022 -0600
GROOVY-10813: `makeDeclaringAndActualGenericsTypeMapOfExactType` erasure
2_5_X backport
---
.../codehaus/groovy/ast/tools/GenericsUtils.java | 67 ++-
src/test/groovy/bugs/Groovy7204.groovy | 559 +++++++++++++++++++++
.../groovy/ast/tools/GenericsUtilsTest.groovy | 369 ++++++++------
3 files changed, 814 insertions(+), 181 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 25a4205a19..10167026ce 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -759,24 +759,28 @@ 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);
+ }
}
}
@@ -827,7 +831,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);
}
/**
@@ -844,25 +848,42 @@ public class GenericsUtils {
* @since 2.5.9
*/
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.<GenericsType, GenericsType>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<GenericsType, GenericsType>();
- 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/bugs/Groovy7204.groovy b/src/test/groovy/bugs/Groovy7204.groovy
new file mode 100644
index 0000000000..50deba1584
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy7204.groovy
@@ -0,0 +1,559 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.bugs
+
+import groovy.transform.NotYetImplemented
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy7204 {
+
+ @Test
+ void testTypeChecked1() {
+ assertScript '''import groovy.transform.*
+ @TypeChecked
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @TypeChecked
+ interface CrudRepository<T, S extends Serializable> {
+ void delete(T arg)
+ void delete(S arg)
+ }
+
+ @TypeChecked
+ interface MyRepository extends CrudRepository<String, Long> {
+ }
+
+ @TypeChecked
+ class MyRepositoryImpl implements MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testTypeChecked2() {
+ assertScript '''import groovy.transform.*
+ @TypeChecked
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @TypeChecked
+ abstract class CrudRepository<T, S extends Serializable> {
+ abstract void delete(T arg)
+ abstract void delete(S arg)
+ }
+
+ @TypeChecked
+ abstract class MyRepository extends CrudRepository<String, Long> {
+ }
+
+ @TypeChecked
+ class MyRepositoryImpl extends MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testTypeChecked3() {
+ assertScript '''import groovy.transform.*
+ @TypeChecked
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @TypeChecked
+ interface CrudRepository<T, S extends Serializable> {
+ void delete(T arg)
+ void delete(S arg)
+ }
+
+ @TypeChecked
+ interface MyRepository2 extends CrudRepository<String, Long> {
+ }
+
+ @TypeChecked
+ interface MyRepository extends MyRepository2 {
+ }
+
+ @TypeChecked
+ class MyRepositoryImpl implements MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testTypeChecked4() {
+ assertScript '''import groovy.transform.*
+ @TypeChecked
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @TypeChecked
+ abstract class CrudRepository<T, S extends Serializable> {
+ abstract void delete(T arg)
+ abstract void delete(S arg)
+ }
+
+ @TypeChecked
+ abstract class MyRepository2 extends CrudRepository<String, Long> {
+ }
+
+ @TypeChecked
+ abstract class MyRepository extends MyRepository2 {
+ }
+
+ @TypeChecked
+ class MyRepositoryImpl extends MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testTypeChecked5() {
+ assertScript '''import groovy.transform.*
+ @TypeChecked
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @TypeChecked
+ interface CrudRepository<T, S extends Serializable> {
+ void delete(T arg)
+ void delete(S arg)
+ }
+
+ @TypeChecked
+ abstract class MyRepository2 implements CrudRepository<String, Long> {
+ }
+
+ @TypeChecked
+ abstract class MyRepository extends MyRepository2 {
+ }
+
+ @TypeChecked
+ class MyRepositoryImpl extends MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @NotYetImplemented @Test
+ void testTypeChecked6() {
+ assertScript '''import groovy.transform.*
+ class Repository<T, S extends Serializable> {
+ void delete(T arg) { assert true }
+ void delete(S arg) { assert false: 'wrong method invoked' }
+ }
+
+ @TypeChecked
+ def test() {
+ Repository<String, Long> r = new Repository<String, Long>()
+ r.delete('foo')
+ }
+
+ test()
+ '''
+ }
+
+ //
+
+ @Test
+ void testCompileStatic1() {
+ assertScript '''import groovy.transform.*
+ @CompileStatic
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @CompileStatic
+ interface CrudRepository<T, S extends Serializable> {
+ void delete(T arg)
+ void delete(S arg)
+ }
+
+ @CompileStatic
+ interface MyRepository extends CrudRepository<String, Long> {
+ }
+
+ @CompileStatic
+ class MyRepositoryImpl implements MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testCompileStatic2() {
+ assertScript '''import groovy.transform.*
+ @CompileStatic
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @CompileStatic
+ abstract class CrudRepository<T, S extends Serializable> {
+ abstract void delete(T arg)
+ abstract void delete(S arg)
+ }
+
+ @CompileStatic
+ abstract class MyRepository extends CrudRepository<String, Long> {
+ }
+
+ @CompileStatic
+ class MyRepositoryImpl extends MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testCompileStatic3() {
+ assertScript '''import groovy.transform.*
+ @CompileStatic
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @CompileStatic
+ interface CrudRepository<T, S extends Serializable> {
+ void delete(T arg)
+ void delete(S arg)
+ }
+
+ @CompileStatic
+ interface MyRepository2 extends CrudRepository<String, Long> {
+ }
+
+ @CompileStatic
+ interface MyRepository extends MyRepository2 {
+ }
+
+ @CompileStatic
+ class MyRepositoryImpl implements MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testCompileStatic4() {
+ assertScript '''import groovy.transform.*
+ @CompileStatic
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @CompileStatic
+ abstract class CrudRepository<T, S extends Serializable> {
+ abstract void delete(T arg)
+ abstract void delete(S arg)
+ }
+
+ @CompileStatic
+ abstract class MyRepository2 extends CrudRepository<String, Long> {
+ }
+
+ @CompileStatic
+ abstract class MyRepository extends MyRepository2 {
+ }
+
+ @CompileStatic
+ class MyRepositoryImpl extends MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testCompileStatic5() {
+ assertScript '''import groovy.transform.*
+ @CompileStatic
+ public class MyClass {
+ static MyRepository factory() {
+ return new MyRepositoryImpl()
+ }
+
+ static void main(String[] args) {
+ MyRepository r = factory()
+ r.delete('foo')
+ }
+ }
+
+ @CompileStatic
+ interface CrudRepository<T, S extends Serializable> {
+ void delete(T arg)
+ void delete(S arg)
+ }
+
+ @CompileStatic
+ abstract class MyRepository2 implements CrudRepository<String, Long> {
+ }
+
+ @CompileStatic
+ abstract class MyRepository extends MyRepository2 {
+ }
+
+ @CompileStatic
+ class MyRepositoryImpl extends MyRepository {
+ @Override
+ public void delete(String arg) {
+ assert true
+ }
+
+ @Override
+ public void delete(Long arg) {
+ assert false: 'wrong method invoked'
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testCompileStatic6() {
+ assertScript '''import groovy.transform.*
+ class Repository<T, S extends Serializable> {
+ void delete(T arg) { assert true }
+ void delete(S arg) { assert false: 'wrong method invoked' }
+ }
+
+ @CompileStatic
+ def test() {
+ Repository<String, Long> r = new Repository<String, Long>()
+ r.delete('foo')
+ }
+
+ test()
+ '''
+ }
+
+ @Test // GROOVY-8059
+ void testCompileStatic7() {
+ assertScript '''import groovy.transform.*
+ abstract class A<K extends Serializable, V> {
+ void delete(K key) {}
+ void delete(V val) {}
+ }
+ class C extends A<String, Integer> {
+ }
+
+ @CompileStatic
+ class Test {
+ Test() {
+ def obj = new C()
+ obj.delete(Integer.valueOf(1))
+ }
+ }
+
+ new Test()
+ '''
+ }
+
+ @Test
+ void testCompileStatic8() {
+ assertScript '''import groovy.transform.*
+ class Trie<T> {
+ }
+
+ @CompileStatic
+ class Base<T> {
+ protected List<Trie<T>> list = []
+ Base() {
+ list.add(new Trie<String>()) // should fail!!
+ }
+ }
+
+ @CompileStatic
+ class Test extends Base<String> {
+ Trie<String> getFirstElement() {
+ list.get(0)
+ }
+ }
+
+ assert new Test().firstElement instanceof Trie
+ '''
+ }
+
+ @Test
+ void testCompileStatic9() {
+ assertScript '''import groovy.transform.*
+ class Trie<T> {
+ }
+
+ class Base<T> extends ArrayList<Trie<T>> {
+ }
+
+ @CompileStatic
+ class Test extends Base<String> {
+ Test() {
+ add(new Trie<String>())
+ }
+ Trie<String> getFirstElement() {
+ get(0)
+ }
+ }
+
+ assert new Test().firstElement instanceof Trie
+ '''
+ }
+}
diff --git a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
index cf2fb49a7c..4255f2a9f2 100644
--- a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
@@ -21,192 +21,245 @@ package org.codehaus.groovy.ast.tools
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
-class GenericsUtilsTest extends GroovyTestCase {
+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()
+ }
+
+ private static ClassNode findClassNode(String name, List<ClassNode> list) {
+ list.find { it.name == name }
+ }
+
+ //--------------------------------------------------------------------------
+
+ @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)
- 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 result.name == 'Base'
+ assert result.isUsingGenerics()
+ 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().is(target)
}
+ @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)
- 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 result.name == 'Base'
+ assert result.isUsingGenerics()
+ 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().is(target)
}
+ @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)
- 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 result.name == 'Base'
+ assert result.isUsingGenerics()
+ 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().is(target)
}
+ @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)
- 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 result.name == 'Base'
+ assert result.isUsingGenerics()
+ 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().is(target)
}
+ @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)
- 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 result.name == 'Base'
+ assert result.isUsingGenerics()
+ 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().is(target)
}
+ @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)
- 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 result.name == 'Base'
+ assert result.isUsingGenerics()
+ 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().is(target)
}
+ @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 {}
+ '''
+ ClassNode target = findClassNode('Base', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert result.name == 'Base'
+ assert result.isUsingGenerics()
+ 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().is(target)
+ }
+
+ @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> {}
+ '''
+ ClassNode target = findClassNode('A', classNodeList)
+ ClassNode source = findClassNode('C', classNodeList)
+ ClassNode result = GenericsUtils.findParameterizedType(target, source)
+
+ assert result.toString(false) == 'A <java.lang.Number>'
+ }
+
+ //
+
+ @Test
+ void testMakeDeclaringAndActualGenericsTypeMapOfExactType1() {
+ def classNodeList = compile '''
+ interface BiFunction<T,U,R> { R apply(T t, U u) }
+ interface BinaryOperator<T> extends BiFunction<T,T,T> {}
+ interface AnExtension extends BinaryOperator<Integer> {}
+ '''
+ ClassNode target = findClassNode('BiFunction', classNodeList)
+ ClassNode source = findClassNode('AnExtension', classNodeList)
+
+ Map<GenericsType, GenericsType> m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
+
+ 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 classNodeList = compile '''
+ interface I<T, U> {}
+ class Base<U> implements I<String,U> {}
+ class Derived extends Base<Integer > {}
+ '''
+ ClassNode target = findClassNode('I', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+
+ 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'
+ }
+
+ @Test
+ void testMakeDeclaringAndActualGenericsTypeMapOfExactType3() {
+ 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('Base', classNodeList)
- ClassNode actualReceiver = findClassNode('Derived', classNodeList)
-
- ClassNode parameterizedClass = GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
- 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('IBase', classNodeList)
+ ClassNode source = findClassNode('Derived', classNodeList)
+
+ 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'
}
- static ClassNode findClassNode(String name, List<ClassNode> classNodeList) {
- return classNodeList.find { it.name == name }
+ @Test // GROOVY-10813
+ void testMakeDeclaringAndActualGenericsTypeMapOfExactType4() {
+ def classNodeList = compile '''
+ interface BiFunction<T,U,R> { R apply(T t, U u) }
+ interface BinaryOperator<T> extends BiFunction<T,T,T> {}
+ interface I extends BinaryOperator /* <-- raw type */ {}
+ '''
+ ClassNode target = findClassNode('BiFunction', classNodeList)
+ ClassNode source = findClassNode('I', classNodeList)
+
+ def m = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(target, source)
+
+ assert m.size() == 3
+ assert m.entrySet().find { it.key.name == 'T' }.value.type.name == 'java.lang.Object'
+ assert m.entrySet().find { it.key.name == 'U' }.value.type.name == 'java.lang.Object'
+ assert m.entrySet().find { it.key.name == 'R' }.value.type.name == 'java.lang.Object'
}
}