Repository: groovy
Updated Branches:
  refs/heads/master d427d925d -> 1bd305bfb


GROOVY-8917: Failed to infer parameter type of some SAM, e.g. 
BinaryOperator(closes #837)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/1bd305bf
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/1bd305bf
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/1bd305bf

Branch: refs/heads/master
Commit: 1bd305bfb2b6b3d5ed02947f0117470a6e43c5fe
Parents: d427d92
Author: Daniel Sun <sun...@apache.org>
Authored: Tue Dec 11 23:30:25 2018 +0800
Committer: Daniel Sun <sun...@apache.org>
Committed: Tue Dec 11 23:30:25 2018 +0800

----------------------------------------------------------------------
 .../groovy/ast/tools/GenericsUtils.java         | 145 +++++++++++++++++--
 .../asm/sc/StaticTypesLambdaWriter.java         |   9 +-
 .../stc/StaticTypeCheckingVisitor.java          |  33 +++++
 .../groovy/transform/stc/GenericsSTCTest.groovy |   2 +-
 src/test/groovy/transform/stc/LambdaTest.groovy |  22 ++-
 .../groovy/ast/tools/GenericsUtilsTest.groovy   |  85 ++++++++++-
 6 files changed, 273 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/1bd305bf/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
----------------------------------------------------------------------
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 9e8c0bc..8519642 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -20,6 +20,7 @@ package org.codehaus.groovy.ast.tools;
 
 import antlr.RecognitionException;
 import antlr.TokenStreamException;
+import groovy.lang.Tuple2;
 import groovy.transform.stc.IncorrectTypeHintException;
 import org.apache.groovy.util.SystemUtil;
 import org.codehaus.groovy.GroovyBugError;
@@ -53,7 +54,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
 
+import static groovy.lang.Tuple.tuple;
 import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.getCorrectedClassNode;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf;
@@ -685,27 +688,28 @@ 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)}
+     * 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) {
+    public static ClassNode findParameterizedTypeFromCache(final ClassNode 
genericsClass, final ClassNode actualType, boolean tryToFindExactType) {
         if (!PARAMETERIZED_TYPE_CACHE_ENABLED) {
-            return findParameterizedType(genericsClass, actualType);
+            return findParameterizedType(genericsClass, actualType, 
tryToFindExactType);
         }
 
-        SoftReference<ClassNode> sr = PARAMETERIZED_TYPE_CACHE.getAndPut(new 
ParameterizedTypeCacheKey(genericsClass, actualType), key -> new 
SoftReference<>(findParameterizedType(key.getGenericsClass(), 
key.getActualType())));
+        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();
     }
 
     /**
      * Get the parameterized type by search the whole class hierarchy 
according to generics class and actual receiver.
-     * {@link #findParameterizedTypeFromCache(ClassNode, ClassNode)} is 
strongly recommended for better performance.
+     * {@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
      */
-    public static ClassNode findParameterizedType(ClassNode genericsClass, 
ClassNode actualType) {
+    public static ClassNode findParameterizedType(ClassNode genericsClass, 
ClassNode actualType, boolean tryToFindExactType) {
         ClassNode parameterizedType = null;
 
         if (null == genericsClass.getGenericsTypes()) {
@@ -717,12 +721,18 @@ public class GenericsUtils {
         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 (!genericsClass.equals(cn.redirect())) {
+            if (tryToFindExactType && null != cn.getGenericsTypes() && 
hasNonPlaceHolders(cn)) {
+                parameterizedTypeCandidateList.add(cn);
+            }
+
+            if (!(genericsClass.equals(cn.redirect()))) {
                 continue;
             }
 
@@ -732,6 +742,12 @@ public class GenericsUtils {
             }
         }
 
+        if (null == parameterizedType) {
+            if (!parameterizedTypeCandidateList.isEmpty()) {
+                parameterizedType = parameterizedTypeCandidateList.getLast();
+            }
+        }
+
         return parameterizedType;
     }
 
@@ -786,23 +802,130 @@ public class GenericsUtils {
      * so we need actual types:  T: String, S: Long
      */
     public static Map<GenericsType, GenericsType> 
makeDeclaringAndActualGenericsTypeMap(ClassNode declaringClass, ClassNode 
actualReceiver) {
-        ClassNode parameterizedType = 
findParameterizedTypeFromCache(declaringClass, actualReceiver);
+        return doMakeDeclaringAndActualGenericsTypeMap(declaringClass, 
actualReceiver, false).getV1();
+    }
+
+    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);
+    }
+
+    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();
+
+        if (hasPlaceHolders(parameterizedType) && 
!parameterizedTypeList.contains(parameterizedType)) {
+            parameterizedTypeList.add(parameterizedType);
+            
result.putAll(makeDeclaringAndActualGenericsTypeMapOfExactType(parameterizedType,
 actualReceiver, parameterizedTypeList));
+        }
+
+        return connectGenericsTypes(result);
+    }
+
+    private static Tuple2<Map<GenericsType, GenericsType>, ClassNode> 
doMakeDeclaringAndActualGenericsTypeMap(ClassNode declaringClass, ClassNode 
actualReceiver, boolean tryToFindExactType) {
+        ClassNode parameterizedType = 
findParameterizedTypeFromCache(declaringClass, actualReceiver, 
tryToFindExactType);
 
         if (null == parameterizedType) {
+            return tuple(Collections.emptyMap(), parameterizedType);
+        }
+
+        Map<GenericsType, GenericsType> result = new LinkedHashMap<>();
+
+        result.putAll(makePlaceholderAndParameterizedTypeMap(declaringClass));
+        
result.putAll(makePlaceholderAndParameterizedTypeMap(parameterizedType));
+
+        result = connectGenericsTypes(result);
+
+        return tuple(result, parameterizedType);
+    }
+
+    private static Map<GenericsType, GenericsType> 
makePlaceholderAndParameterizedTypeMap(ClassNode declaringClass) {
+        if (null == declaringClass) {
             return Collections.emptyMap();
         }
 
+        Map<GenericsType, GenericsType> result = new LinkedHashMap<>();
+
+        ClassNode redirectDeclaringClass = declaringClass.redirect();
         GenericsType[] declaringGenericsTypes = 
declaringClass.getGenericsTypes();
-        GenericsType[] actualGenericsTypes = 
parameterizedType.getGenericsTypes();
+        GenericsType[] redirectDeclaringGenericsTypes = 
redirectDeclaringClass.getGenericsTypes();
+
+        if (null != declaringGenericsTypes && null != 
redirectDeclaringGenericsTypes) {
+            for (int i = 0, n = declaringGenericsTypes.length; i < n; i++) {
+                result.put(redirectDeclaringGenericsTypes[i], 
declaringGenericsTypes[i]);
+            }
+        }
+
+        return result;
+    }
 
+    private static Map<GenericsType, GenericsType> 
connectGenericsTypes(Map<GenericsType, GenericsType> genericsTypeMap) {
         Map<GenericsType, GenericsType> result = new LinkedHashMap<>();
-        for (int i = 0, n = declaringGenericsTypes.length; i < n; i++) {
-            result.put(declaringGenericsTypes[i], actualGenericsTypes[i]);
+
+        outter:
+        for (Map.Entry<GenericsType, GenericsType> entry : 
genericsTypeMap.entrySet()) {
+            GenericsType key = entry.getKey();
+            GenericsType value = entry.getValue();
+
+            if (value.isPlaceholder()) {
+                for (Map.Entry<GenericsType, GenericsType> 
genericsTypeMapEntry : genericsTypeMap.entrySet()) {
+                    GenericsType genericsTypeMapEntryValue = 
genericsTypeMapEntry.getValue();
+                    if (!genericsTypeMapEntryValue.isPlaceholder() && 
(genericsTypeMapEntry.getKey().getName().equals(value.getName()))) {
+                        result.put(key, genericsTypeMapEntryValue); // 
connected to actual type
+                        continue outter;
+                    }
+                }
+            }
+
+            result.put(key, value);
         }
 
         return result;
     }
 
+    public static boolean hasNonPlaceHolders(ClassNode parameterizedType) {
+        return checkPlaceHolders(parameterizedType, genericsType -> 
!genericsType.isPlaceholder());
+    }
+
+    public static boolean hasPlaceHolders(ClassNode parameterizedType) {
+        return checkPlaceHolders(parameterizedType, genericsType -> 
genericsType.isPlaceholder());
+    }
+
+    private static boolean checkPlaceHolders(ClassNode parameterizedType, 
Predicate<GenericsType> p) {
+        if (null == parameterizedType) return false;
+
+        GenericsType[] genericsTypes = parameterizedType.getGenericsTypes();
+
+        if (null == genericsTypes) return false;
+
+        for (GenericsType genericsType : genericsTypes) {
+            if (p.test(genericsType)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static boolean isGenericsPlaceHolder(ClassNode cn) {
+        if (null == cn) return false;
+
+        GenericsType[] genericsTypes = cn.getGenericsTypes();
+
+        if (null == genericsTypes) return false;
+        if (genericsTypes.length != 1) return false;
+
+        GenericsType genericsType = genericsTypes[0];
+
+        if (!genericsType.isPlaceholder()) return false;
+
+        return genericsType.getName().equals(cn.getUnresolvedName());
+    }
+
     /**
      * Get the actual type according to the placeholder name
      *

http://git-wip-us.apache.org/repos/asf/groovy/blob/1bd305bf/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
index 523c011..c9301df 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java
@@ -54,6 +54,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
 import static 
org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_LAMBDA_TYPE;
 import static 
org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE;
 import static org.objectweb.asm.Opcodes.ACC_FINAL;
@@ -335,8 +336,12 @@ public class StaticTypesLambdaWriter extends LambdaWriter {
                 continue;
             }
 
-            parameter.setType(inferredType);
-            parameter.setOriginType(inferredType);
+            // Java 11 does not allow primitive type, we should use the 
wrapper type
+            // java.lang.invoke.LambdaConversionException: Type mismatch for 
instantiated parameter 0: int is not a subtype of class java.lang.Object
+            ClassNode wrappedType = getWrapper(inferredType);
+
+            parameter.setType(wrappedType);
+            parameter.setOriginType(wrappedType);
         }
 
         return parameters;

http://git-wip-us.apache.org/repos/asf/groovy/blob/1bd305bf/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
----------------------------------------------------------------------
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 12a48d3..d5b7688 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -179,6 +179,7 @@ import static 
org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
 import static 
org.codehaus.groovy.ast.tools.GenericsUtils.findActualTypeByGenericsPlaceholderName;
+import static 
org.codehaus.groovy.ast.tools.GenericsUtils.isGenericsPlaceHolder;
 import static 
org.codehaus.groovy.ast.tools.GenericsUtils.makeDeclaringAndActualGenericsTypeMap;
 import static org.codehaus.groovy.ast.tools.GenericsUtils.toGenericTypesString;
 import static 
org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode;
@@ -2848,9 +2849,41 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
                     applyGenericsContext(SAMTypeConnections, 
typeOrNull(parameterTypesForSAM, i));
             blockParameterTypes[i] = resolvedParameter;
         }
+
+        
tryToInferUnresolvedBlockParameterType(paramTypeWithReceiverInformation, 
methodForSAM, blockParameterTypes);
+
         openBlock.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, 
blockParameterTypes);
     }
 
+    private void tryToInferUnresolvedBlockParameterType(ClassNode 
paramTypeWithReceiverInformation, MethodNode methodForSAM, ClassNode[] 
blockParameterTypes) {
+        List<Integer> indexList = new LinkedList<>();
+        for (int i = 0, n = blockParameterTypes.length; i < n; i++) {
+            ClassNode blockParameterType = blockParameterTypes[i];
+            if (isGenericsPlaceHolder(blockParameterType)) {
+                indexList.add(i);
+            }
+        }
+
+        if (!indexList.isEmpty()) {
+            // If the parameter type failed to resolve, try to find the 
parameter type through the class hierarchy
+            Map<GenericsType, GenericsType> genericsTypeMap = 
GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(methodForSAM.getDeclaringClass(),
 paramTypeWithReceiverInformation);
+
+            for (Integer index : indexList) {
+                for (Map.Entry<GenericsType, GenericsType> entry : 
genericsTypeMap.entrySet()) {
+                    if 
(entry.getKey().getName().equals(blockParameterTypes[index].getUnresolvedName()))
 {
+                        ClassNode type = entry.getValue().getType();
+
+                        if (!isGenericsPlaceHolder(type)) {
+                            blockParameterTypes[index] = type;
+                        }
+
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
     private ClassNode typeOrNull(ClassNode[] parameterTypesForSAM, int i) {
         return i < parameterTypesForSAM.length ? parameterTypesForSAM[i] : 
null;
     }

http://git-wip-us.apache.org/repos/asf/groovy/blob/1bd305bf/src/test/groovy/transform/stc/GenericsSTCTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy 
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 69a43a7..9084cca 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -76,7 +76,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<String> list = []
             list << 1
-        ''', '[Static type checking] - Cannot find matching method 
java.util.List#leftShift(int)'
+        ''', '[Static type checking] - Cannot call <T> java.util.List 
<String>#leftShift(T) with arguments [int] '
     }
 
     void testAddOnList2UsingLeftShift() {

http://git-wip-us.apache.org/repos/asf/groovy/blob/1bd305bf/src/test/groovy/transform/stc/LambdaTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy 
b/src/test/groovy/transform/stc/LambdaTest.groovy
index 1654781..5aae4ee 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -20,8 +20,6 @@
 package groovy.transform.stc
 
 class LambdaTest extends GroovyTestCase {
-    private static final boolean SKIP_ERRORS = true;
-
     void testFunction() {
         assertScript '''
         import groovy.transform.CompileStatic
@@ -110,6 +108,26 @@ class LambdaTest extends GroovyTestCase {
         '''
     }
 
+    // GROOVY-8917: Failed to infer parameter type of some SAM, e.g. 
BinaryOperator
+    void testBinaryOperatorWithoutExplicitTypeDef() {
+        assertScript '''
+        import groovy.transform.CompileStatic
+        import java.util.stream.Collectors
+        import java.util.stream.Stream
+        
+        @CompileStatic
+        public class Test1 {
+            public static void main(String[] args) {
+                p();
+            }
+            
+            public static void p() {
+                assert 13 == [1, 2, 3].stream().reduce(7, (r, e) -> r + e);
+            }
+        }
+        '''
+    }
+
     void testConsumer() {
         assertScript '''
         import groovy.transform.CompileStatic

http://git-wip-us.apache.org/repos/asf/groovy/blob/1bd305bf/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy 
b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
index cf2fb49..27fcff7 100644
--- a/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
+++ b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
@@ -19,11 +19,14 @@
 
 package org.codehaus.groovy.ast.tools
 
+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 java.util.function.BiFunction
+
 class GenericsUtilsTest extends GroovyTestCase {
     void testFindParameterizedType1() {
         def code = '''
@@ -39,7 +42,7 @@ class GenericsUtilsTest extends GroovyTestCase {
         ClassNode genericsClass = findClassNode('Base', classNodeList)
         ClassNode actualReceiver = findClassNode('Derived', classNodeList)
 
-        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
+        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
         assert parameterizedClass.isUsingGenerics()
         assert 'Base' == parameterizedClass.name
         GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
@@ -64,7 +67,7 @@ class GenericsUtilsTest extends GroovyTestCase {
         ClassNode genericsClass = findClassNode('Base', classNodeList)
         ClassNode actualReceiver = findClassNode('Derived', classNodeList)
 
-        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
+        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
         assert parameterizedClass.isUsingGenerics()
         assert 'Base' == parameterizedClass.name
         GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
@@ -90,7 +93,7 @@ class GenericsUtilsTest extends GroovyTestCase {
         ClassNode genericsClass = findClassNode('Base', classNodeList)
         ClassNode actualReceiver = findClassNode('Derived', classNodeList)
 
-        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
+        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
         assert parameterizedClass.isUsingGenerics()
         assert 'Base' == parameterizedClass.name
         GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
@@ -115,7 +118,7 @@ class GenericsUtilsTest extends GroovyTestCase {
         ClassNode genericsClass = findClassNode('Base', classNodeList)
         ClassNode actualReceiver = findClassNode('Derived', classNodeList)
 
-        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
+        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
         assert parameterizedClass.isUsingGenerics()
         assert 'Base' == parameterizedClass.name
         GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
@@ -141,7 +144,7 @@ class GenericsUtilsTest extends GroovyTestCase {
         ClassNode genericsClass = findClassNode('Base', classNodeList)
         ClassNode actualReceiver = findClassNode('Derived', classNodeList)
 
-        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
+        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
         assert parameterizedClass.isUsingGenerics()
         assert 'Base' == parameterizedClass.name
         GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
@@ -168,7 +171,7 @@ class GenericsUtilsTest extends GroovyTestCase {
         ClassNode genericsClass = findClassNode('Base', classNodeList)
         ClassNode actualReceiver = findClassNode('Derived', classNodeList)
 
-        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
+        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
         assert parameterizedClass.isUsingGenerics()
         assert 'Base' == parameterizedClass.name
         GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
@@ -196,7 +199,7 @@ class GenericsUtilsTest extends GroovyTestCase {
         ClassNode genericsClass = findClassNode('Base', classNodeList)
         ClassNode actualReceiver = findClassNode('Derived', classNodeList)
 
-        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver)
+        ClassNode parameterizedClass = 
GenericsUtils.findParameterizedType(genericsClass, actualReceiver, false)
         assert parameterizedClass.isUsingGenerics()
         assert 'Base' == parameterizedClass.name
         GenericsType[] genericsTypes = parameterizedClass.getGenericsTypes()
@@ -206,6 +209,74 @@ class GenericsUtilsTest extends GroovyTestCase {
         assert genericsClass.is(parameterizedClass.redirect())
     }
 
+    void testMakeDeclaringAndActualGenericsTypeMapOfExactType() {
+        def code = '''
+        import java.util.function.*
+        interface Derived extends BinaryOperator<Integer> {}
+        '''
+        def ast = new CompilationUnit().tap {
+            addSource 'hello.groovy', code
+            compile Phases.SEMANTIC_ANALYSIS
+        }.ast
+
+        def classNodeList = ast.getModules()[0].getClasses()
+        ClassNode genericsClass = 
ClassHelper.makeWithoutCaching(BiFunction.class)
+        ClassNode actualReceiver = findClassNode('Derived', classNodeList)
+
+        Map<GenericsType, GenericsType> m = 
GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(genericsClass, 
actualReceiver)
+
+        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
+    }
+
+    void testMakeDeclaringAndActualGenericsTypeMapOfExactType2() {
+        def code = '''
+        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
+
+        def classNodeList = ast.getModules()[0].getClasses()
+        ClassNode genericsClass = findClassNode('IBase', classNodeList)
+        ClassNode actualReceiver = findClassNode('Derived', classNodeList)
+
+        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
+    }
+
+    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 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
+    }
+
     static ClassNode findClassNode(String name, List<ClassNode> classNodeList) 
{
         return classNodeList.find { it.name == name }
     }

Reply via email to