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

[groovy] branch GROOVY_3_0_X updated: GROOVY-10813: fix receiver distance, receiver static and extension order

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


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 192b5f5dd2 GROOVY-10813: fix receiver distance, receiver static and extension order
192b5f5dd2 is described below

commit 192b5f5dd296d4b61a6fc1c1b33737027223c930
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Nov 28 11:41:20 2022 -0600

    GROOVY-10813: fix receiver distance, receiver static and extension order
    
    3_0_X backport
---
 ...StaticTypesMethodReferenceExpressionWriter.java | 189 ++---
 .../transform/stc/MethodReferenceTest.groovy       | 939 ++++++++++++---------
 2 files changed, 619 insertions(+), 509 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index 6894b5cccb..6dfb345ae1 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -18,8 +18,6 @@
  */
 package org.codehaus.groovy.classgen.asm.sc;
 
-import groovy.lang.Tuple;
-import groovy.lang.Tuple2;
 import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
@@ -39,16 +37,14 @@ import org.codehaus.groovy.syntax.RuntimeParserException;
 import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
 import org.codehaus.groovy.transform.stc.ExtensionMethodNode;
 import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.stream.Collectors;
 
-import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedMethod;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static java.util.Comparator.comparingInt;
+import static java.util.stream.Collectors.joining;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
@@ -114,21 +110,15 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             }
 
             typeOrTargetRefType = methodRefMethod.getDeclaringClass();
-            Expression classExpression = classX(typeOrTargetRefType);
-            classExpression.setSourcePosition(typeOrTargetRef);
-            typeOrTargetRef = classExpression;
+            typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
         }
 
-        methodRefMethod.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType);
-
         if (!isClassExpression) {
-            if (isConstructorReference) {
-                // TODO: move the checking code to the parser
+            if (isConstructorReference) { // TODO: move this check to the parser
                 addFatalError("Constructor reference must be className::new", methodReferenceExpression);
             } else if (methodRefMethod.isStatic()) {
-                ClassExpression classExpression = classX(typeOrTargetRefType);
-                classExpression.setSourcePosition(typeOrTargetRef);
-                typeOrTargetRef = classExpression;
+                // "string"::valueOf refers to static method, so instance is superfluous
+                typeOrTargetRef = makeClassTarget(typeOrTargetRefType, typeOrTargetRef);
                 isClassExpression = true;
             } else {
                 typeOrTargetRef.visit(controller.getAcg());
@@ -144,18 +134,24 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             referenceKind = Opcodes.H_INVOKEVIRTUAL;
         }
 
-        controller.getMethodVisitor().visitInvokeDynamicInsn(
-                abstractMethod.getName(),
-                createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
-                createBootstrapMethod(classNode.isInterface(), false),
-                createBootstrapMethodArguments(
-                        createMethodDescriptor(abstractMethod),
-                        referenceKind,
-                        isConstructorReference ? classNode : typeOrTargetRefType,
-                        methodRefMethod,
-                        false
-                )
-        );
+        String methodName = abstractMethod.getName();
+        String methodDesc = BytecodeHelper.getMethodDescriptor(functionalInterfaceType.redirect(),
+                isClassExpression ? Parameter.EMPTY_ARRAY : new Parameter[]{new Parameter(typeOrTargetRefType, "__METHODREF_EXPR_INSTANCE")});
+
+        methodRefMethod.putNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE, parametersWithExactType);
+        try {
+            Handle bootstrapMethod = createBootstrapMethod(classNode.isInterface(), false);
+            Object[] bootstrapArgs = createBootstrapMethodArguments(
+                    createMethodDescriptor(abstractMethod),
+                    referenceKind,
+                    methodRefMethod.getDeclaringClass(),
+                    methodRefMethod,
+                    false
+            );
+            controller.getMethodVisitor().visitInvokeDynamicInsn(methodName, methodDesc, bootstrapMethod, bootstrapArgs);
+        } finally {
+            methodRefMethod.removeNodeMetaData(ORIGINAL_PARAMETERS_WITH_EXACT_TYPE);
+        }
 
         if (isClassExpression) {
             controller.getOperandStack().push(functionalInterfaceType);
@@ -167,40 +163,33 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
     private void validate(final MethodReferenceExpression methodReference, final ClassNode targetType, final String methodName, final MethodNode methodNode, final Parameter[] samParameters, final ClassNode samReturnType) {
         if (methodNode == null) {
             String error = String.format("Failed to find the expected method[%s(%s)] in the type[%s]",
-                    methodName, Arrays.stream(samParameters).map(e -> e.getType().getText()).collect(Collectors.joining(",")), targetType.getText());
+                    methodName, Arrays.stream(samParameters).map(e -> e.getType().getText()).collect(joining(",")), targetType.getText());
             addFatalError(error, methodReference);
         } else if (methodNode.isVoidMethod() && !samReturnType.equals(ClassHelper.VOID_TYPE)) {
             addFatalError("Invalid return type: void is not convertible to " + samReturnType.getText(), methodReference);
-        } else if (samParameters.length > 0 && isTypeReferingInstanceMethod(methodReference.getExpression(), methodNode) && !isAssignableTo(samParameters[0].getType(), targetType)) {
+        } else if (samParameters.length > 0 && isTypeReferringInstanceMethod(methodReference.getExpression(), methodNode) && !isAssignableTo(samParameters[0].getType(), targetType)) {
             throw new RuntimeParserException("Invalid receiver type: " + samParameters[0].getType().getText() + " is not compatible with " + targetType.getText(), methodReference.getExpression());
         }
     }
 
     private MethodNode addSyntheticMethodForDGSM(final MethodNode mn) {
         Parameter[] parameters = removeFirstParameter(mn.getParameters());
-        ArgumentListExpression args = args(parameters);
+        ArgumentListExpression args = new ArgumentListExpression(parameters);
         args.getExpressions().add(0, nullX());
 
         MethodCallExpression returnValue = callX(classX(mn.getDeclaringClass()), mn.getName(), args);
         returnValue.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, mn);
         returnValue.setMethodTarget(mn);
 
-        MethodNode delegateMethod = addGeneratedMethod(controller.getClassNode(),
-                "dgsm$$" + mn.getParameters()[0].getType().getName().replace('.', '$') + "$$" + mn.getName(),
-                Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC,
-                mn.getReturnType(),
-                parameters,
-                ClassNode.EMPTY_ARRAY,
-                block(returnS(returnValue))
-        );
+        String methodName = "dgsm$$" + mn.getParameters()[0].getType().getName().replace('.', '$') + "$$" + mn.getName();
 
+        MethodNode delegateMethod = addSyntheticMethod(methodName, mn.getReturnType(), returnValue, parameters, mn.getExceptions());
         delegateMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
-
         return delegateMethod;
     }
 
