You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/03/30 19:46:33 UTC

[groovy] 01/03: checking generics of super used raw type instead of parameterized type

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

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

commit 4a9624b87ebc3d374e2ceff1cbb66ab3fbb50496
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Feb 27 11:19:16 2021 -0600

    checking generics of super used raw type instead of parameterized type
    
        abstarct class A<X> {}
        class C<Y> extends A<Y> {}
        A<Z> a = new C<Z>() // was checking C<Z> compatible with A (raw)
---
 .../java/org/codehaus/groovy/ast/GenericsType.java |  119 +--
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 1056 +++++++++++++++++---
 .../asm/sc/GenericsStaticCompileTest.groovy        |    8 +-
 3 files changed, 947 insertions(+), 236 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index e689249..36d5c8c 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -22,6 +22,7 @@ import org.codehaus.groovy.ast.tools.GenericsUtils;
 import org.codehaus.groovy.ast.tools.WideningCategories;
 
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
@@ -61,6 +62,7 @@ public class GenericsType extends ASTNode {
         this.type = Objects.requireNonNull(type); // TODO: ensure type is not primitive
     }
 
+    @Override
     public String toString() {
         return toString(this, new HashSet<>());
     }
@@ -189,12 +191,8 @@ public class GenericsType extends ASTNode {
     //--------------------------------------------------------------------------
 
     /**
-     * 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
+     * Determines if the provided type is compatible with this specification.
+     * The check is complete, meaning that nested generics are also checked.
      */
     public boolean isCompatibleWith(final ClassNode classNode) {
         GenericsType[] genericsTypes = classNode.getGenericsTypes();
@@ -212,21 +210,24 @@ public class GenericsType extends ASTNode {
                 return name0.equals(getName());
             }
             if (getLowerBound() != null) {
+                // check for "? super T" vs "T"
                 if (name0.equals(getLowerBound().getUnresolvedName())) {
                     return true;
                 }
             } else if (getUpperBounds() != null) {
+                // check for "? extends T" vs "T"
                 if (name0.equals(getUpperBounds()[0].getUnresolvedName())) {
                     return true;
                 }
             }
+            // check for "? extends/super X" vs "T extends/super X"
             return checkGenerics(classNode);
         }
         if (isWildcard() || isPlaceholder()) {
             // if the generics spec is a wildcard or a placeholder then check the bounds
             ClassNode lowerBound = getLowerBound();
             if (lowerBound != null) {
-                // for a lower bound, perform the upper bound checks with reversed arguments
+                // test bound and type in reverse for lower bound vs upper bound
                 if (!implementsInterfaceOrIsSubclassOf(lowerBound, classNode)) {
                     return false;
                 }
@@ -240,19 +241,16 @@ public class GenericsType extends ASTNode {
                         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>
+                // if the provided type is a subclass of the upper bound(s) then
+                // check that the generic types supplied are compatible with this
+                // for example, this spec could be "Type<X>" but type is "Type<Y>"
                 return checkGenerics(classNode);
             }
-            // if there are no bounds, the generic type is basically Object, and everything is compatible
+            // 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);
+        // not placeholder or wildcard; no covariance allowed for type or bound(s)
+        return classNode.equals(getType()) && compareGenericsWithBound(classNode, getType());
     }
 
     private static boolean implementsInterfaceOrIsSubclassOf(final ClassNode type, final ClassNode superOrInterface) {
@@ -332,24 +330,31 @@ public class GenericsType extends ASTNode {
                         // 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);
+                        if (face.getGenericsTypes() != null) {
+                            face = GenericsUtils.parameterizeType(classNode, face);
+                        }
+                        return compareGenericsWithBound(face, bound);
                     }
                 }
             }
             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;
+                if (compareGenericsWithBound(classNode, bound.getSuperClass())
+                        && Arrays.stream(bound.getInterfaces()).allMatch(face -> compareGenericsWithBound(classNode, face))) {
+                    return true;
                 }
             }
-            return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
+            if (classNode.equals(ClassHelper.OBJECT_TYPE)) {
+                return false;
+            }
+            ClassNode superClass = classNode.getUnresolvedSuperClass();
+            if (superClass == null) {
+                superClass = ClassHelper.OBJECT_TYPE;
+            } else if (superClass.getGenericsTypes() != null) {
+                superClass = GenericsUtils.parameterizeType(classNode, superClass);
+            }
+            return compareGenericsWithBound(superClass, bound);
         }
 
         GenericsType[] cnTypes = classNode.getGenericsTypes();
@@ -357,7 +362,7 @@ public class GenericsType extends ASTNode {
             cnTypes = classNode.redirect().getGenericsTypes();
         }
         if (cnTypes == null) {
-            // may happen if generic type is Foo<T extends Foo> and classnode is Foo -> Foo
+            // may happen if generic type is Foo<T extends Foo> and ClassNode is Foo -> Foo
             return true;
         }
 
