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/12/16 16:20:45 UTC

[groovy] branch master updated: STC: refactor closure/lambda/pointer/reference generics handling

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 06079d32ec STC: refactor closure/lambda/pointer/reference generics handling
06079d32ec is described below

commit 06079d32ecb26c17a3db6b0bc99ea01d1c7c58d1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Dec 16 09:40:21 2022 -0600

    STC: refactor closure/lambda/pointer/reference generics handling
---
 .../transform/stc/StaticTypeCheckingSupport.java   |   2 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 188 ++++++++-------------
 .../groovy/transform/stc/StaticTypesMarker.java    |  10 +-
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  37 ++--
 .../transform/stc/MethodReferenceTest.groovy       |  26 +--
 5 files changed, 101 insertions(+), 162 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 16a797db4c..b96e37015b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1495,7 +1495,7 @@ public abstract class StaticTypeCheckingSupport {
         // the context we compare with in the end is the one of the callsite
         // so far we specified the context of the method declaration only
         // thus for each argument, we try to find the connected generics first
-        Map<GenericsTypeName, GenericsType> connections = new LinkedHashMap<>();
+        Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
         extractGenericsConnections(connections, wrappedArgument, type);
 
         // each new connection must comply with previous connections
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index c833106456..5be61fd0cd 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -986,7 +986,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
                 inferParameterAndReturnTypesOfClosureOnRHS(target, lambda);
                 source.putNodeMetaData(PARAMETER_TYPE, lambda.getNodeMetaData(PARAMETER_TYPE));
-                source.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambda.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
+                source.putNodeMetaData(CLOSURE_ARGUMENTS, extractTypesFromParameters(lambda.getParameters()));
             }
         }
     }
