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 2019/12/16 03:04:25 UTC

[groovy] branch GROOVY_3_0_X updated (3059452 -> 0041f71)

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a change to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git.


    from 3059452  Tweak Java9 vmplugin
     new d5190da  remove inner class
     new 01e484e  minor edits
     new 7902193  add test case
     new 0041f71  add test cases and other minor edits

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/codehaus/groovy/ast/GenericsType.java | 531 ++++++++++-----------
 src/test/groovy/transform/stc/LambdaTest.groovy    | 314 ++++++------
 2 files changed, 417 insertions(+), 428 deletions(-)


[groovy] 04/04: add test cases and other minor edits

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 0041f7122c94bdc75fa7f9f3c53deb05ac985ff8
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Dec 15 12:17:55 2019 -0600

    add test cases and other minor edits
    
    (cherry picked from commit fee93c27c34d3200b23f3e3b306ff29fa2beb825)
---
 src/test/groovy/transform/stc/LambdaTest.groovy | 210 +++++++++++++-----------
 1 file changed, 110 insertions(+), 100 deletions(-)

diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index 9485be3..48cf528 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -165,18 +165,31 @@ final class LambdaTest {
     @Test
     void testConsumer() {
         assertScript '''
-            import groovy.transform.CompileStatic
-            import java.util.stream.Collectors
-            import java.util.stream.Stream
+            @groovy.transform.CompileStatic
+            class Test1 {
+                static main(args) {
+                    p()
+                }
 
-            @CompileStatic
-            public class Test1 {
-                public static void main(String[] args) {
-                    p();
+                static void p() {
+                    [1, 2, 3].stream().forEach(e -> { System.out.println(e + 1); })
                 }
+            }
+        '''
+    }
 
-                public static void p() {
-                    [1, 2, 3].stream().forEach(e -> { System.out.println(e + 1); });
+    @Test @NotYetImplemented // GROOVY-9340
+    void testConsumerWithSelfType() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            class Test1 {
+                static main(args) {
+                    p()
+                }
+
+                static void p() {
+                    java.util.function.Consumer<Test1> c = t -> null
+                    c.accept(this.newInstance())
                 }
             }
         '''
@@ -630,103 +643,106 @@ final class LambdaTest {
     }
 
     @Test
-    void testConsumerCall() {
+    void testConsumer1() {
         assertScript '''
-            import groovy.transform.CompileStatic
-            import java.util.stream.Collectors
-            import java.util.stream.Stream
-            import java.util.function.Consumer
-
-            @CompileStatic
-            public class Test1 {
-                public static void main(String[] args) {
-                    p();
-                }
+            @groovy.transform.CompileStatic
+            void m() {
+                int a = 1
+                java.util.function.Consumer<Integer> c = i -> { a += i }
+                c.accept(2)
+                assert a == 3
+            }
+            m()
+        '''
+    }
 
-                public static void p() {
-                    int r = 1
-                    Consumer<Integer> c = (Integer e) -> { r += e }
-                    c(2)
-                    assert 3 == r
-                }
+    @Test
+    void testConsumer2() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            void m() {
+                int a = 1
+                java.util.function.Consumer<Integer> c = (i) -> { a += i }
+                c.accept(2)
+                assert a == 3
             }
+            m()
         '''
     }
 
     @Test
-    void testConsumerCallWithoutExplicitTypeDef() {
+    void testConsumer3() {
         assertScript '''
-            import groovy.transform.CompileStatic
-            import java.util.stream.Collectors
-            import java.util.stream.Stream
-            import java.util.function.Consumer
+            @groovy.transform.CompileStatic
+            void m() {
+                int a = 1
+                java.util.function.Consumer<Integer> c = (Integer i) -> { a += i }
+                c.accept(2)
+                assert a == 3
+            }
+            m()
+        '''
+    }
 
-            @CompileStatic
-            public class Test1 {
-                public static void main(String[] args) {
-                    p();
+    @Test
+    void testConsumer4() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            class Test1 {
+                static main(args) {
+                    p()
                 }
 
-                public static void p() {
-                    int r = 1
-                    Consumer<Integer> c = e -> { r += e }
-                    c(2)
-                    assert 3 == r
+                static void p() {
+                    int a = 1
+                    java.util.function.Consumer<Integer> c = e -> { a += e }
+                    c.accept(2)
+                    assert a == 3
                 }
             }
         '''
     }
 
     @Test
-    void testConsumerCall2() {
+    void testConsumer5() {
         assertScript '''
-            import groovy.transform.CompileStatic
-            import java.util.stream.Collectors
-            import java.util.stream.Stream
-            import java.util.function.Consumer
-
-            @CompileStatic
-            public class Test1 {
-                public static void main(String[] args) {
-                    new Test1().p();
+            @groovy.transform.CompileStatic
+            class Test1 {
+                static main(args) {
+                    new Test1().p()
                 }
 
-                public void p() {
-                    int r = 1
-                    Consumer<Integer> c = (Integer e) -> { r += e }
-                    c(2)
-                    assert 3 == r
+                void p() {
+                    int a = 1
+                    java.util.function.Consumer<Integer> c = (Integer e) -> { a += e }
+                    c.accept(2)
+                    assert a == 3
                 }
             }
         '''
     }
 
     @Test
-    void testConsumerCall3() {
+    void testConsumer6() {
         assertScript '''
-            import groovy.transform.CompileStatic
-            import java.util.stream.Collectors
-            import java.util.stream.Stream
-            import java.util.function.Consumer
-
-            @CompileStatic
-            public class Test1 {
-                public static void main(String[] args) {
-                    p();
+            @groovy.transform.CompileStatic
+            class Test1 {
+                static main(args) {
+                    p()
                 }
 
-                public static void p() {
-                    int r = 1
-                    Consumer<Integer> c = (Integer e) -> { r += e }
-                    c.accept(2)
-                    assert 3 == r
+                static void p() {
+                    int a = 1
+                    java.util.function.Consumer<Integer> c = (Integer e) -> { a += e }
+                    c(2)
+                    assert a == 3
                 }
             }
         '''
     }
 
     @Test
-    void testSamCall() {
+    void testFunctionalInterface1() {
         assertScript '''
             import groovy.transform.CompileStatic
             import java.util.stream.Collectors
@@ -752,7 +768,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testSamCallWithoutExplicitTypeDef() {
+    void testFunctionalInterface2() {
         assertScript '''
             import groovy.transform.CompileStatic
             import java.util.stream.Collectors
@@ -778,7 +794,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testSamCall2() {
+    void testFunctionalInterface3() {
         assertScript '''
             import groovy.transform.CompileStatic
             import java.util.stream.Collectors
@@ -1050,18 +1066,15 @@ final class LambdaTest {
         '''
     }
 
-    @Test @NotYetImplemented
+    @Test @NotYetImplemented // GROOVY-9342
     void testStaticInitializeBlocks2() {
         assertScript '''
             @groovy.transform.CompileStatic
             class Test1 {
-                static list
                 static int acc = 1
-                static { list = [1, 2, 3].stream().map(e -> acc += e).toList() }
+                static { [1, 2, 3].forEach(e -> acc += e) }
             }
-
-            assert [2, 4, 7] == Test1.list
-            assert 7 == Test1.acc
+            assert Test1.acc == 7
         '''
     }
 
@@ -1148,13 +1161,12 @@ final class LambdaTest {
             @groovy.transform.CompileStatic
             class Test1 {
                 def p() {
-                        def out = new ByteArrayOutputStream()
-                        out.withObjectOutputStream {
-                            SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
-                            it.writeObject(f)
-                        }
-
-                        return out.toByteArray()
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    return out.toByteArray()
                 }
             }
 
@@ -1170,13 +1182,12 @@ final class LambdaTest {
             @groovy.transform.CompileStatic
             class Test1 {
                 def p() {
-                        def out = new ByteArrayOutputStream()
-                        out.withObjectOutputStream {
-                            Function<Integer, String> f = ((Integer e) -> 'a' + e)
-                            it.writeObject(f)
-                        }
-
-                        return out.toByteArray()
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        Function<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    return out.toByteArray()
                 }
             }
 
@@ -1195,13 +1206,12 @@ final class LambdaTest {
             @groovy.transform.CompileStatic
             class Test1 {
                 byte[] p() {
-                        def out = new ByteArrayOutputStream()
-                        out.withObjectOutputStream {
-                            SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
-                            it.writeObject(f)
-                        }
-
-                        return out.toByteArray()
+                    def out = new ByteArrayOutputStream()
+                    out.withObjectOutputStream {
+                        SerializableFunction<Integer, String> f = ((Integer e) -> 'a' + e)
+                        it.writeObject(f)
+                    }
+                    return out.toByteArray()
                 }
 
                 static void main(String[] args) {


[groovy] 03/04: add test case

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 790219388a4e58a0cc300d1352c9decda9da85ba
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Dec 15 11:58:04 2019 -0600

    add test case
    
    (cherry picked from commit e0e132183f328a328b5082c30244384fc8347ec6)
---
 src/test/groovy/transform/stc/LambdaTest.groovy | 104 +++++++++++++-----------
 1 file changed, 57 insertions(+), 47 deletions(-)

diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index d0e1259..9485be3 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -1066,70 +1066,80 @@ final class LambdaTest {
     }
 
     @Test
-    void testAccessingThis() {
+    void testAccessingThis1() {
         assertScript '''
-            import groovy.transform.CompileStatic
-            import java.util.function.Predicate
-
-            @CompileStatic
-            public class Test4 {
-                public static void main(String[] args) {
-                    new Test4().p();
-                }
-
-                public Test4 thisVar = this;
-
-                public void p() {
-                    Predicate<Test4> predicate = (Test4 s) -> { this === s }
+            @groovy.transform.CompileStatic
+            class ThisTest {
+                private final ThisTest that = this
 
-                    assert predicate.test(thisVar)
+                void m() {
+                    java.util.function.Predicate<ThisTest> p = (ThisTest t) -> {
+                        assert this === t
+                    }
+                    p.test(that)
+                    p.test(this)
                 }
             }
+            new ThisTest().m()
         '''
     }
 
-    @Test
+    @Test @NotYetImplemented
     void testAccessingThis2() {
         assertScript '''
-            import groovy.transform.CompileStatic
-
-            @CompileStatic
-            public class Test4 {
-                public static void main(String[] args) {
-                    new Test4().p();
-                }
-
-                public String a = "a";
+            @groovy.transform.CompileStatic
+            class ThisTest {
+                private final ThisTest that = this
 
-                public void p() {
-                    assert ['a1', 'a2'] == [1, 2].stream().map(e -> this.a + e).toList();
+                void m() {
+                    java.util.function.Predicate<ThisTest> p1 = (ThisTest t1) -> {
+                        java.util.function.Predicate<ThisTest> p2 = (ThisTest t2) -> {
+                            assert this === t1 && this === t2
+                        }
+                        p2.test(t1)
+                    }
+                    p1.test(that)
+                    p1.test(this)
                 }
             }
+            new ThisTest().m()
         '''
     }
 
     @Test
     void testAccessingThis3() {
         assertScript '''
-            import groovy.transform.CompileStatic
+            @groovy.transform.CompileStatic
+            class ThisTest {
+                String p = 'a'
 
-            @CompileStatic
-            public class Test4 {
-                public static void main(String[] args) {
-                    new Test4().p();
+                void m() {
+                    def list = [1, 2].stream().map(e -> this.p + e).toList()
+                    assert list == ['a1', 'a2']
                 }
+            }
+            new ThisTest().m()
+        '''
+    }
 
-                public String a() { "a" }
+    @Test
+    void testAccessingThis4() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            class ThisTest {
+                String getP() { 'a' }
 
-                public void p() {
-                    assert ['a1', 'a2'] == [1, 2].stream().map(e -> this.a() + e).toList();
+                void m() {
+                    def list = [1, 2].stream().map(e -> this.p + e).toList()
+                    assert list == ['a1', 'a2']
                 }
             }
+            new ThisTest().m()
         '''
     }
 
     @Test
-    void testSerialize() {
+    void testSerialize1() {
         assertScript '''
             import java.util.function.Function
 
@@ -1153,7 +1163,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testSerializeFailed() {
+    void testSerialize2() {
         def err = shouldFail NotSerializableException, '''
             import java.util.function.Function
 
@@ -1177,7 +1187,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserialize() {
+    void testDeserialize1() {
         assertScript '''
             package tests.lambda
             import java.util.function.Function
@@ -1207,7 +1217,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserializeLambdaInInitializeBlock() {
+    void testDeserialize1_InitializeBlock() {
         assertScript '''
             package tests.lambda
             import java.util.function.Function
@@ -1244,7 +1254,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserializeLambdaInInitializeBlockShouldFail() {
+    void testDeserialize1_InitializeBlockShouldFail() {
         def err = shouldFail NotSerializableException, '''
             package tests.lambda
             import java.util.function.Function
@@ -1401,7 +1411,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserialize6InstanceFields() {
+    void testDeserialize6_InstanceFields() {
         assertScript '''
             package tests.lambda
             import java.util.function.Function
@@ -1434,7 +1444,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserialize6InstanceFieldsShouldFail() {
+    void testDeserialize6_InstanceFieldsShouldFail() {
         def err = shouldFail NotSerializableException, '''
             package tests.lambda
             import java.util.function.Function
@@ -1468,7 +1478,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserialize6InstanceMethods() {
+    void testDeserialize6_InstanceMethods() {
         assertScript '''
             package tests.lambda
             import java.util.function.Function
@@ -1501,7 +1511,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserialize6InstanceMethodsShouldFail() {
+    void testDeserialize6_InstanceMethodsShouldFail() {
         def err = shouldFail NotSerializableException, '''
             package tests.lambda
             import java.util.function.Function
@@ -1535,7 +1545,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserialize7StaticFields() {
+    void testDeserialize7_StaticFields() {
         assertScript '''
             package tests.lambda
             import java.util.function.Function
@@ -1566,7 +1576,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserialize7StaticMethods() {
+    void testDeserialize7_StaticMethods() {
         assertScript '''
             package tests.lambda
             import java.util.function.Function
@@ -1597,7 +1607,7 @@ final class LambdaTest {
     }
 
     @Test
-    void testDeserializeNestedLambda() {
+    void testDeserializeNestedLambda1() {
         assertScript '''
             import java.util.function.Function
 


[groovy] 02/04: minor edits

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 01e484e2f3a97ecc42cef56e2575b94897ab9986
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Dec 14 13:04:33 2019 -0600

    minor edits
    
    (cherry picked from commit 51b14ea4a07f2640f6e8d1d31810ce8c9d24d714)
---
 src/main/java/org/codehaus/groovy/ast/GenericsType.java | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index ed0d938..d63e59f 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -285,20 +285,15 @@ public class GenericsType extends ASTNode {
     }
 
     /**
-     * Iterates over each generics bound of this generics specification, and checks
-     * that the generics defined by the bound are compatible with the generics specified
-     * by the type.
-     * @param classNode the classnode the bounds should be compared with
-     * @return true if generics from bounds are compatible
+     * Compares the bounds of this generics specification against the given type
+     * for compatibility.  Ex: String would satisfy &lt;? extends CharSequence>.
      */
     private boolean checkGenerics(final ClassNode classNode) {
         ClassNode lowerBound = getLowerBound();
-        ClassNode[] upperBounds = getUpperBounds();
         if (lowerBound != null) {
-            if (!lowerBound.redirect().isUsingGenerics()) {
-                return compareGenericsWithBound(classNode, lowerBound);
-            }
+            return compareGenericsWithBound(classNode, lowerBound);
         }
+        ClassNode[] upperBounds = getUpperBounds();
         if (upperBounds != null) {
             for (ClassNode upperBound : upperBounds) {
                 if (!compareGenericsWithBound(classNode, upperBound)) {


[groovy] 01/04: remove inner class

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit d5190da43d726190fb6936a954ea7f0eed862789
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Dec 14 12:15:39 2019 -0600

    remove inner class
    
    (cherry picked from commit 0d7e447b05c4ed60b459ecfc74d0d2abdf0b9915)
---
 .../java/org/codehaus/groovy/ast/GenericsType.java | 536 ++++++++++-----------
 1 file changed, 255 insertions(+), 281 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index 2841afe..ed0d938 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -185,335 +185,309 @@ public class GenericsType extends ASTNode {
         return upperBounds;
     }
 
-    /**
-     * If you have a class which extends a class using generics, returns the superclass with parameterized types. For
-     * example, if you have:
-     * <code>class MyList&lt;T&gt; extends LinkedList&lt;T&gt;
-     * def list = new MyList&lt;String&gt;
-     * </code>
-     * then the parameterized superclass for MyList&lt;String&gt; is LinkedList&lt;String&gt;
-     * @param classNode the class for which we want to return the parameterized superclass
-     * @return the parameterized superclass
-     */
-    private static ClassNode getParameterizedSuperClass(final ClassNode classNode) {
-        if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null;
-        ClassNode superClass = classNode.getUnresolvedSuperClass();
-        if (superClass == null) return ClassHelper.OBJECT_TYPE;
-
-        if (!classNode.isUsingGenerics() || !superClass.isUsingGenerics()) {
-            return superClass;
-        }
-
-        GenericsType[] genericsTypes = classNode.getGenericsTypes();
-        GenericsType[] redirectGenericTypes = classNode.redirect().getGenericsTypes();
-        superClass = superClass.getPlainNodeReference();
-        if (genericsTypes == null || redirectGenericTypes == null || superClass.getGenericsTypes() == null) {
-            return superClass;
-        }
-        for (int i = 0, genericsTypesLength = genericsTypes.length; i < genericsTypesLength; i += 1) {
-            if (redirectGenericTypes[i].isPlaceholder()) {
-                GenericsType genericsType = genericsTypes[i];
-                GenericsType[] superGenericTypes = superClass.getGenericsTypes();
-                for (int j = 0, superGenericTypesLength = superGenericTypes.length; j < superGenericTypesLength; j += 1) {
-                    final GenericsType superGenericType = superGenericTypes[j];
-                    if (superGenericType.isPlaceholder() && superGenericType.getName().equals(redirectGenericTypes[i].getName())) {
-                        superGenericTypes[j] = genericsType;
-                    }
-                }
-            }
-        }
-        return superClass;
-    }
+    //--------------------------------------------------------------------------
 
     /**
-     * Tells if the provided class node is compatible with this generic type definition
-     * @param classNode the class node to be checked
-     * @return true if the class node is compatible with this generics type definition
+     * Compares this generics type with the provided class node. If the provided
+     * class node is compatible with the generics specification, returns true.
+     * Otherwise, returns false. The check is complete, meaning that nested
+     * generics are also checked.
+     *
+     * @return if {@code classNode} is or is not compatible with this generics specification
      */
     public boolean isCompatibleWith(final ClassNode classNode) {
-        return new GenericsTypeMatcher().matches(classNode);
-    }
-
-    /**
-     * Implements generics type comparison.
-     */
-    private class GenericsTypeMatcher {
-
-        public boolean implementsInterfaceOrIsSubclassOf(final ClassNode type, final ClassNode superOrInterface) {
-            boolean result = type.equals(superOrInterface)
-                    || type.isDerivedFrom(superOrInterface)
-                    || type.implementsInterface(superOrInterface);
-            if (result) {
-                return true;
-            }
-            if (ClassHelper.GROOVY_OBJECT_TYPE.equals(superOrInterface) && type.getCompileUnit() != null) {
-                // type is being compiled so it will implement GroovyObject later
-                return true;
-            }
-            if (superOrInterface instanceof WideningCategories.LowestUpperBoundClassNode) {
-                WideningCategories.LowestUpperBoundClassNode cn = (WideningCategories.LowestUpperBoundClassNode) superOrInterface;
-                result = implementsInterfaceOrIsSubclassOf(type, cn.getSuperClass());
-                if (result) {
-                    for (ClassNode interfaceNode : cn.getInterfaces()) {
-                        result = implementsInterfaceOrIsSubclassOf(type, interfaceNode);
-                        if (!result) break;
-                    }
-                }
-                if (result) return true;
-            }
-            if (type.isArray() && superOrInterface.isArray()) {
-                return implementsInterfaceOrIsSubclassOf(type.getComponentType(), superOrInterface.getComponentType());
-            }
-            return false;
+        GenericsType[] genericsTypes = classNode.getGenericsTypes();
+        if (genericsTypes != null && genericsTypes.length == 0) {
+            return true; // diamond always matches
         }
-
-        /**
-         * Compares this generics type with the one represented by the provided class node. If the provided
-         * classnode is compatible with the generics specification, returns true. Otherwise, returns false.
-         * The check is complete, meaning that we also check "nested" generics.
-         * @param classNode the classnode to be checked
-         * @return true iff the classnode is compatible with this generics specification
-         */
-        public boolean matches(final ClassNode classNode) {
-            GenericsType[] genericsTypes = classNode.getGenericsTypes();
-            if (genericsTypes != null && genericsTypes.length == 0) {
-                return true; // diamond always matches
-            }
-            if (classNode.isGenericsPlaceHolder()) {
-                // if the classnode we compare to is a generics placeholder (like <E>) then we
-                // only need to check that the names are equal
-                if (genericsTypes == null) {
-                    return true;
-                }
-                if (isWildcard()) {
-                    if (getLowerBound() != null) {
-                        ClassNode lowerBound = getLowerBound();
-                        return genericsTypes[0].name.equals(lowerBound.getUnresolvedName());
-                    }
-                    if (getUpperBounds() != null) {
-                        for (ClassNode upperBound : getUpperBounds()) {
-                            if (genericsTypes[0].name.equals(upperBound.getUnresolvedName())) {
-                                return true;
-                            }
-                        }
-                        return false;
-                    }
-                }
-                return genericsTypes[0].name.equals(name);
+        if (classNode.isGenericsPlaceHolder()) {
+            // if the classnode we compare to is a generics placeholder (like <E>) then we
+            // only need to check that the names are equal
+            if (genericsTypes == null) {
+                return true;
             }
-            if (isWildcard() || isPlaceholder()) {
-                ClassNode lowerBound = getLowerBound();
-                ClassNode[] upperBounds = getUpperBounds();
-                // if the current generics spec is a wildcard spec or a placeholder spec
-                // then we must check lower and upper bounds
-                if (lowerBound != null) {
-                    // if a lower bound is declared, then we must perform the same checks that for an upper bound
-                    // but with reversed arguments
-                    if (!implementsInterfaceOrIsSubclassOf(lowerBound, classNode)) {
-                        return false;
-                    }
-                    return checkGenerics(classNode);
+            if (isWildcard()) {
+                if (getLowerBound() != null) {
+                    ClassNode lowerBound = getLowerBound();
+                    return genericsTypes[0].name.equals(lowerBound.getUnresolvedName());
                 }
-                if (upperBounds != null) {
-                    // check that the provided classnode is a subclass of all provided upper bounds
-                    for (ClassNode upperBound : upperBounds) {
-                        if (!implementsInterfaceOrIsSubclassOf(classNode, upperBound)) {
-                            return false;
+                if (getUpperBounds() != null) {
+                    for (ClassNode upperBound : getUpperBounds()) {
+                        if (genericsTypes[0].name.equals(upperBound.getUnresolvedName())) {
+                            return true;
                         }
                     }
-                    // if the provided classnode is a subclass of the upper bound
-                    // then check that the generic types supplied by the class node are compatible with
-                    // this generics specification
-                    // for example, we could have the spec saying List<String> but provided classnode
-                    // saying List<Integer>
-                    return checkGenerics(classNode);
+                    return false;
                 }
-                // If there are no bounds, the generic type is basically Object, and everything is compatible.
-                return true;
             }
-            // last, we could have the spec saying List<String> and a classnode saying List<Integer> so
-            // we must check that generics are compatible.
-            // The null check is normally not required but done to prevent from NPEs
-            return getType().equals(classNode) && compareGenericsWithBound(classNode, type);
+            return genericsTypes[0].name.equals(name);
         }
-
-        /**
-         * Iterates over each generics bound of this generics specification, and checks
-         * that the generics defined by the bound are compatible with the generics specified
-         * by the type.
-         * @param classNode the classnode the bounds should be compared with
-         * @return true if generics from bounds are compatible
-         */
-        private boolean checkGenerics(final ClassNode classNode) {
+        if (isWildcard() || isPlaceholder()) {
             ClassNode lowerBound = getLowerBound();
             ClassNode[] upperBounds = getUpperBounds();
+            // if the current generics spec is a wildcard spec or a placeholder spec
+            // then we must check lower and upper bounds
             if (lowerBound != null) {
-                if (!lowerBound.redirect().isUsingGenerics()) {
-                    return compareGenericsWithBound(classNode, lowerBound);
+                // if a lower bound is declared, then we must perform the same checks that for an upper bound
+                // but with reversed arguments
+                if (!implementsInterfaceOrIsSubclassOf(lowerBound, classNode)) {
+                    return false;
                 }
+                return checkGenerics(classNode);
             }
             if (upperBounds != null) {
+                // check that the provided classnode is a subclass of all provided upper bounds
                 for (ClassNode upperBound : upperBounds) {
-                    if (!compareGenericsWithBound(classNode, upperBound)) {
+                    if (!implementsInterfaceOrIsSubclassOf(classNode, upperBound)) {
                         return false;
                     }
                 }
+                // if the provided classnode is a subclass of the upper bound
+                // then check that the generic types supplied by the class node are compatible with
+                // this generics specification
+                // for example, we could have the spec saying List<String> but provided classnode
+                // saying List<Integer>
+                return checkGenerics(classNode);
             }
+            // if there are no bounds, the generic type is basically Object, and everything is compatible
             return true;
         }
+        // last, we could have the spec saying List<String> and a classnode saying List<Integer> so
+        // we must check that generics are compatible
+        return getType().equals(classNode) && compareGenericsWithBound(classNode, type);
+    }
 
-        /**
-         * Given a parameterized type (List&lt;String&gt; for example), checks that its
-         * generic types are compatible with those from a bound.
-         * @param classNode the classnode from which we will compare generics types
-         * @param bound the bound to which the types will be compared
-         * @return true if generics are compatible
-         */
-        private boolean compareGenericsWithBound(final ClassNode classNode, final ClassNode bound) {
-            if (classNode == null) {
-                return false;
+    private static boolean implementsInterfaceOrIsSubclassOf(final ClassNode type, final ClassNode superOrInterface) {
+        if (type.equals(superOrInterface)
+                || type.isDerivedFrom(superOrInterface)
+                || type.implementsInterface(superOrInterface)) {
+            return true;
+        }
+        if (ClassHelper.GROOVY_OBJECT_TYPE.equals(superOrInterface) && type.getCompileUnit() != null) {
+            // type is being compiled so it will implement GroovyObject later
+            return true;
+        }
+        if (superOrInterface instanceof WideningCategories.LowestUpperBoundClassNode) {
+            WideningCategories.LowestUpperBoundClassNode lub = (WideningCategories.LowestUpperBoundClassNode) superOrInterface;
+            boolean result = implementsInterfaceOrIsSubclassOf(type, lub.getSuperClass());
+            if (result) {
+                for (ClassNode face : lub.getInterfaces()) {
+                    result = implementsInterfaceOrIsSubclassOf(type, face);
+                    if (!result) break;
+                }
             }
-            if (!bound.isUsingGenerics() || (classNode.getGenericsTypes() == null && classNode.redirect().getGenericsTypes() != null)) {
-                // if the bound is not using generics, there's nothing to compare with
-                return true;
+            if (result) return true;
+        }
+        if (type.isArray() && superOrInterface.isArray()) {
+            return implementsInterfaceOrIsSubclassOf(type.getComponentType(), superOrInterface.getComponentType());
+        }
+        return false;
+    }
+
+    /**
+     * Iterates over each generics bound of this generics specification, and checks
+     * that the generics defined by the bound are compatible with the generics specified
+     * by the type.
+     * @param classNode the classnode the bounds should be compared with
+     * @return true if generics from bounds are compatible
+     */
+    private boolean checkGenerics(final ClassNode classNode) {
+        ClassNode lowerBound = getLowerBound();
+        ClassNode[] upperBounds = getUpperBounds();
+        if (lowerBound != null) {
+            if (!lowerBound.redirect().isUsingGenerics()) {
+                return compareGenericsWithBound(classNode, lowerBound);
             }
-            if (!classNode.equals(bound)) {
-                 // the class nodes are on different types
-                // in this situation, we must choose the correct execution path : either the bound
-                // is an interface and we must find the implementing interface from the classnode
-                // to compare their parameterized generics, or the bound is a regular class and we
-                // must compare the bound with a superclass
-                if (bound.isInterface()) {
-                    Set<ClassNode> interfaces = classNode.getAllInterfaces();
-                    // iterate over all interfaces to check if any corresponds to the bound we are
-                    // comparing to
-                    for (ClassNode anInterface : interfaces) {
-                        if (anInterface.equals(bound)) {
-                            // when we obtain an interface, the types represented by the interface
-                            // class node are not parameterized. This means that we must create a
-                            // new class node with the parameterized types that the current class node
-                            // has defined.
-                            ClassNode node = GenericsUtils.parameterizeType(classNode, anInterface);
-                            return compareGenericsWithBound(node, bound);
-                        }
-                    }
+        }
+        if (upperBounds != null) {
+            for (ClassNode upperBound : upperBounds) {
+                if (!compareGenericsWithBound(classNode, upperBound)) {
+                    return false;
                 }
-                if (bound instanceof WideningCategories.LowestUpperBoundClassNode) {
-                    // another special case here, where the bound is a "virtual" type
-                    // we must then check the superclass and the interfaces
-                    boolean success = compareGenericsWithBound(classNode, bound.getSuperClass());
-                    if (success) {
-                        ClassNode[] interfaces = bound.getInterfaces();
-                        for (ClassNode anInterface : interfaces) {
-                            success &= compareGenericsWithBound(classNode, anInterface);
-                            if (!success) break;
-                        }
-                        if (success) return true;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Given a parameterized type (List&lt;String&gt; for example), checks that its
+     * generic types are compatible with those from a bound.
+     * @param classNode the classnode from which we will compare generics types
+     * @param bound the bound to which the types will be compared
+     * @return true if generics are compatible
+     */
+    private static boolean compareGenericsWithBound(final ClassNode classNode, final ClassNode bound) {
+        if (classNode == null) {
+            return false;
+        }
+        if (!bound.isUsingGenerics() || (classNode.getGenericsTypes() == null && classNode.redirect().getGenericsTypes() != null)) {
+            // if the bound is not using generics or the class node is a raw type, there's nothing to compare with
+            return true;
+        }
+        if (!classNode.equals(bound)) {
+             // the class nodes are on different types
+            // in this situation, we must choose the correct execution path : either the bound
+            // is an interface and we must find the implementing interface from the classnode
+            // to compare their parameterized generics, or the bound is a regular class and we
+            // must compare the bound with a superclass
+            if (bound.isInterface()) {
+                // iterate over all interfaces to check if any corresponds to the bound we are
+                // comparing to
+                for (ClassNode face : classNode.getAllInterfaces()) {
+                    if (face.equals(bound)) {
+                        // when we obtain an interface, the types represented by the interface
+                        // class node are not parameterized. This means that we must create a
+                        // new class node with the parameterized types that the current class node
+                        // has defined.
+                        ClassNode node = GenericsUtils.parameterizeType(classNode, face);
+                        return compareGenericsWithBound(node, bound);
                     }
                 }
-                return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
             }
-            GenericsType[] cnTypes = classNode.getGenericsTypes();
-            if (cnTypes == null && classNode.isRedirectNode()) {
-                cnTypes = classNode.redirect().getGenericsTypes();
-            }
-            if (cnTypes == null) {
-                // may happen if generic type is Foo<T extends Foo> and classnode is Foo -> Foo
-                return true;
+            if (bound instanceof WideningCategories.LowestUpperBoundClassNode) {
+                // another special case here, where the bound is a "virtual" type
+                // we must then check the superclass and the interfaces
+                boolean success = compareGenericsWithBound(classNode, bound.getSuperClass());
+                if (success) {
+                    for (ClassNode face : bound.getInterfaces()) {
+                        success &= compareGenericsWithBound(classNode, face);
+                        if (!success) break;
+                    }
+                    if (success) return true;
+                }
             }
-            GenericsType[] redirectBoundGenericTypes = bound.redirect().getGenericsTypes();
-            Map<GenericsTypeName, GenericsType> boundPlaceHolders = GenericsUtils.extractPlaceholders(bound);
-            Map<GenericsTypeName, GenericsType> classNodePlaceholders = GenericsUtils.extractPlaceholders(classNode);
-            boolean match = true;
-            for (int i = 0; redirectBoundGenericTypes != null && i < redirectBoundGenericTypes.length && match; i += 1) {
-                GenericsType redirectBoundType = redirectBoundGenericTypes[i];
-                GenericsType classNodeType = cnTypes[i];
-                if (classNodeType.isPlaceholder()) {
-                    GenericsTypeName name = new GenericsTypeName(classNodeType.getName());
-                    if (redirectBoundType.isPlaceholder()) {
-                        GenericsTypeName gtn = new GenericsTypeName(redirectBoundType.getName());
-                        match = name.equals(gtn);
-                        if (!match) {
-                            GenericsType genericsType = boundPlaceHolders.get(gtn);
-                            match = false;
-                            if (genericsType != null) {
-                                if (genericsType.isPlaceholder()) {
-                                    match = true;
-                                } else if (genericsType.isWildcard()) {
-                                    if (genericsType.getUpperBounds() != null) {
-                                        for (ClassNode ub : genericsType.getUpperBounds()) {
-                                            match |= redirectBoundType.isCompatibleWith(ub);
-                                        }
-                                        if (genericsType.getLowerBound() != null) {
-                                            match |= redirectBoundType.isCompatibleWith(genericsType.getLowerBound());
-                                        }
+            return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
+        }
+
+        GenericsType[] cnTypes = classNode.getGenericsTypes();
+        if (cnTypes == null) {
+            cnTypes = classNode.redirect().getGenericsTypes();
+        }
+        if (cnTypes == null) {
+            // may happen if generic type is Foo<T extends Foo> and classnode is Foo -> Foo
+            return true;
+        }
+
+        GenericsType[] redirectBoundGenericTypes = bound.redirect().getGenericsTypes();
+        Map<GenericsTypeName, GenericsType> boundPlaceHolders = GenericsUtils.extractPlaceholders(bound);
+        Map<GenericsTypeName, GenericsType> classNodePlaceholders = GenericsUtils.extractPlaceholders(classNode);
+        boolean match = true;
+        for (int i = 0; redirectBoundGenericTypes != null && i < redirectBoundGenericTypes.length && match; i += 1) {
+            GenericsType redirectBoundType = redirectBoundGenericTypes[i];
+            GenericsType classNodeType = cnTypes[i];
+            if (classNodeType.isPlaceholder()) {
+                GenericsTypeName name = new GenericsTypeName(classNodeType.getName());
+                if (redirectBoundType.isPlaceholder()) {
+                    GenericsTypeName gtn = new GenericsTypeName(redirectBoundType.getName());
+                    match = name.equals(gtn);
+                    if (!match) {
+                        GenericsType genericsType = boundPlaceHolders.get(gtn);
+                        match = false;
+                        if (genericsType != null) {
+                            if (genericsType.isPlaceholder()) {
+                                match = true;
+                            } else if (genericsType.isWildcard()) {
+                                if (genericsType.getUpperBounds() != null) {
+                                    for (ClassNode ub : genericsType.getUpperBounds()) {
+                                        match |= redirectBoundType.isCompatibleWith(ub);
+                                    }
+                                    if (genericsType.getLowerBound() != null) {
+                                        match |= redirectBoundType.isCompatibleWith(genericsType.getLowerBound());
                                     }
                                 }
                             }
                         }
-                    } else {
-                        if (classNodePlaceholders.containsKey(name))
-                            classNodeType = classNodePlaceholders.get(name);
-                        match = classNodeType.isCompatibleWith(redirectBoundType.getType());
                     }
                 } else {
-                    if (redirectBoundType.isPlaceholder()) {
-                        if (classNodeType.isPlaceholder()) {
-                            match = classNodeType.getName().equals(redirectBoundType.getName());
-                        } else {
-                            GenericsTypeName name = new GenericsTypeName(redirectBoundType.getName());
-                            if (boundPlaceHolders.containsKey(name)) {
-                                redirectBoundType = boundPlaceHolders.get(name);
-                                boolean wildcard = redirectBoundType.isWildcard();
-                                boolean placeholder = redirectBoundType.isPlaceholder();
-                                if (placeholder || wildcard) {
-                                    // placeholder aliases, like Map<U,V> -> Map<K,V>
-                                    if (wildcard) {
-                                        // ex: Comparable<Integer> <=> Comparable<? super T>
-                                        if (redirectBoundType.getLowerBound() != null) {
-                                            GenericsType gt = new GenericsType(redirectBoundType.getLowerBound());
-                                            if (gt.isPlaceholder()) {
-                                                // check for recursive generic typedef, like in
-                                                // <T extends Comparable<? super T>>
-                                                GenericsTypeName gtn = new GenericsTypeName(gt.getName());
-                                                if (classNodePlaceholders.containsKey(gtn)) {
-                                                    gt = classNodePlaceholders.get(gtn);
-                                                }
-                                            }
-                                            match = implementsInterfaceOrIsSubclassOf(gt.getType(), classNodeType.getType());
-                                        }
-                                        if (match && redirectBoundType.getUpperBounds() != null) {
-                                            for (ClassNode upperBound : redirectBoundType.getUpperBounds()) {
-                                                GenericsType gt = new GenericsType(upperBound);
-                                                if (gt.isPlaceholder()) {
-                                                    // check for recursive generic typedef, like in
-                                                    // <T extends Comparable<? super T>>
-                                                    GenericsTypeName gtn = new GenericsTypeName(gt.getName());
-                                                    if (classNodePlaceholders.containsKey(gtn)) {
-                                                        gt = classNodePlaceholders.get(gtn);
-                                                    }
-                                                }
-                                                match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType())
-                                                         || classNodeType.isCompatibleWith(gt.getType()); // workaround for GROOVY-6095
-                                                if (!match) break;
-                                            }
+                    match = classNodePlaceholders.getOrDefault(name, classNodeType).isCompatibleWith(redirectBoundType.getType());
+                }
+            } else {
+                if (redirectBoundType.isPlaceholder()) {
+                    if (classNodeType.isPlaceholder()) {
+                        match = classNodeType.getName().equals(redirectBoundType.getName());
+                    } else {
+                        GenericsTypeName name = new GenericsTypeName(redirectBoundType.getName());
+                        if (boundPlaceHolders.containsKey(name)) {
+                            redirectBoundType = boundPlaceHolders.get(name);
+                            if (redirectBoundType.isPlaceholder()) {
+                                redirectBoundType = classNodePlaceholders.getOrDefault(name, redirectBoundType);
+
+                            } else if (redirectBoundType.isWildcard()) {
+                                if (redirectBoundType.getLowerBound() != null) {
+                                    // ex: class Comparable<Integer> <=> bound Comparable<? super T>
+                                    GenericsType gt = new GenericsType(redirectBoundType.getLowerBound());
+                                    if (gt.isPlaceholder()) {
+                                        // check for recursive generic typedef, like in <T extends Comparable<? super T>>
+                                        gt = classNodePlaceholders.getOrDefault(new GenericsTypeName(gt.getName()), gt);
+                                    }
+                                    match = implementsInterfaceOrIsSubclassOf(gt.getType(), classNodeType.getType());
+
+                                } else if (redirectBoundType.getUpperBounds() != null) {
+                                    // ex: class Comparable<Integer> <=> bound Comparable<? extends T & I>
+                                    for (ClassNode upperBound : redirectBoundType.getUpperBounds()) {
+                                        GenericsType gt = new GenericsType(upperBound);
+                                        if (gt.isPlaceholder()) {
+                                            // check for recursive generic typedef, like in <T extends Comparable<? super T>>
+                                            gt = classNodePlaceholders.getOrDefault(new GenericsTypeName(gt.getName()), gt);
                                         }
-                                        return match;
-                                    } else if (classNodePlaceholders.containsKey(name)) {
-                                        redirectBoundType = classNodePlaceholders.get(name);
+                                        match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType())
+                                                || classNodeType.isCompatibleWith(gt.getType()); // workaround for GROOVY-6095
+
+                                        if (!match) break;
                                     }
                                 }
+                                return match;
                             }
-                            match = redirectBoundType.isCompatibleWith(classNodeType.getType());
                         }
-                    } else {
-                        // TODO: the check for isWildcard should be replaced with a more complete check
-                        match = redirectBoundType.isWildcard() || classNodeType.isCompatibleWith(redirectBoundType.getType());
+                        match = redirectBoundType.isCompatibleWith(classNodeType.getType());
+                    }
+                } else {
+                    // TODO: the check for isWildcard should be replaced with a more complete check
+                    match = redirectBoundType.isWildcard() || classNodeType.isCompatibleWith(redirectBoundType.getType());
+                }
+            }
+        }
+        return match;
+    }
+
+    /**
+     * If you have a class which extends a class using generics, returns the superclass with parameterized types. For
+     * example, if you have:
+     * <code>class MyList&lt;T&gt; extends LinkedList&lt;T&gt;
+     * def list = new MyList&lt;String&gt;
+     * </code>
+     * then the parameterized superclass for MyList&lt;String&gt; is LinkedList&lt;String&gt;
+     * @param classNode the class for which we want to return the parameterized superclass
+     * @return the parameterized superclass
+     */
+    private static ClassNode getParameterizedSuperClass(final ClassNode classNode) {
+        if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null;
+        ClassNode superClass = classNode.getUnresolvedSuperClass();
+        if (superClass == null) return ClassHelper.OBJECT_TYPE;
+
+        if (!classNode.isUsingGenerics() || !superClass.isUsingGenerics()) {
+            return superClass;
+        }
+
+        GenericsType[] genericsTypes = classNode.getGenericsTypes();
+        GenericsType[] redirectGenericTypes = classNode.redirect().getGenericsTypes();
+        superClass = superClass.getPlainNodeReference();
+        if (genericsTypes == null || redirectGenericTypes == null || superClass.getGenericsTypes() == null) {
+            return superClass;
+        }
+        for (int i = 0, genericsTypesLength = genericsTypes.length; i < genericsTypesLength; i += 1) {
+            if (redirectGenericTypes[i].isPlaceholder()) {
+                GenericsType genericsType = genericsTypes[i];
+                GenericsType[] superGenericTypes = superClass.getGenericsTypes();
+                for (int j = 0, superGenericTypesLength = superGenericTypes.length; j < superGenericTypesLength; j += 1) {
+                    final GenericsType superGenericType = superGenericTypes[j];
+                    if (superGenericType.isPlaceholder() && superGenericType.getName().equals(redirectGenericTypes[i].getName())) {
+                        superGenericTypes[j] = genericsType;
                     }
                 }
             }
-            return match;
         }
+        return superClass;
     }
 
     /**