You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2018/05/11 02:12:26 UTC

groovy git commit: Extract util method `findParameterizedType` and change some error messages

Repository: groovy
Updated Branches:
  refs/heads/master 936a7a9fc -> 62f5ce6ff


Extract util method `findParameterizedType` and change some error messages


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

Branch: refs/heads/master
Commit: 62f5ce6ffe8d0dad30808944201723e02d3666fd
Parents: 936a7a9
Author: sunlan <su...@apache.org>
Authored: Fri May 11 10:11:52 2018 +0800
Committer: sunlan <su...@apache.org>
Committed: Fri May 11 10:12:19 2018 +0800

----------------------------------------------------------------------
 .../groovy/ast/tools/GenericsUtils.java         |  65 ++++++
 .../stc/StaticTypeCheckingSupport.java          |  55 +----
 .../groovy/transform/stc/GenericsSTCTest.groovy |  10 +-
 .../groovy/ast/tools/GenericsUtilsTest.groovy   | 212 +++++++++++++++++++
 4 files changed, 288 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/62f5ce6f/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 31f6c6e..98057d0 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -43,6 +43,7 @@ import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
@@ -649,4 +650,68 @@ public class GenericsUtils {
         }
         return newTypes;
     }
+
+    /**
+     * Get the parameterized type by search the whole class hierarchy according to generics class and actual receiver
+     *
+     * @param genericsClass the generics class
+     * @param actualReceiver the actual receiver
+     * @return the parameterized type
+     */
+    public static ClassNode findParameterizedType(ClassNode genericsClass, ClassNode actualReceiver) {
+        ClassNode parameterizedType = null;
+
+        if (null == genericsClass.getGenericsTypes()) {
+            return parameterizedType;
+        }
+
+        GenericsType[] declaringGenericsTypes = genericsClass.getGenericsTypes();
+
+        List<ClassNode> classNodeList = new LinkedList<>(getAllSuperClassesAndInterfaces(actualReceiver));
+        classNodeList.add(0, actualReceiver);
+
+        for (ClassNode cn : classNodeList) {
+            if (cn == genericsClass) {
+                continue;
+            }
+
+            if (!genericsClass.equals(cn.redirect())) {
+                continue;
+            }
+
+            if (isGenericsTypeArraysLengthEqual(declaringGenericsTypes, cn.getGenericsTypes())) {
+                parameterizedType = cn;
+                break;
+            }
+        }
+
+        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;
+    }
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/62f5ce6f/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index fa924a6..314473a 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -20,7 +20,6 @@
 package org.codehaus.groovy.transform.stc;
 
 import org.codehaus.groovy.GroovyBugError;
-import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.GenericsType;
 import org.codehaus.groovy.ast.MethodNode;