@@ -2819,26 +2819,29 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         for (int i = 0; i < nExpressions; i += 1) {
             Expression expression = expressions.get(i);
             if (visitClosures == (expression instanceof ClosureExpression || expression instanceof MethodPointerExpression)) {
-                ClassNode targetType = null;
                 if (visitClosures && nthParameter != -1) { // GROOVY-10636: vargs call
                     Parameter target = parameters[Math.min(i, nthParameter)];
-                    targetType = target.getType();
+                    ClassNode targetType = target.getType();
                     if (targetType.isArray() && i >= nthParameter)
                         targetType = targetType.getComponentType();
-
+                    boolean coerceToMethod = isSAMType(targetType);
+                    if (coerceToMethod) { // resolve the target parameter's type
+                        Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, selectedMethod, arguments);
+                        targetType = applyGenericsContext(context, targetType);
+                        expression.putNodeMetaData(PARAMETER_TYPE, targetType);
+                    }
                     if (expression instanceof ClosureExpression) {
-                        ClosureExpression source = (ClosureExpression) expression;
-                        checkClosureWithDelegatesTo(receiver, selectedMethod, args(expressions), parameters, source, target);
+                        checkClosureWithDelegatesTo(receiver, selectedMethod, args(expressions), parameters, expression, target);
                         if (i > 0 || !(selectedMethod instanceof ExtensionMethodNode)) {
-                            inferClosureParameterTypes(receiver, arguments, source, target, selectedMethod);
+                            inferClosureParameterTypes(receiver, arguments, (ClosureExpression) expression, target, selectedMethod);
                         }
-                        if (isFunctionalInterface(targetType)) {
-                            storeInferredReturnType(source, GenericsUtils.parameterizeSAM(targetType).getV2());
+                        if (coerceToMethod && targetType.isInterface()) { // @FunctionalInterface
+                            storeInferredReturnType(expression, GenericsUtils.parameterizeSAM(targetType).getV2());
                         } else if (isClosureWithType(targetType)) {
-                            storeInferredReturnType(source, getCombinedBoundType(targetType.getGenericsTypes()[0]));
+                            storeInferredReturnType(expression, getCombinedBoundType(targetType.getGenericsTypes()[0]));
                         }
                     } else if (expression instanceof MethodReferenceExpression) {
-                        if (isFunctionalInterface(targetType)) {
+                        if (coerceToMethod && targetType.isInterface()) { // @FunctionalInterface
                             LambdaExpression lambda = constructLambdaExpressionForMethodReference(
                                                         targetType, (MethodReferenceExpression) expression);
                             inferClosureParameterTypes(receiver, arguments, lambda, target, selectedMethod);
@@ -2850,13 +2853,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
                 expression.visit(this);
                 expression.removeNodeMetaData(DELEGATION_METADATA);
-
-                MethodNode sam = findSAM(targetType);
-                if (sam != null) {
-                    Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, selectedMethod, arguments);
-                    targetType = applyGenericsContext(context, targetType);
-                    expression.putNodeMetaData(PARAMETER_TYPE, targetType);
-                }
             }
             if (i == 0 && parameters.length > 0 && expression instanceof MapExpression) {
                 checkNamedParamsAnnotation(parameters[0], (MapExpression) expression);
@@ -2864,15 +2860,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private LambdaExpression constructLambdaExpressionForMethodReference(final ClassNode functionalInterface, final MethodReferenceExpression methodReference) {
+    private static LambdaExpression constructLambdaExpressionForMethodReference(final ClassNode functionalInterface, final MethodReferenceExpression methodReference) {
         Parameter[] parameters = findSAM(functionalInterface).getParameters();
         int nParameters = parameters.length;
         if (nParameters > 0) {
-            ClassNode firstParamType = dynamicType();
             parameters = new Parameter[nParameters];
-            for (int i = 0; i < nParameters; i += 1) {
-                parameters[i] = new Parameter(i == 0 ? firstParamType : dynamicType(), "p" + i);
-            }
+            for (int i = 0; i < nParameters; i += 1)
+                parameters[i] = new Parameter(dynamicType(), "p" + i);
         }
         return new LambdaExpression(parameters, GENERATED_EMPTY_STATEMENT);
     }
@@ -4358,14 +4352,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         Expression rightExpression = expr.getRightExpression();
 
         if (op == EQUAL || op == ELVIS_EQUAL) {
-            if (rightRedirect.isDerivedFrom(CLOSURE_TYPE)) {
-                MethodNode abstractMethod = findSAM(left);
-                if (abstractMethod != null && (rightExpression instanceof ClosureExpression
-                                            || rightExpression instanceof MethodPointerExpression)) {
-                    return convertClosureTypeToSAMType(rightExpression, right, abstractMethod, left);
-                }
-            }
-
             if (leftExpression instanceof VariableExpression) {
                 ClassNode initialType = getOriginalDeclarationType(leftExpression);
 
@@ -4395,12 +4381,16 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             || LinkedHashSet_TYPE.isDerivedFrom(leftRedirect)) { // GROOVY-6912
                         return getLiteralResultType(left, right, LinkedHashSet_TYPE); // GROOVY-7128
                     }
-                }
-                if (rightExpression instanceof MapExpression) {
+                } else if (rightExpression instanceof MapExpression) {
                     if (MAP_TYPE.equals(leftRedirect)
                             || LinkedHashMap_TYPE.isDerivedFrom(leftRedirect)) {
                         return getLiteralResultType(left, right, LinkedHashMap_TYPE); // GROOVY-7128, GROOVY-9844
                     }
+                } else if (rightExpression instanceof ClosureExpression
+                        || rightExpression instanceof MethodPointerExpression) {
+                    if (isSAMType(leftRedirect)) {
+                        return left; // coercion
+                    }
                 }
             }
 
@@ -5297,10 +5287,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
             if (isVargs ? nArguments >= nParams - 1 : nArguments == nParams) {
                 for (int i = 0; i < nArguments; i += 1) {
-                    if (isNullConstant(expressions.get(i)))
-                        continue; // GROOVY-9984: skip null
+                    Expression argument = expressions.get(i);
+                    if (isNullConstant(argument)) continue; // GROOVY-9984: skip
+                    ClassNode argumentType = getDeclaredOrInferredType(argument);
                     ClassNode paramType = parameters[Math.min(i, nParams - 1)].getType();
-                    ClassNode argumentType = getDeclaredOrInferredType(expressions.get(i));
 
                     if (GenericsUtils.hasUnresolvedGenerics(paramType)) {
                         // if supplying array param with multiple arguments or single non-array argument, infer using element type
@@ -5308,15 +5298,51 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             paramType = paramType.getComponentType();
                         }
 
-                        if (argumentType.equals(CLOSURE_TYPE)) {
-                            MethodNode sam = findSAM(paramType);
-                            if (sam != null) { // adapt closure to functional interface or other single-abstract-method class
-                                argumentType = convertClosureTypeToSAMType(expressions.get(i), argumentType, sam, paramType);
+                        Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
+
+                        if ((argument instanceof ClosureExpression || argument instanceof MethodPointerExpression) && isSAMType(paramType)) {
+                            // target type information
+                            Tuple2<ClassNode[], ClassNode> samParamsAndReturnType = GenericsUtils.parameterizeSAM(paramType);
+                            ClassNode[] q = samParamsAndReturnType.getV1();
+
+                            // source type information
+                            ClassNode returnType = isClosureWithType(argumentType)
+                                    ? getCombinedBoundType(argumentType.getGenericsTypes()[0])
+                                        : wrapTypeIfNecessary(getInferredReturnType(argument));
+                            ClassNode[] p;
+                            if (argument instanceof ClosureExpression) {
+                                ClosureExpression closure = (ClosureExpression) argument;
+                                p = extractTypesFromParameters(getParametersSafe(closure));
+                            } else { // argument instanceof MethodPointerExpression
+                                List<MethodNode> candidates = argument.getNodeMetaData(MethodNode.class);
+                                if (candidates != null && !candidates.isEmpty()) {
+                                    MethodPointerExpression methodPointer = (MethodPointerExpression) argument;
+                                    p = collateMethodReferenceParameterTypes(methodPointer, candidates.get(0));
+                                    if (p.length > 0) {
+                                        for (int j = 0; j < q.length; j += 1) {
+                                            // SAM parameters are like arguments in this case
+                                            extractGenericsConnections(connections, q[j], p[j]);
+                                        }
+                                        // convert the method's generics into the SAM's generics
+                                        returnType = applyGenericsContext(connections, returnType);
+
+                                        connections.clear();
+                                    }
+                                } else {
+                                    p = ClassNode.EMPTY_ARRAY;
+                                }
                             }
+
+                            // parameters and return type correspond to the SAM's
+                            for (int j = 0; j < p.length && j < q.length; j += 1) {
+                                if (!isDynamicTyped(p[j]))
+                                    extractGenericsConnections(connections, wrapTypeIfNecessary(p[j]), q[j]);
+                            }
+                            extractGenericsConnections(connections, returnType, samParamsAndReturnType.getV2());
+                        } else {
+                            extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType);
                         }
 
-                        Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
-                        extractGenericsConnections(connections, wrapTypeIfNecessary(argumentType), paramType);
                         connections.forEach((gtn, gt) -> resolvedPlaceholders.merge(gtn, gt, (gt1, gt2) -> {
                             // GROOVY-10339: incorporate another witness
                             return getCombinedGenericsType(gt1, gt2);
@@ -5456,67 +5482,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    /**
-     * Converts a Closure type to the appropriate SAM type, which can be used to
-     * infer return type generics.
-     *
-     * @param expression  the argument expression
-     * @param closureType the inferred type of {@code expression}
-     * @param samType     the target type for the argument expression
-     * @return SAM type augmented using information from the argument expression
-     */
-    private static ClassNode convertClosureTypeToSAMType(Expression expression, final ClassNode closureType, final MethodNode sam, final ClassNode samType) {
-        Map<GenericsTypeName, GenericsType> samTypeConnections = GenericsUtils.extractPlaceholders(samType);
-        samTypeConnections.replaceAll((xx, gt) -> // GROOVY-9762, GROOVY-9803: reduce "? super T" to "T"
-            Optional.ofNullable(gt.getLowerBound()).map(GenericsType::new).orElse(gt)
-        );
-
-        ClassNode closureReturnType = isClosureWithType(closureType)
-                ? getCombinedBoundType(closureType.getGenericsTypes()[0])
-                : wrapTypeIfNecessary(expression.getNodeMetaData(INFERRED_RETURN_TYPE));
-
-        Parameter[] parameters = sam.getParameters();
-        if (parameters.length > 0 && expression instanceof MethodPointerExpression) {
-            // try to resolve referenced method type parameters in return type
-            MethodPointerExpression mp = (MethodPointerExpression) expression;
-            List<MethodNode> candidates = mp.getNodeMetaData(MethodNode.class);
-            if (candidates != null && !candidates.isEmpty()) {
-                ClassNode[] paramTypes = applyGenericsContext(samTypeConnections, extractTypesFromParameters(parameters));
-                ClassNode[] matchTypes = candidates.stream()
-                        .map(candidate -> collateMethodReferenceParameterTypes(mp, candidate))
-                        .filter(signature -> checkSignatureSuitability(signature, paramTypes))
-                        .findFirst().orElse(null); // TODO: order signatures by param distance
-                if (matchTypes != null) {
-                    Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
-                    for (int i = 0, n = parameters.length; i < n; i += 1) {
-                        // SAM parameters should align with the referenced method's parameters
-                        extractGenericsConnections(connections, paramTypes[i], matchTypes[i]);
-                    }
-                    // convert the method reference's generics into the SAM's generics domain
-                    closureReturnType = applyGenericsContext(connections, closureReturnType);
-                    // apply known generics connections to the SAM's placeholders in the return type
-                    closureReturnType = applyGenericsContext(samTypeConnections, closureReturnType);
-
-                    expression = new ClosureExpression(Arrays.stream(matchTypes).map(t -> new Parameter(t,"")).toArray(Parameter[]::new), null);
-                }
-            }
-        }
-
-        // the SAM's return type exactly corresponds to the inferred closure return type
-        extractGenericsConnections(samTypeConnections, closureReturnType, sam.getReturnType());
-
-        // repeat the same for each parameter given in the ClosureExpression
-        if (parameters.length > 0 && expression instanceof ClosureExpression) {
-            ClassNode[] paramTypes = applyGenericsContext(samTypeConnections, extractTypesFromParameters(parameters));
-            int i = 0;
-            // GROOVY-10054, GROOVY-10699, GROOVY-10749, et al.
-            for (Parameter p : getParametersSafe((ClosureExpression) expression))
-                if (!p.isDynamicTyped()) extractGenericsConnections(samTypeConnections, p.getType(), paramTypes[i++]);
-        }
-
-        return applyGenericsContext(samTypeConnections, samType.redirect());
-    }
-
     private static ClassNode[] collateMethodReferenceParameterTypes(final MethodPointerExpression source, final MethodNode target) {
         Parameter[] params;
 
@@ -5536,21 +5501,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return extractTypesFromParameters(params);
     }
 
-    private static boolean checkSignatureSuitability(final ClassNode[] receiverTypes, final ClassNode[] providerTypes) {
-        int n = receiverTypes.length;
-        if (n != providerTypes.length) {
-            return false;
-        }
-        for (int i = 0; i < n; i += 1) {
-            // for method closure SAM parameters act like arguments
-            if (!isAssignableTo(providerTypes[i], receiverTypes[i])
-                    && !providerTypes[i].isGenericsPlaceHolder()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     private ClassNode getDeclaredOrInferredType(final Expression expression) {
         ClassNode declaredOrInferred;
         // in case of "T t = new ExtendsOrImplementsT()", return T for the expression type
@@ -5650,7 +5600,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     private static Map<GenericsTypeName, GenericsType> extractPlaceHoldersVisibleToDeclaration(final ClassNode receiver, final MethodNode method, final Expression argument) {
         Map<GenericsTypeName, GenericsType> result;
-        if (method.isStatic()) {
+        if (method.isStatic() || (method.isConstructor() && !asBoolean(receiver.getGenericsTypes()))) {
             result = new HashMap<>();
         } else {
             ClassNode declaring = method.getDeclaringClass();
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
index fa66212a7f..8435b0fa5d 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypesMarker.java
@@ -30,9 +30,9 @@ public enum StaticTypesMarker {
     DECLARATION_INFERRED_TYPE,
     /** used to store inferred return type for methods and closures */
     INFERRED_RETURN_TYPE,
-    /** used to store closure argument types on a variable expression */
+    /** used to store expected closure argument types on an expression */
     CLOSURE_ARGUMENTS,
-    /** used to tell that a property expression refers to a readonly property */
+    /** used to tell that a property expression refers to a read-only property */
     READONLY_PROPERTY,
     /** used to store the default expression for a parameter */
     INITIAL_EXPRESSION,
@@ -52,12 +52,8 @@ public enum StaticTypesMarker {
     DYNAMIC_RESOLUTION,
     /** used to store the list of MOP methods that still have to be generated */
     SUPER_MOP_METHOD_REQUIRED,
-    /** used to store the parameter type information of method invocation on an expression */
+    /** used to store the parameter type of method invocation on an expression */
     PARAMETER_TYPE,
-    /** used to store the function interface type information on an expression */
-    INFERRED_FUNCTIONAL_INTERFACE_TYPE,
-    /** used to store the constructed lambda expression for method reference and constructor reference */
-    CONSTRUCTED_LAMBDA_EXPRESSION,
     /** used to store the condition expression type of the switch-case statement */
     SWITCH_CONDITION_EXPRESSION_TYPE,
     /** used to store the result of {@link StaticTypeCheckingVisitor#getType} */
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index b30d8e8749..5cd2d70534 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -302,7 +302,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             assert result == [ 42 ]
         '''
 
-        ['t::cast', 'n -> t.cast(n)', 'n -> (N) n', '{ n -> (N) n }'].each { cast ->
+        for (cast in ['t::cast', 'n -> t.cast(n)', 'n -> (N) n', '{ n -> (N) n }']) {
             assertScript """
                 Set<Number> f() {
                     Collections.<Number>singleton(42)
@@ -458,7 +458,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-8409
     void testReturnTypeInferenceWithMethodGenerics14() {
-        ['R', 'S', 'T', 'U'].each { t -> // BiFunction uses R, T and U
+        for (t in ['R', 'S', 'T', 'U']) { // 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'))
@@ -874,7 +874,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-6232
     void testDiamondInferrenceFromConstructor5() {
-        ['"a",new Object()', 'new Object(),"b"', '"a","b"'].each { args ->
+        for (args in ['"a",new Object()', 'new Object(),"b"', '"a","b"']) {
             assertScript """
                 class C<T> {
                     C(T x, T y) {
@@ -1402,9 +1402,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-10368
     void testDiamondInferrenceFromConstructor25() {
-        ['T', 'T extends Number', 'T extends Object'].each {
+        for (t in ['T', 'T extends Number', 'T extends Object']) {
             assertScript """
-                class C<$it> {
+                class C<$t> {
                     C(p) {
                     }
                 }
@@ -1564,7 +1564,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 def type = node.getNodeMetaData(INFERRED_TYPE)
                 assert type.toString(false) == 'C<java.lang.String>'
             })
-            def y = new C<>((String s) -> { print(s); }) // error: Expected type Object for lambda parameter: s
+            def y = new C<>((String s) -> { print(s) }) // error: Expected type Object for lambda parameter: s
         '''
 
         assertScript '''import java.util.function.Supplier
@@ -2265,7 +2265,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-8961, GROOVY-9915
     void testShouldUseMethodGenericType3() {
-        ['', 'static'].each { mods ->
+        for (mods in ['', 'static']) {
             assertScript """
                 $mods void setX(List<String> strings) {
                 }
@@ -2304,7 +2304,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-9734, GROOVY-9915
     void testShouldUseMethodGenericType4() {
-        ['', 'static'].each { mods ->
+        for (mods in ['', 'static']) {
             assertScript """
                 $mods void m(List<String> strings) {
                 }
@@ -2392,7 +2392,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             assert opt.get() == 42
         '''
         // same as above but with separate type parameter name for each location
-        ['D.&wrap', 'Collections.&singleton', '{x -> [x].toSet()}', '{Collections.singleton(it)}'].each { toSet ->
+        for (toSet in ['D.&wrap', 'Collections.&singleton', '{x -> [x].toSet()}', '{Collections.singleton(it)}']) {
             assertScript """
                 abstract class A<I,O> {
                     abstract O apply(I input)
@@ -3158,7 +3158,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-10482
     void testCompatibleArgumentsForPlaceholders6() {
-        ['', 'def', 'public', 'static', '@Deprecated'].each {
+        for (it in ['', 'def', 'public', 'static', '@Deprecated']) {
             assertScript """
                 class Foo<X> {
                     Foo(X x) {
@@ -3176,7 +3176,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-10499
     void testCompatibleArgumentsForPlaceholders7() {
-        ['?', 'Y', '? extends Y'].each {
+        for (it in ['?', 'Y', '? extends Y']) {
             assertScript """
                 class Foo<X> {
                     Foo(X x) {
@@ -3247,14 +3247,14 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-10153
     void testCompatibleArgumentsForPlaceholders11() {
-        ['A', 'B', 'C'].each { T ->
+        for (t in ['A', 'B', 'C']) {
             assertScript """
                 class A {}
                 class B extends A {}
                 class C extends B {}
                 class Foo<T extends A> {}
 
-                Foo<? super C> foo = new Foo<$T>()
+                Foo<? super C> foo = new Foo<$t>()
                 //  ^ lower bound is C (explicit); upper bound is A (implicit)
             """
         }
@@ -3268,7 +3268,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '#test(int, java.lang.String). Please check if the declared type is correct and if the method exists.'
     }
 
-    @NotYetImplemented // GROOVY-7720
+    // GROOVY-7720
+    @NotYetImplemented
     void testIncompatibleArgumentsForPlaceholders2() {
         shouldFailWithMessages '''
             class Consumer<T> {
@@ -3803,7 +3804,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-5893
     void testPlusInClosure() {
-        ['def', 'var', 'Object', 'Number', 'Integer', 'Comparable'].each { type ->
+        for (type in ['def', 'var', 'Object', 'Number', 'Integer', 'Comparable']) {
             assertScript """
                 List<Integer> list = [1, 2, 3]
 
@@ -4621,7 +4622,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     void testContravariantMethodResolutionWithImplicitCoercion2() {
-        ['public', 'static'].each { modifier ->
+        for (modifier in ['public', 'static']) {
             assertScript """
                 $modifier <I, O> void transform(java.util.function.Function<? super I, ? extends O> function) {
                     function.apply('')
@@ -4868,7 +4869,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
 
         // GROOVY-9821
-        ['.', '?.', '*.'].each { op ->
+        for (op in ['.', '?.', '*.']) {
             File parentDir = File.createTempDir()
             config.with {
                 targetDirectory = File.createTempDir()
@@ -5043,7 +5044,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-10556
     void testSelfReferentialTypeParameter3() {
-        ['(B) this', 'this as B'].each { self ->
+        for (self in ['(B) this', 'this as B']) {
             assertScript """
                 abstract class A<B extends A<B,X>,X> {
                     B m() {
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 29cc338a05..d9879ce7ed 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -643,14 +643,14 @@ final class MethodReferenceTest {
             }
             @CompileStatic
             class X extends A {
-              public X() {
-                super(Y::new)
-              }
-              private static class Y extends B {
-                Y(A a) {
-                  super(a)
+                public X() {
+                    super(Y::new)
+                }
+                private static class Y extends B {
+                    Y(A a) {
+                      super(a)
+                    }
                 }
-              }
             }
 
             new X()
@@ -970,7 +970,7 @@ final class MethodReferenceTest {
         }
     }
 
-    @Test // GROOVY-10742, GROOVY-10858
+    @Test // GROOVY-10742
     void testIncompatibleReturnType() {
         def err = shouldFail shell, '''
             void foo(bar) {
@@ -980,15 +980,7 @@ final class MethodReferenceTest {
                 Function<Object,String> f = this::foo
             }
         '''
-        assert err =~ /Cannot assign java.util.function.Function<java.lang.Object, java.lang.Void> to: java.util.function.Function<java.lang.Object, java.lang.String>/
-
-        err = shouldFail shell, '''
-            @CompileStatic
-            void test() {
-                Function<Object,Number> f = Object::toString
-            }
-        '''
-        assert err =~ /Cannot assign java.util.function.Function<java.lang.Object, java.lang.String> to: java.util.function.Function<java.lang.Object, java.lang.Number>/
+        assert err =~ /Invalid return type: void is not convertible to java.lang.String/
     }
 
     @Test // GROOVY-10269