-    private MethodNode addSyntheticMethodForConstructorReference(final String syntheticMethodName, final ClassNode returnType, final Parameter[] parametersWithExactType) {
-        ArgumentListExpression ctorArgs = args(parametersWithExactType);
+    private MethodNode addSyntheticMethodForConstructorReference(final String methodName, final ClassNode returnType, final Parameter[] parametersWithExactType) {
+        ArgumentListExpression ctorArgs = new ArgumentListExpression(parametersWithExactType);
 
         Expression returnValue;
         if (returnType.isArray()) {
@@ -211,32 +200,22 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             returnValue = ctorX(returnType, ctorArgs);
         }
 
-        MethodNode delegateMethod = addGeneratedMethod(controller.getClassNode(),
-                syntheticMethodName,
-                Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC,
-                returnType,
-                parametersWithExactType,
-                ClassNode.EMPTY_ARRAY,
-                block(returnS(returnValue))
-        );
-
+        MethodNode delegateMethod = addSyntheticMethod(methodName, returnType, returnValue, parametersWithExactType, ClassNode.EMPTY_ARRAY);
         // TODO: if StaticTypesMarker.DIRECT_METHOD_CALL_TARGET or
         // OptimizingStatementWriter.StatementMeta.class metadatas
         // can bet set for the ctorX above, then this can be TRUE:
         delegateMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.FALSE);
-
         return delegateMethod;
     }
 