@@ -62,6 +61,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -1216,32 +1216,16 @@ public abstract class StaticTypeCheckingSupport {
      * so we need actual types:  T: String, S: Long
      */
     private static Map<GenericsType, GenericsType> makeDeclaringAndActualGenericsTypeMap(ClassNode declaringClass, ClassNode actualReceiver) {
-        GenericsType[] declaringGenericsTypes = declaringClass.getGenericsTypes();
-        GenericsType[] actualGenericsTypes = actualReceiver.getGenericsTypes();
+        ClassNode parameterizedType = GenericsUtils.findParameterizedType(declaringClass, actualReceiver);
 
-        if (null == declaringGenericsTypes) {
+        if (null == parameterizedType) {
             return Collections.emptyMap();
         }
 
-        if (null == actualGenericsTypes) {
-            List<ClassNode> superClassAndInterfaceList = getAllSuperClassesAndInterfaces(actualReceiver);
-
-            for (ClassNode cn : superClassAndInterfaceList) {
-                if (declaringClass.equals(cn.redirect())) {
-                    actualGenericsTypes = cn.getGenericsTypes();
-
-                    if (isGenericsTypeArraysLengthEqual(declaringGenericsTypes, actualGenericsTypes)) {
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (!isGenericsTypeArraysLengthEqual(declaringGenericsTypes, actualGenericsTypes)) {
-            return Collections.emptyMap();
-        }
+        GenericsType[] declaringGenericsTypes = declaringClass.getGenericsTypes();
+        GenericsType[] actualGenericsTypes = parameterizedType.getGenericsTypes();
 
-        Map<GenericsType, GenericsType> result = new HashMap<>();
+        Map<GenericsType, GenericsType> result = new LinkedHashMap<>();
         for (int i = 0, n = declaringGenericsTypes.length; i < n; i++) {
             result.put(declaringGenericsTypes[i], actualGenericsTypes[i]);
         }
@@ -1249,33 +1233,6 @@ public abstract class StaticTypeCheckingSupport {
         return result;
     }
 
-    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;
-    }
-
     private static Parameter[] makeRawTypes(Parameter[] params, Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) {
         Parameter[] newParam = new Parameter[params.length];
         for (int i = 0; i < params.length; i++) {

http://git-wip-us.apache.org/repos/asf/groovy/blob/62f5ce6f/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 9c42e3e..3a89bcb 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -116,14 +116,14 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<Integer> list = new LinkedList<>()
             list.add 'Hello'
-        ''', '[Static type checking] - Cannot find matching method java.util.LinkedList#add(java.lang.String)'
+        ''', '[Static type checking] - Cannot call java.util.LinkedList <java.lang.Integer>#add(java.lang.Integer) with arguments [java.lang.String]'
     }
 
     void testAddOnListWithDiamondAndWrongTypeUsingLeftShift() {
         shouldFailWithMessages '''
             List<Integer> list = new LinkedList<>()
             list << 'Hello'
-        ''', '[Static type checking] - Cannot find matching method java.util.LinkedList#leftShift(java.lang.String)'
+        ''', '[Static type checking] - Cannot call <T> java.util.LinkedList <java.lang.Integer>#leftShift(T) with arguments [java.lang.String]'
     }
 
     void testAddOnListWithDiamondAndNullUsingLeftShift() {
@@ -424,7 +424,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             Map<String, Integer> map = new HashMap<String,Integer>()
             map.put('hello', new Object())
-        ''', '[Static type checking] - Cannot find matching method java.util.HashMap#put(java.lang.String, java.lang.Object)'
+        ''', '[Static type checking] - Cannot call java.util.HashMap <String, Integer>#put(java.lang.String, java.lang.Integer) with arguments [java.lang.String, java.lang.Object]'
     }
 
     void testPutMethodWithPrimitiveValueAndArrayPut() {
@@ -749,7 +749,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             })
             Map<Date, Date> map = new HashMap<>()
             map.put('foo', new Date())
-        ''', '[Static type checking] - Cannot find matching method java.util.HashMap#put(java.lang.String, java.util.Date)'
+        ''', '[Static type checking] - Cannot call java.util.HashMap <java.util.Date, java.util.Date>#put(java.util.Date, java.util.Date) with arguments [java.lang.String, java.util.Date]'
     }
     void testInferDiamondForAssignmentWithDatesAndIllegalKeyUsingSquareBracket() {
         shouldFailWithMessages '''
@@ -787,7 +787,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             })
             Map<Date, Date> map = new HashMap<>()
             map.put(new Date(), 'foo')
-        ''', '[Static type checking] - Cannot find matching method java.util.HashMap#put(java.util.Date, java.lang.String)'
+        ''', '[Static type checking] - Cannot call java.util.HashMap <java.util.Date, java.util.Date>#put(java.util.Date, java.util.Date) with arguments [java.util.Date, java.lang.String]'
     }
     void testInferDiamondForAssignmentWithDatesAndIllegalValueUsingSquareBracket() {
         shouldFailWithMessages '''

http://git-wip-us.apache.org/repos/asf/groovy/blob/62f5ce6f/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
new file mode 100644
index 0000000..cf2fb49
--- /dev/null
+++ b/src/test/org/codehaus/groovy/ast/tools/GenericsUtilsTest.groovy
@@ -0,0 +1,212 @@
+/*
+ *  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 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
+
+class GenericsUtilsTest extends GroovyTestCase {
+    void testFindParameterizedType1() {
+        def code = '''
+        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())
+    }
+
+    void testFindParameterizedType2() {
+        def code = '''
+        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())
+    }
+
+    void testFindParameterizedType3() {
+        def code = '''
+        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())
+    }
+
+    void testFindParameterizedType4() {
+        def code = '''
+        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())
+    }
+
+    void testFindParameterizedType5() {
+        def code = '''
+        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())
+    }
+
+    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 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())
+    }
+
+    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 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())
+    }
+
+    static ClassNode findClassNode(String name, List<ClassNode> classNodeList) {
+        return classNodeList.find { it.name == name }
+    }
+}