@@ -460,59 +465,19 @@ public class GenericsType extends ASTNode {
     }
 
     /**
-     * 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;
-    }
-
-    /**
-     * Represents GenericsType name
-     * TODO In order to distinguish GenericsType with same name(See GROOVY-8409), we should add a property to keep the declaring class.
-     *
-     * fixing GROOVY-8409 steps:
-     * 1) change the signature of constructor GenericsTypeName to `GenericsTypeName(String name, ClassNode declaringClass)`
-     * 2) try to fix all compilation errors(if `GenericsType` has declaringClass property, the step would be a bit easy to fix...)
-     * 3) run all tests to see whether the change breaks anything
-     * 4) if all tests pass, congratulations! but if some tests are broken, try to debug and find why...
-     *
+     * Represents {@link GenericsType} name.
+     * <p>
+     * TODO: In order to distinguish GenericsType with same name, we should add a property to keep the declaring class.
+     * <ol>
+     * <li> change the signature of constructor GenericsTypeName to `GenericsTypeName(String name, ClassNode declaringClass)`
+     * <li> try to fix all compilation errors(if `GenericsType` has declaringClass property, the step would be a bit easy to fix...)
+     * <li> run all tests to see whether the change breaks anything
+     * <li> if all tests pass, congratulations! but if some tests are broken, try to debug and find why...
+     * </ol>
      * We should find a way to set declaring class for `GenericsType` first, it can be completed at the resolving phase.
      */
     public static class GenericsTypeName {
-        private String name;
+        private final String name;
 
         public GenericsTypeName(final String name) {
             this.name = Objects.requireNonNull(name);
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 81dab95..70da3e4 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -27,36 +27,39 @@ import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
  */
 class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
-    void testDeclaration() {
+    void testDeclaration1() {
         assertScript '''
             List test = new LinkedList<String>()
         '''
     }
 
-    void testDeclaration5() {
+    void testDeclaration2() {
         assertScript '''
             Map<String,Integer> obj = new HashMap<String,Integer>()
         '''
     }
 
-    void testDeclaration6() {
+    void testDeclaration3() {
         shouldFailWithMessages '''
             Map<String,String> obj = new HashMap<String,Integer>()
-        ''', 'Incompatible generic argument types. Cannot assign java.util.HashMap <String, Integer> to: java.util.Map <String, String>'
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.HashMap <String, Integer> to: java.util.Map <String, String>'
     }
 
     void testDeclaration4() {
         // no generics checked after first wildcard
         shouldFailWithMessages '''
             Map<? extends CharSequence,String> obj = new HashMap<String,Integer>()
-        ''', 'Incompatible generic argument types. Cannot assign java.util.HashMap <String, Integer> to: java.util.Map <? extends java.lang.CharSequence, String>'
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.HashMap <String, Integer> to: java.util.Map <? extends java.lang.CharSequence, String>'
     }
 
     void testAddOnList() {
         shouldFailWithMessages '''
             List<String> list = []
             list.add(1)
-        ''', "[Static type checking] - Cannot find matching method java.util.List#add(int)"
+        ''',
+        'Cannot find matching method java.util.List#add(int)'
     }
 
     void testAddOnList2() {
@@ -82,7 +85,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<String> list = []
             list << 1
-        ''', 'Cannot call <T> java.util.List <String>#leftShift(T) with arguments [int]'
+        ''',
+        'Cannot call <T> java.util.List <String>#leftShift(T) with arguments [int]'
     }
 
     void testAddOnList2UsingLeftShift() {
@@ -130,14 +134,16 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<Integer> list = new LinkedList<>()
             list.add 'Hello'
-        ''', 'Cannot find matching method java.util.LinkedList#add(java.lang.String). Please check if the declared type is correct and if the method exists.'
+        ''',
+        'Cannot find matching method java.util.LinkedList#add(java.lang.String). Please check if the declared type is correct and if the method exists.'
     }
 
     void testAddOnListWithDiamondAndWrongTypeUsingLeftShift() {
         shouldFailWithMessages '''
             List<Integer> list = new LinkedList<>()
             list << 'Hello'
-        ''', 'Cannot call <T> java.util.LinkedList <java.lang.Integer>#leftShift(T) with arguments [java.lang.String]'
+        ''',
+        'Cannot call <T> java.util.LinkedList <java.lang.Integer>#leftShift(T) with arguments [java.lang.String]'
     }
 
     void testAddOnListWithDiamondAndNullUsingLeftShift() {
@@ -147,7 +153,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testReturnTypeInference() {
+    void testReturnTypeInference1() {
         assertScript '''
             class Foo<U> {
                 U method() { }
@@ -157,18 +163,262 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testReturnTypeInferenceWithDiamond() {
+    void testReturnTypeInference2() {
         assertScript '''
-            class Foo<U> {
-                U method() { }
+        Object m() {
+          def s = '1234'
+          println 'Hello'
+        }
+        '''
+    }
+
+    void testReturnTypeInferenceWithMethodGenerics1() {
+        assertScript '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.List <java.lang.Long>'
+            })
+            def list = Arrays.asList(new Long[]{1L,0L})
+            assert list.size() == 2
+            assert list.get(0) == 1
+            assert list.get(1) == 0
+        '''
+
+        assertScript '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.List <java.lang.Long>'
+            })
+            def list = Arrays.asList(1L,0L)
+            assert list.size() == 2
+            assert list.get(0) == 1
+            assert list.get(1) == 0
+        '''
+
+        assertScript '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.List <java.lang.Long>'
+            })
+            def list = Arrays.asList(0L)
+            assert list.size() == 1
+            assert list.get(0) == 0
+        '''
+
+        assertScript '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.List <T extends java.lang.Object>'
+            })
+            def list = Arrays.asList()
+            assert list.size() == 0
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10062
+    void testReturnTypeInferenceWithMethodGenerics2() {
+        assertScript '''
+            def <T> T m(T t, ... zeroOrMore) {
             }
-            Foo<Integer> foo = new Foo<>()
-            Integer result = foo.method()
+
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type == Integer_TYPE
+            })
+            def obj = m(42)
+        '''
+    }
+
+    void testReturnTypeInferenceWithMethodGenerics3() {
+        assertScript '''
+            Number number = Optional.of(42).get()
+            assert number.intValue() == 42
+        '''
+    }
+
+    // GROOVY-9796
+    void testReturnTypeInferenceWithMethodGenerics4() {
+        shouldFailWithMessages '''
+            Number number = Optional.of(42).orElse(Double.NaN)
+            assert number.intValue() == 42
+        ''',
+        'Cannot find matching method java.util.Optional#orElse(double).'
+    }
+
+    void testReturnTypeInferenceWithMethodGenerics5() {
+        assertScript '''
+            Number number = Optional.of((Number) 42).orElse(Double.NaN)
+            assert number.intValue() == 42
+        '''
+    }
+
+    // GROOVY-9796
+    void testReturnTypeInferenceWithMethodGenerics6() {
+        shouldFailWithMessages '''
+            Number number = Optional.ofNullable((Integer) null).orElse(42d)
+            assert number.intValue() == 42
+        ''',
+        'Cannot find matching method java.util.Optional#orElse(double).'
+    }
+
+    void testReturnTypeInferenceWithMethodGenerics7() {
+        assertScript '''
+            Number number = Optional.<Number>ofNullable((Integer) null).orElse(42d)
+            assert number.intValue() == 42
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-9033
+    void testReturnTypeInferenceWithMethodGenerics8() {
+        shouldFailWithMessages '''
+            List<String> test() {
+              def x = [].each { }
+              x.add(new Object())
+              return x // List<E>
+            }
+        ''',
+        'Incompatible generic argument types.' // Cannot assign java.util.List<java.lang.Object> to: java.util.List<java.lang.String>
+
+        assertScript '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.genericsTypes[0].toString() == 'java.lang.String'
+                assert type.genericsTypes[1].toString() == 'java.util.List<java.lang.Object>' // not List<E>
+            })
+            def map = [ key: [] ]
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10049, GROOVY-10053
+    void testReturnTypeInferenceWithMethodGenerics9() {
+        assertScript '''
+            def <X> Set<X> f(Class<X> x) {
+                Collections.singleton(x.newInstance(42))
+            }
+            def <T extends Number> List<T> g(Class<T> t) {
+                f(t).stream().filter(n -> n.intValue() > 0).toList()
+            }
+
+            def result = g(Integer)
+            assert result == [ 42 ]
+        '''
+
+        ['t::cast', 'n -> t.cast(n)', 'n -> (N) n', '{ n -> (N) n }'].each { cast ->
+            assertScript """
+                Set<Number> f() {
+                    Collections.<Number>singleton(42)
+                }
+                def <N extends Number> Set<N> g(Class<N> t) {
+                    Set<N> result = new HashSet<>()
+                    f().stream().filter(t::isInstance)
+                        .map($cast).forEach(n -> result.add(n))
+                    return result
+                }
+
+                def result = g(Integer)
+                assert result == [42] as Set
+            """
+        }
+
+        assertScript '''
+            def <T> String test(Iterable<T> iterable) {
+                Iterator<T> it = iterable.iterator()
+                if (it.hasNext()) {
+                    List<String[]> table = []
+                    it.forEachRemaining(r -> {
+                        if (r instanceof List) {
+                            String[] cells = ((List) r).stream().map(c -> c?.toString())
+                            table.add(cells)
+                        }
+                    })
+                    return table
+                }
+            }
+
+            String result = test([ ['x'], ['y'], [null] ])
+            assert result == '[[x], [y], [null]]'
+        '''
+    }
+
+    // GROOVY-10051
+    void testReturnTypeInferenceWithMethodGenerics10() {
+        assertScript '''
+            abstract class State<H extends Handle> {
+                // Why not return HandleContainer<H>? I can't really say.
+                def <T extends Handle> HandleContainer<T> getHandleContainer(key) {
+                }
+            }
+            class HandleContainer<H extends Handle> {
+                H handle
+            }
+            interface Handle {
+                Result getResult()
+            }
+            class Result {
+                int itemCount
+                String[] items
+            }
+
+            List<String> getStrings(State state, List keys) {
+                keys.collectMany { key ->
+                    List<String> strings = Collections.emptyList()
+                    def container = state.getHandleContainer(key) // returns HandleContainer<Object> not HandleContainer<SearchHandle>
+                    if (container != null) {
+                        def result = container.handle.result
+                        if (result != null && result.itemCount > 0) {
+                            strings = Arrays.asList(result.items)
+                        }
+                    }
+                    strings
+                }
+            }
+
+            assert getStrings(null,[]).isEmpty()
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10067
+    void testReturnTypeInferenceWithMethodGenerics11() {
+        assertScript '''
+            def <N extends Number> N getNumber() {
+                return (N) 42
+            }
+            def f(Integer i) {
+            }
+            def g(int i) {
+            }
+
+            Integer i = this.<Integer>getNumber()
+            f(this.<Integer>getNumber())
+            g(this.<Integer>getNumber())
+
+            i = (Integer) getNumber()
+            f((Integer) getNumber())
+            g((Integer) getNumber())
+
+            i = getNumber()
+            f(getNumber())
+            g(getNumber())
+
+            i = number
+            f(number)
+            g(number)
+        '''
+    }
+
+    // GROOVY-8974
+    void testReturnTypeInferenceWithMethodGenerics12() {
+        assertScript '''
+            def <T> T identity(T t) { t }
+            List<String> list = identity(new ArrayList<>())
+            list.add('foo')
+            def foo = list[0]
+            assert foo.toUpperCase() == 'FOO'
         '''
     }
 
     // GROOVY-9064
-    void testReturnTypeInferenceWithGenerics() {
+    void testReturnTypeInferenceWithMethodGenerics13() {
         assertScript '''
             List getSomeRows() { [new String[]{'x'}] }
             List<String[]> rows = getSomeRows()
@@ -179,20 +429,70 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testReturnTypeInferenceWithMethodGenerics1() {
+    // GROOVY-8409
+    void testReturnTypeInferenceWithMethodGenerics14() {
+        ['R', 'S', 'T', 'U'].each { t -> // BiFunction uses R, T and U
+            assertScript """
+                def <$t> $t applyFunction(java.util.function.BiFunction<Date, URL, $t> action) {
+                    $t result = action.apply(new Date(), new URL('https://www.example.com'))
+                    return result
+                }
+
+                // GroovyCastException: Cannot cast object 'foo' with class 'java.lang.String' to class 'java.util.Date'
+                java.util.function.BiFunction<Date, URL, String> func = { Date d, URL u -> 'foo' }
+                def result = applyFunction(func)
+                assert result == 'foo'
+            """
+        }
+    }
+
+    @NotYetImplemented // GROOVY-8409, GROOVY-10067
+    void testReturnTypeInferenceWithMethodGenerics15() {
+        shouldFailWithMessages '''
+            List<CharSequence> list = ['x'].collect() // GROOVY-10074
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
+
+        shouldFailWithMessages '''
+            List<CharSequence> list = ['x'].stream().toList() // TODO: fix type param bound of StreamGroovyMethods#toList(Stream<T>)
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
+
         assertScript '''
-            List<Long> list = Arrays.asList([0L,0L] as Long[])
+            import static java.util.stream.Collectors.toList
+            List<CharSequence> list = ['x'].stream().collect(toList())
         '''
     }
 
-    void testReturnTypeInferenceWithMethodGenerics2() {
+    @NotYetImplemented // GROOVY-7316, GROOVY-10256
+    void testReturnTypeInferenceWithMethodGenerics16() {
+        shouldFailWithMessages '''
+            def <T extends CharSequence> T chars() {
+            }
+            List test() {
+                chars()
+            }
+        ''',
+        'Cannot return value of type #T for method returning java.util.List'
+    }
+
+    // GROOVY-10098
+    void testReturnTypeInferenceWithMethodGenerics17() {
         assertScript '''
-            List<Long> list = Arrays.asList(0L,0L)
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T extends Number> {
+              T p
+              T m() {
+                Closure<T> x = { -> p }
+                x() // Cannot return value of type Object for method returning T
+              }
+            }
+            assert new C<>(42).m() == 42
         '''
     }
 
     // GROOVY-8638
-    void testReturnTypeInferenceWithMethodGenerics3() {
+    void testReturnTypeInferenceWithMethodGenerics18() {
         assertScript '''
             @Grab('com.google.guava:guava:30.1.1-jre')
             import com.google.common.collect.*
@@ -208,36 +508,43 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    // GROOVY-10098
-    void testReturnTypeInferenceWithMethodGenerics4() {
+    // GROOVY-10222
+    void testReturnTypeInferenceWithMethodGenerics19() {
         assertScript '''
-            @groovy.transform.TupleConstructor(defaults=false)
-            class C<T extends Number> {
-              T p
-              T m() {
-                Closure<T> x = { -> p }
-                x() // Cannot return value of type Object on method returning type T
-              }
+            class Task {
+                def <T> T exec(args) {
+                    args
+                }
             }
-            assert new C<>(42).m() == 42
+            class Test {
+                Task task
+                def <T> T exec(args) {
+                    task.exec(args) // Cannot return value of type #T for method returning T
+                }
+            }
+            String result = new Test(task: new Task()).exec('works')
+            assert result == 'works'
         '''
     }
 
     // GROOVY-9500
-    void testReturnTypeInferenceWithMethodGenerics5() {
+    void testReturnTypeInferenceWithMethodGenerics20() {
         assertScript '''
             trait Entity<D> {
             }
+
             abstract class Path<F extends Entity, T extends Entity> implements Iterable<Path.Segment<F,T>> {
                 interface Segment<F, T> {
                     F start()
                     T end()
                 }
+
                 abstract F start()
                 T end() {
-                  end // Cannot return value of type Path$Segment<F,T> on method returning type T
+                  end // Cannot return value of type Path$Segment<F,T> for method returning T
                 }
                 T end
+
                 @Override
                 void forEach(java.util.function.Consumer<? super Segment<F, T>> action) {
                 }
@@ -248,194 +555,423 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 Iterator<Segment<F, T>> iterator() {
                 }
             }
+
             null
         '''
     }
 
-    // GROOVY-10051
-    void testReturnTypeInferenceWithMethodGenerics6() {
-        assertScript '''
-            abstract class State<H extends Handle> {
-                // Why not return HandleContainer<H>? I can't really say.
-                def <T extends Handle> HandleContainer<T> getHandleContainer(key) {
+    @NotYetImplemented // GROOVY-10339
+    void testReturnTypeInferenceWithMethodGenerics21() {
+        for (type in ['Character', 'Integer']) {
+            shouldFailWithMessages """
+                Character foo() {
                 }
-            }
-            class HandleContainer<H extends Handle> {
-                H handle
-            }
-            interface Handle {
-                Result getResult()
-            }
-            class Result {
-                int itemCount
-                String[] items
-            }
+                def <T> T bar(T x, T y) {
+                }
+                $type z = bar(foo(), 1)
+            """,
+            'Cannot assign value of type'
+        }
+    }
 
-            List<String> getStrings(State state, List keys) {
-                keys.collectMany { key ->
-                    List<String> strings = Collections.emptyList()
-                    def container = state.getHandleContainer(key) // returns HandleContainer<Object> not HandleContainer<SearchHandle>
-                    if (container != null) {
-                        def result = container.handle.result
-                        if (result != null && result.itemCount > 0) {
-                            strings = Arrays.asList(result.items)
-                        }
-                    }
-                    strings
+    @NotYetImplemented // GROOVY-10339
+    void testReturnTypeInferenceWithMethodGenerics22() {
+        for (type in ['Comparable', 'Serializable']) {
+            assertScript """
+                String foo() {
+                }
+                def <T> T bar(T x, T y) {
                 }
+                $type z = bar(foo(), 1)
+            """
+        }
+    }
+
+    // GROOVY-10347
+    void testReturnTypeInferenceWithMethodGenerics23() {
+        assertScript '''
+            String[] one = ['foo','bar'], two = ['baz']
+            Map<String,Integer> map = one.collectEntries{[it,1]} + two.collectEntries{[it,2]}
+            assert map == [foo:1, bar:1, baz:2]
+        '''
+    }
+
+    // GROOVY-10347
+    void testReturnTypeInferenceWithMethodGenerics24() {
+        assertScript '''
+            class Pogo {
+                String s
+            }
+            class Sorter implements Comparator<Pogo>, Serializable {
+                int compare(Pogo p1, Pogo p2) { p1.s <=> p2.s }
             }
 
-            assert getStrings(null,[]).isEmpty()
+            Pogo[] pogos = [new Pogo(s:'foo'), new Pogo(s:'bar')]
+            List<String> strings = pogos.sort(true, new Sorter())*.s // sort(T[],boolean,Comparator<? super T>)
+            assert strings == ['bar', 'foo']
         '''
     }
 
     void testDiamondInferrenceFromConstructor1() {
         assertScript '''
-            Set< Long > s2 = new HashSet<>()
+            class Foo<U> {
+                U method() { }
+            }
+            Foo<Integer> foo = new Foo<>()
+            Integer result = foo.method()
         '''
     }
 
     void testDiamondInferrenceFromConstructor2() {
         assertScript '''
-            new HashSet<>(Arrays.asList(0L,0L));
+            Set<Long> set = new HashSet<>()
         '''
     }
 
     void testDiamondInferrenceFromConstructor3() {
+        assertScript '''
+            new HashSet<>(Arrays.asList(0L))
+        '''
+
+        // not diamond inference, but tests compatible assignment
+        assertScript '''
+            Set<Number> set = new HashSet<Number>(Arrays.asList(0L))
+        '''
+
         shouldFailWithMessages '''
-            Set< Number > s3 = new HashSet<>(Arrays.asList(0L,0L));
-        ''', 'Cannot assign java.util.HashSet <java.lang.Long> to: java.util.Set <Number>'
+            Set<Number> set = new HashSet<>(Arrays.asList(0L))
+        ''',
+        'Cannot assign java.util.HashSet <java.lang.Long> to: java.util.Set <Number>'
+    }
+
+    @NotYetImplemented
+    void testDiamondInferrenceFromConstructor3a() {
+        assertScript '''
+            Set<Number> set = new HashSet<>(Arrays.asList(0L))
+        '''
+
+        assertScript '''
+            Set<? super Number> set = new HashSet<>(Arrays.asList(0L))
+        '''
+
+        assertScript '''
+            Set<? extends Number> set = new HashSet<>(Arrays.asList(0L))
+        '''
     }
 
+    @NotYetImplemented // GROOVY-7419
     void testDiamondInferrenceFromConstructor4() {
         assertScript '''
-            Set<Number> s4 = new HashSet<Number>(Arrays.asList(0L,0L))
+            Map<Thread.State, Object> map = new EnumMap<>(Thread.State)
+            assert map.size() == 0
+            assert map.isEmpty()
+        '''
+    }
+
+    // GROOVY-6232
+    void testDiamondInferrenceFromConstructor5() {
+        ['"a",new Object()', 'new Object(),"b"', '"a","b"'].each { args ->
+            assertScript """
+                class C<T> {
+                    C(T x, T y) {
+                    }
+                }
+                C<Object> c = new C<>($args)
+            """
+        }
+    }
+
+    // GROOVY-9948
+    void testDiamondInferrenceFromConstructor6() {
+        assertScript '''
+            class C<T> {
+                T p
+                C(T p) {
+                    this.p = p
+                }
+            }
+
+            C<Integer> c = new C<>(1)
+            assert c.p < 10
+        '''
+    }
+
+    // GROOVY-9948
+    void testDiamondInferrenceFromConstructor7() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+
+            C<Integer> c = new C<>(1)
+            assert c.p < 10
+        '''
+    }
+
+    // GROOVY-9984
+    void testDiamondInferrenceFromConstructor7a() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+
+            C<Integer> c = new C<>(null)
+            assert c.p === null
+        '''
+    }
+
+    // GROOVY-9956
+    void testDiamondInferrenceFromConstructor8() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+            interface I { }
+            class D implements I { }
+
+            C<I> ci = new C<I>(new D())
+            ci = new C<>(new D()) // infers C<D> on RHS
+        '''
+    }
+
+    // GROOVY-9996
+    void testDiamondInferrenceFromConstructor8a() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+            interface I { }
+            class D implements I { }
+            void test(C<I> c) { assert c.p instanceof D }
+
+            I i = new D() // infers D for "i"
+            def ci = new C<>(i) // infers C<D> for "ci"
+            test(ci)
+        '''
+    }
+
+    // GROOVY-10011
+    void testDiamondInferrenceFromConstructor8b() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+            interface I { }
+            class D implements I { }
+
+            void test(I i) {
+                if (i instanceof D) {
+                    C<D> cd = new C<>(i)
+                }
+            }
+            test(new D())
+        '''
+    }
+
+    @NotYetImplemented
+    void testDiamondInferrenceFromConstructor9() {
+        assertScript '''
+            abstract class A<X> { }
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> extends A<T> {
+                T p
+            }
+            interface I { }
+            class D implements I { }
+
+            A<I> ai = new C<>(new D())
+        '''
+
+        shouldFailWithMessages '''
+            abstract class A<X> { }
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> extends A<T> {
+                T p
+            }
+            interface I { }
+            class D implements I { }
+
+            A<String> ax = new C<>(new D())
+        ''',
+        'Incompatible generic argument types. Cannot assign C<D> to: A<java.lang.String>'
+
+        shouldFailWithMessages '''
+            Set<List<String>> strings = new HashSet<>([new ArrayList<Number>()])
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.HashSet<java.util.ArrayList<java.lang.Number>> to: java.util.Set<java.util.List<java.lang.String>>'
+    }
+
+    // GROOVY-9972
+    void testDiamondInferrenceFromConstructor9a() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+            class D {
+                public String f = 'D#f'
+            }
+            C<D> cd = true ? new C<>(new D()) : new C<>(new D())
+            assert cd.p.f.toLowerCase() == 'd#f'
+        '''
+
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+            class D {
+                public String f = 'D#f'
+            }
+            boolean b = false
+            C<D> cd = b ? new C<>(new D()) : (b ? new C<>((D) null) : new C<>(new D()))
+            assert cd.p.f.toLowerCase() == 'd#f'
         '''
-    }
-
-    // GROOVY-9984
-    void testDiamondInferrenceFromConstructor5() {
+/*
         assertScript '''
             @groovy.transform.TupleConstructor(defaults=false)
             class C<T> {
                 T p
             }
-
-            C<Integer> c = new C<>(null)
-            assert c.p === null
+            class D {
+                public String f = 'D#f'
+            }
+            def cd
+            if (true) {
+                cd = new C<>(new D())
+            } else {
+                cd = new C<>((D) null)
+            }
+            assert cd.p.f.toLowerCase() == 'd#f'
+        '''
+*/
+        assertScript '''
+            @groovy.transform.TupleConstructor
+            class C {
+                List<D> list
+            }
+            class D {
+            }
+            List test(C... array) {
+                // old code used "List<D> many" as target for guts of closure
+                List<D> many = array.collectMany { it.list ?: [] }
+            }
+            def result = test(new C(), new C(list:[new D()]))
+            assert result.size() == 1
         '''
     }
 
-    // GROOVY-9996
-    void testDiamondInferrenceFromConstructor6() {
+    @NotYetImplemented
+    void testDiamondInferrenceFromConstructor9b() {
         assertScript '''
             @groovy.transform.TupleConstructor(defaults=false)
             class C<T> {
                 T p
             }
-            interface I { }
-            class D implements I { }
-            void test(C<I> c) { assert c.p instanceof D }
-
-            I i = new D() // infers D for "i"
-            def ci = new C<>(i) // infers C<D> for "ci"
-            test(ci)
+            class D {
+                public String f = 'D#f'
+            }
+            def foo = { flag, C<D> cd = (flag ? new C<>(new D()) : new C<>(new D())) ->
+                cd.p.f.toLowerCase()
+            }
+            assert foo.call(true) == 'd#f'
         '''
-    }
 
-    // GROOVY-10011
-    void testDiamondInferrenceFromConstructor7() {
-        assertScript '''
+        shouldFailWithMessages '''
             @groovy.transform.TupleConstructor(defaults=false)
             class C<T> {
                 T p
             }
-            interface I { }
-            class D implements I { }
-
-            void test(I i) {
-                if (i instanceof D) {
-                    C<D> cd = new C<>(i)
-                }
+            class D {
+                public String f = 'D#f'
             }
-            test(new D())
-        '''
-    }
-
-    // GROOVY-8974
-    void testDiamondInferrenceFromConstructor8() {
-        assertScript '''
-            def <T> T identity(T t) { t }
-            List<String> list = identity(new ArrayList<>())
-            list.add('foo')
-            def foo = list[0]
-            assert foo.toUpperCase() == 'FOO'
-        '''
+            def foo = { flag, C<D> cd = (flag ? new C<>(new D()) : new C<>(new Object())) ->
+                cd.p.f.toLowerCase()
+            }
+        ''',
+        'Incompatible generic argument types. Cannot assign C<? extends java.lang.Object> to: C<D>'
     }
 
-    // GROOVY-10283
-    void testDiamondInferrenceFromConstructor9() {
+    @NotYetImplemented // GROOVY-9963
+    void testDiamondInferrenceFromConstructor10() {
         assertScript '''
-            class A<T1, T2> {
-            }
-            class B<T1 extends Number, T2 extends A<C, ? extends T1>> {
-                T2 t
-                B(T2 t) {
-                    this.t  = t
-                }
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
             }
-            class C {
+            static void m(String s) {
+                assert s.isEmpty()
             }
-
-            new B<Integer,A<C,Integer>>(new A<>())
+            m(new C<>("").p)
         '''
     }
 
-    // GROOVY-9972
-    void testDiamondInferrenceFromConstructor10() {
+    @NotYetImplemented // GROOVY-10080
+    void testDiamondInferrenceFromConstructor11() {
         assertScript '''
-            @groovy.transform.TupleConstructor
+            @groovy.transform.TupleConstructor(defaults=false)
             class C<T> {
                 T p
             }
             class D {
-                public String f = 'D#f'
+                int m(Object[] objects) {
+                    42
+                }
             }
-            C<D> cd = true ? new C<>(new D()) : new C<>(new D())
-            assert cd.p.f.toLowerCase() == 'd#f'
+            def closure = { ->
+                new C<>(new D())
+            }
+            def result = closure().p.m(new BigDecimal[0]) // Cannot find matching method Object#m(...)
+            assert result == 42
         '''
+    }
 
-        assertScript '''
-            @groovy.transform.TupleConstructor
+    @NotYetImplemented // GROOVY-10086
+    void testDiamondInferrenceFromConstructor12() {
+        shouldFailWithMessages '''
+            @groovy.transform.TupleConstructor(defaults=false)
             class C<T> {
                 T p
             }
             class D {
-                public String f = 'D#f'
             }
-            boolean b = false
-            C<D> cd = b ? new C<>(new D()) : (b ? new C<>((D) null) : new C<>(new D()))
-            assert cd.p.f.toLowerCase() == 'd#f'
-        '''
+            void m(int i, C<D>... zeroOrMore) {
+                D d = zeroOrMore[0].p
+            }
+            m(0, new C<>(1))
+        ''',
+        'Cannot call', 'm(int, C<D>[]) with arguments [int, C<java.lang.Integer>]'
+    }
 
+    // GROOVY-9970
+    void testDiamondInferrenceFromConstructor13() {
         assertScript '''
-            @groovy.transform.TupleConstructor
-            class C {
-                List<D> list
+            @groovy.transform.TupleConstructor(defaults=false)
+            class A<T extends B> {
+                T p
             }
-            class D {
+            class B {
             }
-            List test(C... array) {
-                // old code used "List<D> many" as target for guts of closure
-                List<D> many = array.collectMany { it.list ?: [] }
+            class C<T extends Number> {
+                void test(T n) {
+                    A<B> x = new A<>(new B())
+                    def closure = { ->
+                        A<B> y = new A<>(new B())
+                    }
+                    closure.call()
+                }
             }
-            def result = test(new C(), new C(list:[new D()]))
-            assert result.size() == 1
+            new C<Long>().test(42L)
         '''
     }
 
     // GROOVY-9983
-    void testDiamondInferrenceFromConstructor11() {
+    void testDiamondInferrenceFromConstructor14() {
         String types = '''
             @groovy.transform.TupleConstructor(defaults=false)
             class A<T> {
@@ -480,6 +1016,216 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         'Cannot call C#m(A<B>) with arguments [A<? extends java.lang.Object>]'*/
     }
 
+    @NotYetImplemented // GROOVY-10114
+    void testDiamondInferrenceFromConstructor14a() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class A<T> {
+              T p
+            }
+            class B {
+                Character m() {
+                    (Character) '!'
+                }
+            }
+            (false ? new A<B>(new B()) : new A<>(new B())).p.m()
+            (false ? new A< >(new B()) : new A<>(new B())).p.m()
+            def a = (true ? new A<>(new B()) : new A<>(new B()))
+            assert a.p.m() == (char)'!'
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-9995
+    void testDiamondInferrenceFromConstructor15() {
+        [
+            ['Closure<A<Long>>', 'java.util.concurrent.Callable<A<Long>>'],
+            ['new A<>(42L)', 'return new A<>(42L)']
+        ].combinations { functionalType, returnStmt ->
+            assertScript """
+                @groovy.transform.TupleConstructor(defaults=false)
+                class A<T extends Number> {
+                    T p
+                }
+                $functionalType callable = { ->
+                    $returnStmt
+                }
+                Long n = callable.call().p
+                assert n == 42L
+            """
+        }
+    }
+
+    // GROOVY-10283
+    void testDiamondInferrenceFromConstructor16() {
+        assertScript '''
+            class A<T1, T2> {
+            }
+            class B<T1 extends Number, T2 extends A<C, ? extends T1>> {
+                T2 t
+                B(T2 t) {
+                    this.t  = t
+                }
+            }
+            class C {
+            }
+
+            new B<Integer,A<C,Integer>>(new A<>())
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10291
+    void testDiamondInferrenceFromConstructor17() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class A<X> {
+                X x
+            }
+            class B<Y> {
+                def m(Y y) { null }
+                def test() {
+                    def c = { Y yy -> null }
+
+                    Y y = null
+                    m(new A<>(y).x) // works
+                    c(new A<>(y).x) // fails
+                }
+            }
+            new B().test()
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10228
+    void testDiamondInferrenceFromConstructor18() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<T> {
+                T p
+            }
+            def m(Number n) {
+                'works'
+            }
+            def x = 12345
+            x = m(new C<>(x).getP()) // Cannot find matching method
+            assert x == 'works'
+        '''
+    }
+
+    // GROOVY-10323
+    void testDiamondInferrenceFromConstructor19() {
+        assertScript '''
+            class C<T> {
+            }
+            def <T,T> T m(C<T> c) {
+            }
+            Number n = m(new C<>())
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10324
+    void testDiamondInferrenceFromConstructor20() {
+        assertScript '''
+            class C<T> {
+            }
+            def <X> X m(C<X> c) {
+            }
+            List<String> x = m(new C<>())
+        '''
+    }
+
+    // GROOVY-10310
+    void testDiamondInferrenceFromConstructor21() {
+        assertScript '''
+            @groovy.transform.TupleConstructor
+            class A<T> {
+                T f
+            }
+            class B<T> {
+            }
+            def <T> A<T> m(T t, B<? extends T> b_of_t) {
+                new A<>(t)
+            }
+            def x = 'x'
+            m(x, new B<>()) // Cannot call m(T,B<? extends T>) with arguments...
+        '''
+    }
+
+    // GROOVY-10344
+    void testDiamondInferrenceFromConstructor22() {
+        assertScript '''
+            class C<X,Y> {
+            }
+            def <T extends C<? extends Number, ? extends Number>> T m(T t) {
+                return t
+            }
+            def x = m(new C<>())
+            assert x instanceof C
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10230
+    void testDiamondInferrenceFromConstructor23() {
+        assertScript '''
+            class A {
+                def <T extends C<Number,Number>> T m(T t) {
+                    return t
+                }
+            }
+            class B extends A {
+                @Override
+                def <T extends C<Number,Number>> T m(T t) {
+                    T x = null; super.m(true ? t : x)
+                }
+            }
+            class C<X,Y> {
+            }
+            def x = new B().m(new C<>())
+            assert x instanceof C
+        '''
+    }
+
+    // GROOVY-10351
+    void testDiamondInferrenceFromConstructor24() {
+        assertScript '''
+            class C<T> {
+                C(T one, D<T,? extends T> two) {
+                }
+            }
+            class D<U,V> {
+            }
+            D<Integer,? extends Integer> d_of_i_and_i = null
+            C<Integer> c_of_i = new C<>(1,d_of_i_and_i) // 3 witnesses for T
+        '''
+    }
+
+    // GROOVY-10368
+    void testDiamondInferrenceFromConstructor25() {
+        ['T', 'T extends Number', 'T extends Object'].each {
+            assertScript """
+                class C<$it> {
+                    C(p) {
+                    }
+                }
+                void m(C<Integer> c) {
+                }
+                m(new C<>(null)) // Cannot call m(C<Integer>) with arguments [C<# extends Number>]
+            """
+        }
+    }
+
+    @NotYetImplemented // GROOVY-10367
+    void testDiamondInferrenceFromConstructor26() {
+        assertScript '''
+            @groovy.transform.TupleConstructor(defaults=false)
+            class C<X, Y extends X> { // works without Y
+              X x
+            }
+            def <Z extends Number> void test(Z z) {
+              z = new C<>(z).x // Cannot assign value of type Object to variable of type Z
+            }
+            test(null)
+        '''
+    }
+
     // GROOVY-10280
     void testTypeArgumentPropagation() {
         assertScript '''
@@ -532,13 +1278,15 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     void testLinkedListWithListArgumentAndWrongElementTypes() {
         shouldFailWithMessages '''
             List<String> list = new LinkedList<String>([1,2,3])
-        ''', 'Cannot call java.util.LinkedList <String>#<init>(java.util.Collection <? extends java.lang.String>) with arguments [java.util.List <java.lang.Integer>]'
+        ''',
+        'Cannot call java.util.LinkedList <String>#<init>(java.util.Collection <? extends java.lang.String>) with arguments [java.util.List <java.lang.Integer>]'
     }
 
     void testCompatibleGenericAssignmentWithInference() {
         shouldFailWithMessages '''
             List<String> elements = ['a','b', 1]
-        ''', 'Incompatible generic argument types. Cannot assign java.util.List <java.io.Serializable> to: java.util.List <String>'
+        ''',
+        'Incompatible generic argument types. Cannot assign java.util.List <java.io.Serializable> to: java.util.List <String>'
     }
 
     void testGenericAssignmentWithSubClass() {
@@ -550,7 +1298,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     void testGenericAssignmentWithSubClassAndWrongGenericType() {
         shouldFailWithMessages '''
             List<Integer> list = new groovy.transform.stc.GenericsSTCTest.MyList()
-        ''', 'Incompatible generic argument types'
+        ''',
+        'Incompatible generic argument types'
     }
 
     void testAddShouldBeAllowedOnUncheckedGenerics() {
@@ -566,7 +1315,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     void testAssignmentShouldFailBecauseOfLowerBound() {
         shouldFailWithMessages '''
             List<? super Number> list = ['string']
-        ''', 'Number'
+        ''',
+        'Number'
     }
 
     // GROOVY-9914, GROOVY-10036
@@ -653,15 +1403,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         ''', 'Cannot find matching method FooWithGenerics#say(java.lang.Object)'
     }
 
-    void testVoidReturnTypeInferrence() {
-        assertScript '''
-        Object m() {
-          def s = '1234'
-          println 'Hello'
-        }
-        '''
-    }
-
     // GROOVY-5237
     void testGenericTypeArgumentAsField() {
         assertScript '''
@@ -1139,8 +1880,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-5528
     void testAssignmentToInterfaceFromUserClassWithGenerics() {
-        assertScript '''class UserList<T> extends LinkedList<T> {}
-        List<String> list = new UserList<String>()
+        assertScript '''
+            class UserList<T> extends LinkedList<T> {}
+            List<String> list = new UserList<String>()
         '''
     }
 
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
index ed1fb12..3a667c5 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/GenericsStaticCompileTest.groovy
@@ -18,12 +18,16 @@
  */
 package org.codehaus.groovy.classgen.asm.sc
 
+import groovy.test.NotYetImplemented
 import groovy.transform.stc.GenericsSTCTest
 
 /**
- * Unit tests for static type checking : generics.
+ * Unit tests for static compilation : generics.
  */
 class GenericsStaticCompileTest extends GenericsSTCTest implements StaticCompilationTestSupport {
 
+    @Override @NotYetImplemented // GROOVY-8409
+    void testReturnTypeInferenceWithMethodGenerics14() {
+        super.testReturnTypeInferenceWithMethodGenerics14()
+    }
 }
-