-    private String createAbstractMethodDesc(final ClassNode functionalInterfaceType, final Expression methodRef) {
-        List<Parameter> methodReferenceSharedVariableList = new ArrayList<>();
-
-        if (!(methodRef instanceof ClassExpression)) {
-            prependParameter(methodReferenceSharedVariableList, "__METHODREF_EXPR_INSTANCE",
-                controller.getTypeChooser().resolveType(methodRef, controller.getClassNode()));
-        }
-
-        return BytecodeHelper.getMethodDescriptor(functionalInterfaceType.redirect(), methodReferenceSharedVariableList.toArray(Parameter.EMPTY_ARRAY));
+    private MethodNode addSyntheticMethod(final String methodName, final ClassNode returnType, final Expression returnValue, final Parameter[] parameters, final ClassNode[] exceptions) {
+        return controller.getClassNode().addSyntheticMethod(
+            methodName,
+            Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
+            returnType,
+            parameters,
+            exceptions,
+            returnS(returnValue));
     }
 
     private Parameter[] createParametersWithExactType(final MethodNode abstractMethod, final ClassNode[] inferredParamTypes) {
@@ -263,9 +242,9 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
     private MethodNode findMethodRefMethod(final String methodName, final Parameter[] samParameters, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType) {
         List<MethodNode> methods = findVisibleMethods(methodName, typeOrTargetRefType);
 
-        return chooseMethodRefMethodCandidate(typeOrTargetRef, methods.stream().filter(method -> {
+        methods.removeIf(method -> {
             Parameter[] parameters = method.getParameters();
-            if (isTypeReferingInstanceMethod(typeOrTargetRef, method)) {
+            if (isTypeReferringInstanceMethod(typeOrTargetRef, method)) {
                 // there is an implicit parameter for "String::length"
                 ClassNode firstParamType = method.getDeclaringClass();
 
@@ -276,8 +255,29 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
                 parameters = plusOne;
             }
-            return parametersCompatible(samParameters, parameters);
-        }).collect(Collectors.toList()));
+            return !parametersCompatible(samParameters, parameters);
+        });
+
+        return chooseMethodRefMethod(methods, typeOrTargetRef, typeOrTargetRefType);
+    }
+
+    private MethodNode chooseMethodRefMethod(final List<MethodNode> methods, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType) {
+        if (methods.isEmpty()) return null;
+        if (methods.size() == 1) return methods.get(0);
+        return methods.stream().max(comparingInt((MethodNode mn) -> {
+            int score = 9;
+            for (ClassNode cn = typeOrTargetRefType; cn != null && !cn.equals(mn.getDeclaringClass()); cn = cn.getSuperClass()) {
+                score -= 1;
+            }
+            if (score < 0) {
+                score = 0;
+            }
+            score *= 10;
+            if ((typeOrTargetRef instanceof ClassExpression) == isStaticMethod(mn)) {
+                score += 9;
+            }
+            return score;
+        }).thenComparing(StaticTypesMethodReferenceExpressionWriter::isExtensionMethod)).get();
     }
 
     private List<MethodNode> findVisibleMethods(final String name, final ClassNode type) {
@@ -298,57 +298,30 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
     //--------------------------------------------------------------------------
 
-    private static boolean isConstructorReference(final String methodRefName) {
-        return "new".equals(methodRefName);
+    private static boolean isConstructorReference(final String name) {
+        return "new".equals(name);
     }
 
-    private static boolean isExtensionMethod(final MethodNode methodRefMethod) {
-        return (methodRefMethod instanceof ExtensionMethodNode);
+    private static boolean isExtensionMethod(final MethodNode mn) {
+        return (mn instanceof ExtensionMethodNode);
     }
 
-    private static boolean isTypeReferingInstanceMethod(final Expression typeOrTargetRef, final MethodNode mn) {
-        // class::instanceMethod
-        return (typeOrTargetRef instanceof ClassExpression) && ((mn != null && !mn.isStatic())
-                || (isExtensionMethod(mn) && !((ExtensionMethodNode) mn).isStaticExtension()));
+    private static boolean isStaticMethod(final MethodNode mn) {
+        return isExtensionMethod(mn) ? ((ExtensionMethodNode) mn).isStaticExtension() : mn.isStatic();
     }
 
-    private static Parameter[] removeFirstParameter(final Parameter[] parameters) {
-        return Arrays.copyOfRange(parameters, 1, parameters.length);
+    private static boolean isTypeReferringInstanceMethod(final Expression typeOrTargetRef, final MethodNode mn) {
+        // class::instanceMethod
+        return (typeOrTargetRef instanceof ClassExpression) && (mn != null && !isStaticMethod(mn));
     }
 
-    /**
-     * Choose the best method node for method reference.
-     */
-    private static MethodNode chooseMethodRefMethodCandidate(final Expression methodRef, final List<MethodNode> candidates) {
-        if (1 == candidates.size()) return candidates.get(0);
-
-        return candidates.stream()
-                .map(e -> Tuple.tuple(e, matchingScore(e, methodRef)))
-                .min((t1, t2) -> Integer.compare(t2.getV2(), t1.getV2()))
-                .map(Tuple2::getV1)
-                .orElse(null);
+    private static Expression makeClassTarget(final ClassNode target, final Expression source) {
+        Expression expression = classX(target);
+        expression.setSourcePosition(source);
+        return expression;
     }
 
-    private static Integer matchingScore(final MethodNode mn, final Expression typeOrTargetRef) {
-        ClassNode typeOrTargetRefType = typeOrTargetRef.getType();
-
-        int score = 9;
-        for (ClassNode cn = mn.getDeclaringClass(); null != cn && !cn.equals(typeOrTargetRefType); cn = cn.getSuperClass()) {
-            score -= 1;
-        }
-        if (score < 0) {
-            score = 0;
-        }
-        score *= 10;
-
-        if ((typeOrTargetRef instanceof ClassExpression) == mn.isStatic()) {
-            score += 9;
-        }
-
-        if (isExtensionMethod(mn)) {
-            score += 100;
-        }
-
-        return score;
+    private static Parameter[] removeFirstParameter(final Parameter[] parameters) {
+        return Arrays.copyOfRange(parameters, 1, parameters.length);
     }
 }
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index f72bfa1f73..bbca175e90 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -18,105 +18,95 @@
  */
 package groovy.transform.stc
 
-import groovy.test.GroovyTestCase
 import groovy.test.NotYetImplemented
+import org.junit.Test
 
+import static groovy.test.GroovyAssert.assertScript
 import static groovy.test.GroovyAssert.isAtLeastJdk
+import static groovy.test.GroovyAssert.shouldFail
 
-final class MethodReferenceTest extends GroovyTestCase {
+final class MethodReferenceTest {
 
-    // class::instanceMethod
-    void testFunctionCI() {
-        assertScript '''
-            import java.util.stream.Collectors
+    private static final imports = '''\
+        import java.util.function.*
+        import java.util.stream.Collectors
+        import groovy.transform.CompileStatic
+    '''
 
-            @groovy.transform.CompileStatic
-            void p() {
+    @Test // class::instanceMethod
+    void testFunctionCI() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def result = [1, 2, 3].stream().map(Object::toString).collect(Collectors.toList())
                 assert result == ['1', '2', '3']
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::instanceMethod
+    @Test // class::instanceMethod
     void testFunctionCI2() {
-        assertScript '''
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def result = [1, 2, 3].stream().map(Integer::toString).collect(Collectors.toList())
                 assert result == ['1', '2', '3']
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::instanceMethod -- GROOVY-10047
+    @Test // class::instanceMethod -- GROOVY-10047
     void testFunctionCI3() {
-        assertScript '''
-            import java.util.function.Function
-            import static java.util.stream.Collectors.toMap
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 List<String> list = ['a','bc','def']
                 Function<String,String> self = str -> str // help for toMap
-                def map = list.stream().collect(toMap(self, String::length))
-                assert map == [a: 1, bc: 2, 'def': 3]
+                def result = list.stream().collect(Collectors.toMap(self, String::length))
+                assert result == [a: 1, bc: 2, 'def': 3]
             }
 
-            p()
+            test()
         '''
 
-        assertScript '''
-            import java.util.function.Function
-            import static java.util.stream.Collectors.toMap
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 List<String> list = ['a','bc','def']
                 // TODO: inference for T in toMap(Function<? super T,...>, Function<? super T,...>)
-                def map = list.stream().collect(toMap(Function.<String>identity(), String::length))
-                assert map == [a: 1, bc: 2, 'def': 3]
+                def result = list.stream().collect(Collectors.toMap(Function.<String>identity(), String::length))
+                assert result == [a: 1, bc: 2, 'def': 3]
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::instanceMethod
+    @Test // class::instanceMethod
     void testFunctionCI4() {
-        def err = shouldFail '''
-            import static java.util.stream.Collectors.toList
-
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1, 2, 3].stream().map(String::toString).collect(toList())
-                assert result == ["1", "2", "3"]
+        def err = shouldFail imports + '''
+            @CompileStatic
+            void test() {
+                [1, 2, 3].stream().map(String::toString).collect(Collectors.toList())
             }
-
-            p()
         '''
         assert err =~ /Invalid receiver type: java.lang.Integer is not compatible with java.lang.String/
     }
 
-    // class::instanceMethod -- GROOVY-9814
+    @Test // class::instanceMethod -- GROOVY-9814
     void testFunctionCI5() {
-        assertScript '''
-            import java.util.function.*
-            import groovy.transform.*
-
+        assertScript imports + '''
             @CompileStatic
             class One { String id }
 
             @CompileStatic
             class Two extends One { }
 
-            @CompileStatic @Immutable(knownImmutableClasses=[Function])
+            @CompileStatic @groovy.transform.Immutable(knownImmutableClasses=[Function])
             class FunctionHolder<T> {
                 Function<T, ?> extractor
 
@@ -133,203 +123,350 @@ final class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
-    // class::instanceMethod -- GROOVY-9853
+    @NotYetImplemented
+    @Test // class::instanceMethod -- GROOVY-9813
     void testFunctionCI6() {
-        assertScript '''
-            import java.util.function.*
-            @groovy.transform.CompileStatic
+        String head = imports + '''
+            @CompileStatic
+            class C {
+                def <T> List<T> asList(T... a) {
+                    return Arrays.asList(a)
+                }
+                static main(args) {
+        '''
+        String tail = '''
+                }
+            }
+        '''
+
+        shouldFail head + '''
+            Supplier<List> zero = C::asList
+        ''' + tail
+
+        assertScript head + '''
+            Function<C, List> one = C::asList
+            def list = one.apply(new C())
+            assert list.isEmpty()
+        ''' + tail
+
+        assertScript head + '''
+            BiFunction<C, Integer, List> two = C::asList
+            def list = two.apply(new C(),1)
+            assert list.size() == 1
+            assert list[0] == 1
+        ''' + tail
+    }
+
+    @Test // class::instanceMethod -- GROOVY-9853
+    void testFunctionCI7() {
+        assertScript imports + '''
+            @CompileStatic
             void test() {
                 ToIntFunction<CharSequence> f = CharSequence::size
                 int size = f.applyAsInt("")
                 assert size == 0
             }
+
             test()
         '''
 
-        assertScript '''
-            import java.util.function.*
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             void test() {
                 ToIntFunction<CharSequence> f = CharSequence::length
                 int length = f.applyAsInt("")
                 assert length == 0
             }
+
             test()
         '''
 
-        assertScript '''
-            import java.util.function.*
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             void test() {
                 Function<CharSequence,Integer> f = CharSequence::length
                 Integer length = f.apply("")
                 assert length == 0
             }
+
             test()
         '''
 
-        assertScript '''
-            import java.util.function.*
+        assertScript imports + '''
             import java.util.stream.IntStream
 
-            @groovy.transform.CompileStatic
+            @CompileStatic
             void test() {
                 Function<CharSequence,IntStream> f = CharSequence::chars // default method
                 IntStream chars = f.apply("")
                 assert chars.count() == 0
             }
+
             test()
         '''
 
         if (!isAtLeastJdk('11.0')) return
 
-        assertScript '''
-            import java.util.function.*
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             void test() {
                 ToIntBiFunction<CharSequence,CharSequence> f = CharSequence::compare // static method
                 int result = f.applyAsInt("","")
                 assert result == 0
             }
+
             test()
         '''
     }
 
-    // class::instanceMethod -- GROOVY-10734
-    void testFunctionCI7() {
-        assertScript '''
+    @Test // class::instanceMethod -- GROOVY-10734
+    void testFunctionCI8() {
+        assertScript imports + '''
             class C {
                 String p
             }
-            @groovy.transform.CompileStatic
+
+            @CompileStatic
             Map test(Collection<C> items) {
                 items.stream().collect(
-                    java.util.stream.Collectors.groupingBy(C::getP) // Failed to find the expected method[getP(Object)] in the type[C]
+                    Collectors.groupingBy(C::getP) // Failed to find the expected method[getP(Object)] in the type[C]
                 )
             }
+
             def map = test([new C(p:'foo'), new C(p:'bar'), new C(p:'foo')])
             assert map.foo.size() == 2
             assert map.bar.size() == 1
         '''
     }
 
-    // class::instanceMethod -- GROOVY-9974
+    @Test // class::instanceMethod -- GROOVY-9974
     void testPredicateCI() {
-        assertScript '''
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             void test(List<String> strings = ['']) {
                 strings.removeIf(String::isEmpty)
                 assert strings.isEmpty()
             }
+
             test()
         '''
     }
 
-    // class::instanceMethod -- GROOVY-10791
+    @Test // class::instanceMethod -- GROOVY-10791
     void testBiConsumerCI() {
-        assertScript '''
-            import java.util.function.*
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             def <T> void test(List<T> list, Consumer<? super T> todo) {
                 BiConsumer<List<T>, Consumer<? super T>> binder = List::forEach // default method of Iterator
                 binder.accept(list, todo)
             }
+
             test(['works']) { assert it == 'works' }
         '''
     }
 
-    // class::instanceMethod
+    @Test // class::instanceMethod
     void testBinaryOperatorCI() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, BigDecimal::add)
+                assert result == 6.0G
+            }
+
+            test()
+        '''
+    }
+
+    @NotYetImplemented
+    @Test // instance::instanceMethod -- GROOVY-9813
+    void testFunctionII() {
+        String asList = '''
+            def <T> List<T> asList(T... a) {
+                return Arrays.asList(a)
+            }
+        '''
+
+        assertScript imports + asList + '''
+            @CompileStatic
+            void test() {
+                Supplier<List> zero = this::asList
+                def list = zero.get()
+                assert list.isEmpty()
+            }
+
+            test()
+        '''
 
-                assert 6.0G == result
+        assertScript imports + asList + '''
+            @CompileStatic
+            void test() {
+                Function<Integer, List> one = this::asList
+                def list = one.apply(1)
+                assert list.size() == 1
+                assert list[0] == 1
+            }
+
+            test()
+        '''
+
+        assertScript imports + asList + '''
+            @CompileStatic
+            void test() {
+                BiFunction<Integer, Integer, List> two = this::asList
+                def list = two.apply(2,3)
+                assert list.size() == 2
+                assert list[0] == 2
+                assert list[1] == 3
+            }
+
+            test()
+        '''
+
+        assertScript imports + asList + '''
+            @CompileStatic
+            void test() { def that = this
+                BiFunction<Integer, Integer, List> two = that::asList
+                def list = two.apply(2,3)
+                assert list.size() == 2
+                assert list[0] == 2
+                assert list[1] == 3
             }
 
-            p()
+            test()
         '''
     }
 
-    // instance::instanceMethod
-    void testBinaryOperatorII() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                Adder adder = new Adder()
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
+    @NotYetImplemented
+    @Test // instance::instanceMethod -- GROOVY-10653
+    void testFunctionII2() {
+        assertScript imports + '''
+            class C {
+                List m(... args) {
+                    [this,*args]
+                }
+            }
+
+            @CompileStatic
+            void test(C c) {
+                BiFunction<Integer, Integer, List> two = c::m
+                def list = two.apply(1,2)
+                assert list.size() == 3
+                assert list[0] == c
+                assert list[1] == 1
+                assert list[2] == 2
+            }
+
+            test(new C())
+        '''
+    }
 
-                assert 6.0G == result
+    @NotYetImplemented
+    @Test // instance::instanceGroovyMethod -- GROOVY-10653
+    void testFunctionII3() {
+        assertScript imports + '''
+            @CompileStatic
+            int test(CharSequence chars) {
+                IntSupplier sizing = chars::size // from StringGroovyMethods
+                return sizing.getAsInt()
             }
 
-            p()
+            int size = test("foo")
+            assert size == 3
+        '''
+    }
+
+    @NotYetImplemented
+    @Test // instance::instanceMethod -- GROOVY-10057
+    void testPredicateII() {
+        assertScript imports + '''
+            Class c = Integer
+            Predicate p
+
+            p = c::isInstance
+            assert p.test(null) == false
+            assert p.test('xx') == false
+            assert p.test(1234) == true
+
+            p = c.&isInstance
+            assert p.test(null) == false
+            assert p.test('xx') == false
+            assert p.test(1234) == true
+
+            p = o -> c.isInstance(o)
+            assert p.test(null) == false
+            assert p.test('xx') == false
+            assert p.test(1234) == true
+
+            p = { c.isInstance(it) }
+            assert p.test(null) == false
+            assert p.test('xx') == false
+            assert p.test(1234) == true
+        '''
+    }
 
-            @groovy.transform.CompileStatic
+    @Test // instance::instanceMethod
+    void testBinaryOperatorII() {
+        assertScript imports + '''
             class Adder {
                 BigDecimal add(BigDecimal a, BigDecimal b) {
                     a.add(b)
                 }
             }
-        '''
-    }
 
-    // instance::instanceMethod
-    void testBinaryOperatorII_COMPATIBLE() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
+            @CompileStatic
+            void test() {
                 Adder adder = new Adder()
                 def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
-
-                assert 6.0G == result
+                assert result == 6.0G
             }
 
-            p()
+            test()
+        '''
+    }
 
-            @groovy.transform.CompileStatic
+    @Test // instance::instanceMethod
+    void testBinaryOperatorII_COMPATIBLE() {
+        assertScript imports + '''
             class Adder {
                 BigDecimal add(Number a, Number b) {
                     ((BigDecimal) a).add((BigDecimal) b)
                 }
             }
-        '''
-    }
-
-    // expression::instanceMethod
-    void testBinaryOperatorII_EXPRESSION() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder()::add)
 
-                assert 6.0G == result
+            @CompileStatic
+            void test() {
+                Adder adder = new Adder()
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
+                assert result == 6.0G
             }
 
-            p()
+            test()
+        '''
+    }
 
-            @groovy.transform.CompileStatic
+    @Test // expression::instanceMethod
+    void testBinaryOperatorII_EXPRESSION() {
+        assertScript imports + '''
             class Adder {
                 public BigDecimal add(BigDecimal a, BigDecimal b) {
                     a.add(b)
                 }
             }
-        '''
-    }
-
-    // expression::instanceMethod
-    void testBinaryOperatorII_EXPRESSION2() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder().getThis()::add)
 
-                assert new BigDecimal(6) == result
+            @CompileStatic
+            void test() {
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder()::add)
+                assert result == 6.0G
             }
 
-            p()
+            test()
+        '''
+    }
 
-            @groovy.transform.CompileStatic
+    @Test // expression::instanceMethod
+    void testBinaryOperatorII_EXPRESSION2() {
+        assertScript imports + '''
             class Adder {
                 BigDecimal add(BigDecimal a, BigDecimal b) {
                     a.add(b)
@@ -339,65 +476,100 @@ final class MethodReferenceTest extends GroovyTestCase {
                     return this
                 }
             }
-        '''
-    }
-
-    // instance::staticMethod
-    void testBinaryOperatorIS() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                Adder adder = new Adder()
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
 
-                assert 6.0G == result
+            @CompileStatic
+            void test() {
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder().getThis()::add)
+                assert result == new BigDecimal(6)
             }
 
-            p()
+            test()
+        '''
+    }
 
-            @groovy.transform.CompileStatic
+    @Test // instance::instanceMethod
+    void testBinaryOperatorII_RHS() {
+        assertScript imports + '''
             class Adder {
-                static BigDecimal add(BigDecimal a, BigDecimal b) {
+                BigDecimal add(BigDecimal a, BigDecimal b) {
                     a.add(b)
                 }
             }
+
+            @CompileStatic
+            void test() {
+                Adder adder = new Adder()
+                BinaryOperator<BigDecimal> b = adder::add
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, b)
+                assert result == 6.0G
+            }
+
+            test()
         '''
     }
 
-    // expression::staticMethod
-    void testBinaryOperatorIS_EXPRESSION() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder()::add)
+    @Test // expression::instanceMethod
+    void testBinaryOperatorII_RHS2() {
+        assertScript imports + '''
+            class Adder {
+                BigDecimal add(BigDecimal a, BigDecimal b) {
+                    a.add(b)
+                }
+            }
 
-                assert 6.0G == result
+            @CompileStatic
+            void test() {
+                BinaryOperator<BigDecimal> b = new Adder()::add
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, b)
+                assert result == 6.0G
             }
 
-            p()
+            test()
+        '''
+    }
 
-            @groovy.transform.CompileStatic
+    @Test // instance::staticMethod
+    void testBinaryOperatorIS() {
+        assertScript imports + '''
             class Adder {
                 static BigDecimal add(BigDecimal a, BigDecimal b) {
                     a.add(b)
                 }
             }
+
+            @CompileStatic
+            void test() {
+                Adder adder = new Adder()
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
+                assert result == 6.0G
+            }
+
+            test()
         '''
     }
 
-    // expression::staticMethod
-    void testBinaryOperatorIS_EXPRESSION2() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, Adder.newInstance()::add)
+    @Test // expression::staticMethod
+    void testBinaryOperatorIS_EXPRESSION() {
+        assertScript imports + '''
+            class Adder {
+                static BigDecimal add(BigDecimal a, BigDecimal b) {
+                    a.add(b)
+                }
+            }
 
-                assert 6.0G == result
+            @CompileStatic
+            void test() {
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder()::add)
+                assert result == 6.0G
             }
 
-            p()
+            test()
+        '''
+    }
 
-            @groovy.transform.CompileStatic
+    @Test // expression::staticMethod
+    void testBinaryOperatorIS_EXPRESSION2() {
+        assertScript imports + '''
             class Adder {
                 static BigDecimal add(BigDecimal a, BigDecimal b) {
                     a.add(b)
@@ -407,43 +579,48 @@ final class MethodReferenceTest extends GroovyTestCase {
                     new Adder()
                 }
             }
+
+            @CompileStatic
+            void test() {
+                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, Adder.newInstance()::add)
+                assert result == 6.0G
+            }
+
+            test()
         '''
     }
 
-    // arrayClass::new
+    @Test // class::new
     void testFunctionCN() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1, 2, 3].stream().toArray(Integer[]::new)
-                assert result == new Integer[] { 1, 2, 3 }
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                def result = ["1", "2", "3"].stream().map(Integer::new).collect(Collectors.toList())
+                assert result == [1, 2, 3]
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::new
+    @Test // class::new
     void testFunctionCN2() {
-        assertScript '''
-            import static java.util.stream.Collectors.toList
-
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = ["1", "2", "3"].stream().map(Integer::new).collect(toList())
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                Function<String, Integer> f = Integer::new
+                def result = ["1", "2", "3"].stream().map(f).collect(Collectors.toList())
                 assert result == [1, 2, 3]
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::new -- GROOVY-10033
+    @Test // class::new -- GROOVY-10033
     void testFunctionCN3() {
-        assertScript '''
-            import java.util.function.Function
-
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             class C {
                 C(Function<String,Integer> f) {
                     def i = f.apply('42')
@@ -453,15 +630,14 @@ final class MethodReferenceTest extends GroovyTestCase {
                     new C(Integer::new)
                 }
             }
+
             C.test()
         '''
     }
 
-    // class::new -- GROOVY-10033
+    @Test // class::new -- GROOVY-10033
     void testFunctionCN4() {
-        assertScript '''
-            import java.util.function.Function
-
+        assertScript imports + '''
             class A {
                 A(Function<A,B> f) {
                     B b = f.apply(this)
@@ -473,7 +649,7 @@ final class MethodReferenceTest extends GroovyTestCase {
                     assert a != null
                 }
             }
-            @groovy.transform.CompileStatic
+            @CompileStatic
             class X extends A {
               public X() {
                 super(Y::new)
@@ -489,41 +665,50 @@ final class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
-    // class::staticMethod
-    void testFunctionCS() {
-        assertScript '''
-            import static java.util.stream.Collectors.toList
+    @Test // arrayClass::new
+    void testIntFunctionCN() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                IntFunction<Integer[]> f = Integer[]::new;
+                def result = [1, 2, 3].stream().toArray(f)
+                result == new Integer[] {1, 2, 3}
+            }
+
+            test()
+        '''
+    }
 
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1, -2, 3].stream().map(Math::abs).collect(toList())
+    @Test // class::staticMethod
+    void testFunctionCS() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                def result = [1, -2, 3].stream().map(Math::abs).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::staticMethod
+    @Test // class::staticMethod
     void testFunctionCS2() {
-        assertScript '''
-            import java.util.function.Function
-            import static java.util.stream.Collectors.toMap
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 List<String> list = ['x','y','z']
-                def map = list.stream().collect(toMap(Function.identity(), Collections::singletonList))
+                def map = list.stream().collect(Collectors.toMap(Function.identity(), Collections::singletonList))
                 assert map == [x: ['x'], y: ['y'], z: ['z']]
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::staticMethod -- GROOVY-9799
+    @Test // class::staticMethod -- GROOVY-9799
     void testFunctionCS3() {
-        assertScript '''
+        assertScript imports + '''
             class C {
                 String x
             }
@@ -535,7 +720,7 @@ final class MethodReferenceTest extends GroovyTestCase {
                 }
             }
 
-            @groovy.transform.CompileStatic
+            @CompileStatic
             def test(C c) {
                 Optional.of(c).map(D::from).get()
             }
@@ -545,81 +730,78 @@ final class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
-    // class::staticMethod
+    @Test // class::staticMethod
     void testFunctionCS4() {
-        assertScript '''
-            import java.util.function.Function
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 Function<Integer, Integer> f = Math::abs
                 def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::staticMethod
+    @Test // class::staticMethod
     void testFunctionCS5() {
-        assertScript '''
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def f = Math::abs // No explicit type defined, so it is actually a method closure. We can make it smarter in a later version.
                 def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::staticMethod -- GROOVY-9813
     @NotYetImplemented
+    @Test // class::staticMethod -- GROOVY-9813
     void testFunctionCS6() {
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                java.util.function.Supplier<List> zero = Arrays::asList
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                Supplier<List> zero = Arrays::asList
                 def list = zero.get()
                 assert list.isEmpty()
             }
 
-            p()
+            test()
         '''
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                java.util.function.Function<Integer, List> one = Arrays::asList
+
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                Function<Integer, List> one = Arrays::asList
                 def list = one.apply(1)
                 assert list.size() == 1
                 assert list[0] == 1
             }
 
-            p()
+            test()
         '''
-        assertScript '''
-            @groovy.transform.CompileStatic
-            void p() {
-                java.util.function.BiFunction<Integer, Integer, List> two = Arrays::asList
+
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                BiFunction<Integer, Integer, List> two = Arrays::asList
                 def list = two.apply(2,3)
                 assert list.size() == 2
                 assert list[0] == 2
                 assert list[1] == 3
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::staticMethod -- GROOVY-10807
+    @Test // class::staticMethod -- GROOVY-10807
     void testFunctionCS7() {
-        assertScript '''
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             class C {
                 public static Comparator<String> c = Comparator.<String,String>comparing(C::m)
                 static String m(String string) {
@@ -634,11 +816,11 @@ final class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
-    // class::staticMethod
     @NotYetImplemented
+    @Test // class::staticMethod
     void testFunctionCS8() {
-        assertScript '''
-            @groovy.transform.CompileStatic
+        assertScript imports + '''
+            @CompileStatic
             class C {
                 public static Comparator<String> c = Comparator.comparing(C::m)
                 static String m(String string) {
@@ -653,157 +835,51 @@ final class MethodReferenceTest extends GroovyTestCase {
         '''
     }
 
-    // instance::instanceMethod
-    void testBinaryOperatorII_RHS() {
-        assertScript '''
-            import java.util.function.BinaryOperator
-
-            @groovy.transform.CompileStatic
-            void p() {
-                Adder adder = new Adder()
-                BinaryOperator<BigDecimal> b = adder::add
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, b)
-
-                assert 6.0G == result
-            }
-
-            p()
-
-            @groovy.transform.CompileStatic
-            class Adder {
-                BigDecimal add(BigDecimal a, BigDecimal b) {
-                    a.add(b)
-                }
-            }
-        '''
-    }
-
-    // expression::instanceMethod
-    void testBinaryOperatorII_RHS2() {
-        assertScript '''
-            import java.util.function.BinaryOperator
-
-            @groovy.transform.CompileStatic
-            void p() {
-                BinaryOperator<BigDecimal> b = new Adder()::add
-                def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, b)
-
-                assert 6.0G == result
-            }
-
-            p()
-
-            @groovy.transform.CompileStatic
-            class Adder {
-                BigDecimal add(BigDecimal a, BigDecimal b) {
-                    a.add(b)
-                }
-            }
-        '''
-    }
-
-    // class::new
-    void testFunctionCN_RHS() {
-        assertScript '''
-            import java.util.function.Function
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
-                Function<String, Integer> f = Integer::new
-                assert [1, 2, 3] == ["1", "2", "3"].stream().map(f).collect(Collectors.toList())
-            }
-
-            p()
-        '''
-    }
-
-    // arrayClass::new
-    void testIntFunctionCN_RHS() {
-        assertScript '''
-            import java.util.function.IntFunction
-            import java.util.stream.Stream
-
-            @groovy.transform.CompileStatic
-            void p() {
-                IntFunction<Integer[]> f = Integer[]::new
-                assert new Integer[] { 1, 2, 3 } == [1, 2, 3].stream().toArray(f)
-            }
-
-            p()
-        '''
-    }
-
-    // class::instanceMethod
-    void testFunctionCI_WRONGTYPE() {
-        def err = shouldFail '''
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
-                def result = [1, 2, 3].stream().map(String::toString).collect(Collectors.toList())
-                assert 3 == result.size()
-                assert ['1', '2', '3'] == result
-            }
-
-            p()
-        '''
-        assert err =~ /Invalid receiver type: java.lang.Integer is not compatible with java.lang.String/
-    }
-
-    // class::instanceMethod, actually class::staticMethod
+    @Test // class::instanceGroovyMethod
     void testFunctionCI_DGM() {
-        assertScript '''
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def result = ['a', 'ab', 'abc'].stream().map(String::size).collect(Collectors.toList())
                 assert [1, 2, 3] == result
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::staticMethod
+    @Test // class::staticGroovyMethod
     void testFunctionCS_DGSM() {
-        assertScript '''
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
                 assert result.every(e -> e instanceof Thread)
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::instanceMethod
+    @Test // class::instanceGroovyMethod
     void testFunctionCI_SHADOW_DGM() {
-        assertScript '''
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def result = [[a:1], [b:2], [c:3]].stream().map(Object::toString).collect(Collectors.toList())
-                assert 3 == result.size()
-                assert ['[a:1]', '[b:2]', '[c:3]'] == result
+                assert result.size() == 3
+                assert result == ['[a:1]', '[b:2]', '[c:3]']
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::staticMethod
+    @Test // class::staticGroovyMethod
     void testFunctionCS_MULTI_DGSM() {
-        assertScript '''
-            import java.util.stream.Collectors
-
-            @groovy.transform.CompileStatic
-            void p() {
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
                 def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
                 assert result.every(e -> e instanceof Thread)
 
@@ -811,41 +887,106 @@ final class MethodReferenceTest extends GroovyTestCase {
                 assert result.every(e -> e instanceof Thread)
             }
 
-            p()
+            test()
         '''
     }
 
-    // class::unknown
+    @Test
     void testMethodNotFound1() {
-        def err = shouldFail '''
-            @groovy.transform.CompileStatic
-            void p() {
+        def err = shouldFail imports + '''
+            @CompileStatic
+            void test() {
                 [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, BigDecimal::addx)
             }
         '''
-        assert err.contains('Failed to find the expected method[addx(java.math.BigDecimal,java.math.BigDecimal)] in the type[java.math.BigDecimal]')
+        assert err.message.contains('Failed to find the expected method[addx(java.math.BigDecimal,java.math.BigDecimal)] in the type[java.math.BigDecimal]')
     }
 
-    // GROOVY-9463
+    @Test // GROOVY-9463
     void testMethodNotFound2() {
-        def err = shouldFail '''
-            import java.util.function.Function
-
-            @groovy.transform.CompileStatic
-            void p() {
+        def err = shouldFail imports + '''
+            @CompileStatic
+            void test() {
                 Function<String,String> reference = String::toLowerCaseX
             }
         '''
-        assert err.contains('Failed to find the expected method[toLowerCaseX(java.lang.String)] in the type[java.lang.String]')
+        assert err.message.contains('Failed to find the expected method[toLowerCaseX(java.lang.String)] in the type[java.lang.String]')
     }
 
-    // GROOVY-10742
-    void testVoidMethodSelection() {
-        def err = shouldFail '''
-            import java.util.function.Function
+    @NotYetImplemented
+    @Test // GROOVY-10714
+    void testMethodSelection() {
+        assertScript imports + '''
+            class C {
+                String which
+                void m(int i) { which = 'int' }
+                void m(Number n) { which = 'Number' }
+            }
+            interface I {
+                I andThen(Consumer<? super Number> c)
+                I andThen(BiConsumer<? super Number, ?> bc)
+            }
+            @CompileStatic
+            void test(I i, C c) {
+                i = i.andThen(c::m) // "andThen" is ambiguous unless parameters of "m" overloads are taken into account
+            }
 
-            void foo(bar) { }
-            @groovy.transform.CompileStatic
+            C x= new C()
+            test(new I() {
+                I andThen(Consumer<? super Number> c) {
+                    c.accept(42)
+                    return this
+                }
+                I andThen(BiConsumer<? super Number, ?> bc) {
+                    bc.accept(42, null)
+                    return this
+                }
+            }, x)
+            assert x.which == 'Number'
+        '''
+    }
+
+    @Test // GROOVY-10813
+    void testMethodSelection2() {
+        for (spec in ['', '<?>', '<Object>', '<? extends Object>', '<? super String>']) {
+            assertScript imports + """
+                @CompileStatic
+                void test() {
+                    Consumer$spec c = this::print // overloads in Script and DefaultGroovyMethods
+                    c.accept('hello world!')
+                }
+
+                test()
+            """
+        }
+        for (spec in ['', '<?,?>', '<?,Object>', '<?,? extends Object>', '<?,? super String>']) {
+            assertScript imports + """
+                @CompileStatic
+                void test() {
+                    BiConsumer$spec c = Object::print
+                    c.accept(this, 'hello world!')
+                }
+
+                test()
+            """
+        }
+        assertScript imports + '''
+            @CompileStatic
+            void test() {
+                BiConsumer<Script,?> c = Script::print
+                c.accept(this, 'hello world!')
+            }
+
+            test()
+        '''
+    }
+
+    @Test // GROOVY-10742
+    void testIncompatibleReturnType() {
+        def err = shouldFail imports + '''
+            void foo(bar) {
+            }
+            @CompileStatic
             void test() {
                 Function<Object,String> f = this::foo
             }
@@ -853,16 +994,14 @@ final class MethodReferenceTest extends GroovyTestCase {
         assert err =~ /Invalid return type: void is not convertible to java.lang.String/
     }
 
-    // GROOVY-10269
+    @Test // GROOVY-10269
     void testNotFunctionalInterface() {
-        def err = shouldFail '''
-            import java.util.function.Consumer
-
+        def err = shouldFail imports + '''
             void foo(Integer y) {
             }
             void bar(Consumer<Integer> x) {
             }
-            @groovy.transform.CompileStatic
+            @CompileStatic
             void test() {
                 bar(this::foo)
                 def baz = { Consumer<Integer> x -> }
@@ -872,19 +1011,17 @@ final class MethodReferenceTest extends GroovyTestCase {
         assert err =~ /The argument is a method reference, but the parameter type is not a functional interface/
     }
 
-    // GROOVY-10336
+    @Test // GROOVY-10336
     void testNotFunctionalInterface2() {
-        def err = shouldFail '''
-            import java.util.function.Supplier
-
+        def err = shouldFail imports + '''
             class C {
                 Integer m() { 1 }
             }
-            @groovy.transform.CompileStatic
+            @CompileStatic
             void test() {
                 Supplier<Long> outer = () -> {
                     Closure<Long> inner = (Object o, Supplier<Integer> s) -> 2L
-                    inner(new Object(), new C()::m)
+                    inner(new Object(), new C()::m) // TODO: resolve call(Object,Supplier<Integer>)
                 }
             }
         '''