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/09/09 01:48:44 UTC

[groovy] branch master updated: GROOVY-6939, GROOVY-8499, GROOVY-8816: STC: closure/lambda param count

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 ea34146709 GROOVY-6939, GROOVY-8499, GROOVY-8816: STC: closure/lambda param count
ea34146709 is described below

commit ea34146709b5f1afec9d84b00c2311f0bb5c8d89
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Sep 8 18:51:41 2022 -0500

    GROOVY-6939, GROOVY-8499, GROOVY-8816: STC: closure/lambda param count
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   |  17 +--
 .../groovy/runtime/DefaultGroovyMethods.java       |  18 +--
 .../transform/stc/StaticTypeCheckingVisitor.java   |  80 ++++++------
 src/test/groovy/bugs/Groovy8816.groovy             |  39 ------
 src/test/groovy/bugs/Groovy9413.groovy             |  34 +++--
 .../stc/ClosureParamTypeInferenceSTCTest.groovy    | 145 ++++++++++++++++-----
 src/test/groovy/transform/stc/Groovy8247Bug.groovy |  36 -----
 .../groovy/console/ui/text/AutoIndentAction.groovy |  12 +-
 .../provider/collection/runtime/NamedTuple.groovy  |  21 +--
 9 files changed, 208 insertions(+), 194 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 2c5fc7bac6..19926328ec 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -639,15 +639,16 @@ public class GenericsUtils {
             // the returned node is DummyNode<Param1, Param2, Param3, ...)
             ClassNode dummyNode = dummyDeclaration.getLeftExpression().getType();
             GenericsType[] dummyNodeGenericsTypes = dummyNode.getGenericsTypes();
-            if (dummyNodeGenericsTypes == null) {
-                return null;
-            }
-            ClassNode[] signature = new ClassNode[dummyNodeGenericsTypes.length];
-            for (int i = 0, n = dummyNodeGenericsTypes.length; i < n; i += 1) {
-                final GenericsType genericsType = dummyNodeGenericsTypes[i];
-                signature[i] = resolveClassNode(sourceUnit, compilationUnit, mn, usage, genericsType.getType());
+            if (dummyNodeGenericsTypes != null) {
+                int n = dummyNodeGenericsTypes.length;
+                ClassNode[] signature = new ClassNode[n];
+                for (int i = 0; i < n; i += 1) {
+                    GenericsType genericsType = dummyNodeGenericsTypes[i];
+                    signature[i] = genericsType.isWildcard() ? ClassHelper.dynamicType()
+                                    : resolveClassNode(sourceUnit, compilationUnit, mn, usage, genericsType.getType());
+                }
+                return signature;
             }
-            return signature;
         } catch (Exception | LinkageError e) {
             sourceUnit.addError(new IncorrectTypeHintException(mn, e, usage.getLineNumber(), usage.getColumnNumber()));
         }
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 8af9eb325d..6d31ecf1fe 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -2213,7 +2213,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self Object
      * @since 1.0
      */
-    public static <T> T each(T self, Closure closure) {
+    public static <T> T each(T self, @ClosureParams(value=FromString.class, options="?") Closure closure) {
         each(InvokerHelper.asIterator(self), closure);
         return self;
     }
@@ -2260,10 +2260,10 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self Object
      * @since 1.0
      */
-    public static <T> T eachWithIndex(T self, /*@ClosureParams(value=FromString.class, options="?,Integer")*/ Closure closure) {
+    public static <T> T eachWithIndex(T self, @ClosureParams(value=FromString.class, options="?,Integer") Closure closure) {
         final Object[] args = new Object[2];
         int counter = 0;
-        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {
             args[0] = iter.next();
             args[1] = counter++;
             closure.call(args);
@@ -2281,7 +2281,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self Iterable
      * @since 2.3.0
      */
-    public static <T> Iterable<T> eachWithIndex(Iterable<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+    public static <T> Iterable<T> eachWithIndex(Iterable<T> self, @ClosureParams(value=FromString.class, options="T,Integer") Closure closure) {
         eachWithIndex(self.iterator(), closure);
         return self;
     }
@@ -2296,7 +2296,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self Iterator (now exhausted)
      * @since 2.3.0
      */
-    public static <T> Iterator<T> eachWithIndex(Iterator<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+    public static <T> Iterator<T> eachWithIndex(Iterator<T> self, @ClosureParams(value=FromString.class, options="T,Integer") Closure closure) {
         final Object[] args = new Object[2];
         int counter = 0;
         while (self.hasNext()) {
@@ -2317,7 +2317,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self Collection
      * @since 2.4.0
      */
-    public static <T> Collection<T> eachWithIndex(Collection<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+    public static <T> Collection<T> eachWithIndex(Collection<T> self, @ClosureParams(value=FromString.class, options="T,Integer") Closure closure) {
         return (Collection<T>) eachWithIndex((Iterable<T>) self, closure);
     }
 
@@ -2331,7 +2331,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self List
      * @since 2.4.0
      */
-    public static <T> List<T> eachWithIndex(List<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+    public static <T> List<T> eachWithIndex(List<T> self, @ClosureParams(value=FromString.class, options="T,Integer") Closure closure) {
         return (List<T>) eachWithIndex((Iterable<T>) self, closure);
     }
 
@@ -2345,7 +2345,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self Set
      * @since 2.4.0
      */
-    public static <T> Set<T> eachWithIndex(Set<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+    public static <T> Set<T> eachWithIndex(Set<T> self, @ClosureParams(value=FromString.class, options="T,Integer") Closure closure) {
         return (Set<T>) eachWithIndex((Iterable<T>) self, closure);
     }
 
@@ -2359,7 +2359,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
      * @return the self SortedSet
      * @since 2.4.0
      */
-    public static <T> SortedSet<T> eachWithIndex(SortedSet<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+    public static <T> SortedSet<T> eachWithIndex(SortedSet<T> self, @ClosureParams(value=FromString.class, options="T,Integer") Closure closure) {
         return (SortedSet<T>) eachWithIndex((Iterable<T>) self, closure);
     }
 
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 f0af7aab95..464c41043c 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -136,6 +136,7 @@ import java.util.Set;
 import java.util.StringJoiner;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
 import java.util.function.BiPredicate;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -3018,6 +3019,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     }
                 }
                 expression.putNodeMetaData(CLOSURE_ARGUMENTS, paramTypes);
+                if (paramTypes.length != samParamTypes.length) { // GROOVY-8499
+                    addError("Incorrect number of parameters. Expected " + samParamTypes.length + " but found " + paramTypes.length, expression);
+                }
             }
         }
     }
@@ -3065,41 +3069,33 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private void doInferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final MethodNode selectedMethod, final Expression hintClass, final Expression resolverClass, final Expression options) {
-        Parameter[] closureParams = expression.getParameters();
-        if (closureParams == null) return; // no-arg closure
+        Parameter[] closureParams = hasImplicitParameter(expression) ? new Parameter[]{new Parameter(dynamicType(),"it")} : getParametersSafe(expression);
 
         List<ClassNode[]> closureSignatures = getSignaturesFromHint(selectedMethod, hintClass, options, expression);
         List<ClassNode[]> candidates = new LinkedList<>();
         for (ClassNode[] signature : closureSignatures) {
             resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature);
-            if (signature.length == closureParams.length // same number of arguments
-                    || (signature.length == 1 && closureParams.length == 0) // implicit it
+            if (signature.length == closureParams.length // matching number of parameters
                     || (closureParams.length > signature.length && last(signature).isArray())) { // vargs
                 candidates.add(signature);
             }
         }
+
+        if (candidates.isEmpty() && !closureSignatures.isEmpty()) {
+            String spec = closureSignatures.stream().mapToInt(sig -> sig.length).distinct()
+                                           .sorted().mapToObj(Integer::toString).collect(Collectors.joining(" or "));
+            addError("Incorrect number of parameters. Expected " + spec + " but found " + closureParams.length, expression);
+        }
+
         if (candidates.size() > 1) {
             for (Iterator<ClassNode[]> candIt = candidates.iterator(); candIt.hasNext(); ) {
                 ClassNode[] inferred = candIt.next();
-                for (int i = 0, n = closureParams.length; i < n; i += 1) {
-                    Parameter closureParam = closureParams[i];
+                checkClosureSignature(closureParams, inferred, (closureParam, inferredType) -> {
                     ClassNode declaredType = closureParam.getOriginType();
-                    ClassNode inferredType;
-                    if (i < inferred.length - 1 || inferred.length == n) {
-                        inferredType = inferred[i];
-                    } else {
-                        ClassNode lastInferred = inferred[inferred.length - 1];
-                        if (lastInferred.isArray()) {
-                            inferredType = lastInferred.getComponentType();
-                        } else {
-                            candIt.remove();
-                            continue;
-                        }
-                    }
-                    if (!typeCheckMethodArgumentWithGenerics(declaredType, inferredType, i == (n - 1))) {
-                        candIt.remove();
-                    }
-                }
+                    if (!typeCheckMethodArgumentWithGenerics(declaredType, inferredType, false)) candIt.remove();
+                }, () -> {
+                    candIt.remove();
+                });
             }
             if (candidates.size() > 1 && resolverClass instanceof ClassExpression) {
                 candidates = resolveWithResolver(candidates, receiver, arguments, expression, selectedMethod, resolverClass, options);
@@ -3108,28 +3104,38 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 addError("Ambiguous prototypes for closure. More than one target method matches. Please use explicit argument types.", expression);
             }
         }
+
         if (candidates.size() == 1) {
             ClassNode[] inferred = candidates.get(0);
-            if (closureParams.length == 0 && inferred.length == 1) {
+            if (hasImplicitParameter(expression) && inferred.length == 1) {
                 expression.putNodeMetaData(CLOSURE_ARGUMENTS, inferred);
             } else {
-                for (int i = 0, n = closureParams.length; i < n; i += 1) {
-                    Parameter closureParam = closureParams[i];
-                    ClassNode inferredType = OBJECT_TYPE;
-                    if (i < inferred.length - 1 || inferred.length == n) {
-                        inferredType = inferred[i];
-                    } else {
-                        ClassNode lastInferred = inferred[inferred.length - 1];
-                        if (lastInferred.isArray()) {
-                            inferredType = lastInferred.getComponentType();
-                        } else {
-                            addError("Incorrect number of parameters. Expected " + inferred.length + " but found " + n, expression);
-                        }
-                    }
-                    checkParamType(closureParam, inferredType, i == n-1, false);
+                checkClosureSignature(closureParams, inferred, (closureParam, inferredType) -> {
+                    checkParamType(closureParam, inferredType, false, false);
                     typeCheckingContext.controlStructureVariables.put(closureParam, inferredType);
+                }, () -> {
+                    addError("Incorrect number of parameters. Expected " + inferred.length + " but found " + closureParams.length, expression);
+                });
+            }
+        }
+    }
+
+    private static void checkClosureSignature(final Parameter[] declared, final ClassNode[] inferred, final BiConsumer<Parameter,ClassNode> consumer, final Runnable reject) {
+        for (int i = 0, j = inferred.length-1, n = declared.length; i < n; i += 1) {
+            ClassNode declaredType = declared[i].getOriginType();
+            ClassNode inferredType = inferred[Math.min(i, j)];
+            if (isDynamicTyped(inferredType)) continue;
+            if (i >= j) { // at or past end
+                if (inferredType.isArray()) {
+                    if (n > inferred.length || !declaredType.isArray() && !isObjectType(declaredType)) {
+                        inferredType = inferredType.getComponentType(); // spread array out
+                    }
+                } else if (i > j) {
+                    reject.run();
+                    continue;
                 }
             }
+            consumer.accept(declared[i], inferredType);
         }
     }
 
diff --git a/src/test/groovy/bugs/Groovy8816.groovy b/src/test/groovy/bugs/Groovy8816.groovy
deleted file mode 100644
index 0bd56e4e04..0000000000
--- a/src/test/groovy/bugs/Groovy8816.groovy
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package groovy.bugs
-
-import org.junit.Test
-
-import static groovy.test.GroovyAssert.shouldFail
-
-final class Groovy8816 {
-
-    @Test
-    void testCallNoArgClosureWithArg() {
-        def err = shouldFail MissingMethodException, '''
-            @groovy.transform.CompileStatic
-            void test() {
-                [0].each { -> }
-            }
-            test()
-        '''
-
-        assert err =~ /No signature of method: .*\.doCall\(\) is applicable for argument types: \(Integer\) values: \[0\]/
-    }
-}
diff --git a/src/test/groovy/bugs/Groovy9413.groovy b/src/test/groovy/bugs/Groovy9413.groovy
index 03f026c7c9..789decc90e 100644
--- a/src/test/groovy/bugs/Groovy9413.groovy
+++ b/src/test/groovy/bugs/Groovy9413.groovy
@@ -23,7 +23,6 @@ import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
 import org.junit.Test
 
 final class Groovy9413 {
-// .\gradlew --no-daemon --max-workers 2 :test --tests groovy.bugs.Groovy9413 --debug-jvm
     @Test
     void testInterfaceGenerics() {
         def config = new CompilerConfiguration(
@@ -38,49 +37,48 @@ final class Groovy9413 {
                 import java.util.Map;
 
                 interface APSBus {
-                  void send(String target, Map<String, Object> message, APSHandler<APSResult<?>> resultHandler);
+                    void send(String target, Map<String, Object> message, APSHandler<APSResult<?>> resultHandler);
                 }
 
                 interface APSBusRouter {
-                  boolean send(String target, Map<String, Object> message, APSHandler<APSResult<?>> resultHandler);
+                    boolean send(String target, Map<String, Object> message, APSHandler<APSResult<?>> resultHandler);
                 }
 
                 interface APSHandler<T> {
-                  void handle(T value);
+                    void handle(T value);
                 }
 
                 interface APSResult<T> {
                 }
 
                 class APSServiceTracker<Service> {
-                  void withService(WithService<Service> withService, Object... args) throws Exception {
-                  }
+                    void withService(WithService<Service> withService, Object... args) throws Exception {
+                    }
                 }
 
                 interface WithService<Service> {
-                  void withService(Service service, Object... args) throws Exception;
+                    void withService(Service service, Object... args) throws Exception;
                 }
             '''
             def b = new File(parentDir, 'B.groovy')
             b.write '''
                 @groovy.transform.CompileStatic
                 class One9413 implements APSBus {
+                    private APSServiceTracker<APSBusRouter> busRouterTracker
 
-                  private APSServiceTracker<APSBusRouter> busRouterTracker
-
-                  @Override
-                  void send(String target, Map<String, Object> message, APSHandler<APSResult<?>> resultHandler) {
-                    busRouterTracker.withService() { APSBusRouter busRouter ->
-                      busRouter.send(target, message, resultHandler)
+                    @Override
+                    void send(String target, Map<String, Object> message, APSHandler<APSResult<?>> resultHandler) {
+                        busRouterTracker.withService({ APSBusRouter busRouter, Object[] arguments ->
+                            busRouter.send(target, message, resultHandler)
+                        })
                     }
-                  }
                 }
 
                 @groovy.transform.CompileStatic
                 class Two9413 implements APSBusRouter {
-                  @Override
-                  boolean send(String target, Map<String, Object> payload, APSHandler<APSResult<?>> resultHandler) {
-                  }
+                    @Override
+                    boolean send(String target, Map<String, Object> payload, APSHandler<APSResult<?>> resultHandler) {
+                    }
                 }
             '''
 
@@ -89,8 +87,8 @@ final class Groovy9413 {
             cu.addSources(a, b)
             cu.compile()
         } finally {
-            parentDir.deleteDir()
             config.targetDirectory.deleteDir()
+            parentDir.deleteDir()
         }
     }
 }
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index 1f6bd95af2..e1e889f7e4 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -620,10 +620,27 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    void testInferenceForDGM_eachMatch() {
+        assertScript '''
+            'foo bar baz'.eachMatch(~/(?m)^(\s*).*\n$/) { all, ws ->
+                all.trim(); ws.length()
+            }
+        '''
+    }
+
     void testInferenceForDGM_eachWithIndexOnMap() {
         assertScript '''
-            [a:'A',bb:'B',ccc:'C'].eachWithIndex { k,v,i -> assert k.toUpperCase() == v*(1+i) }
             [a:'A',bb:'B',ccc:'C'].eachWithIndex { e,i -> assert e.key.toUpperCase() == e.value*(1+i) }
+            [a:'A',bb:'B',ccc:'C'].eachWithIndex { k,v,i -> assert k.toUpperCase() == v*(1+i) }
+        '''
+    }
+    void testInferenceForDGM_eachWithIndexOnObject() {
+        assertScript '''
+            def foo(object) {
+                object.eachWithIndex { Map<String,String> map, i -> map.ccc.toLowerCase() + (1+i) }
+                //                     ^^^^^^^^^^^^^^^^^^ each/eachWithIndex are flexible
+            }
+            foo([ [a:'A',bb:'B',ccc:'C'] ])
         '''
     }
     void testInferenceForDGM_eachWithIndexOnIterable() {
@@ -1192,34 +1209,112 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-6939
+    void testParamCountCheck1() {
+        shouldFailWithMessages '''
+            def m(o) {
+                o.each { x, y -> }
+            }
+        ''',
+        'Incorrect number of parameters. Expected 1 but found 2'
+    }
+
+    // GROOVY-6939
+    void testParamCountCheck2() {
+        shouldFailWithMessages '''
+            def m(o) {
+                o.eachWithIndex { x, y, z -> }
+            }
+        ''',
+        'Incorrect number of parameters. Expected 2 but found 3'
+    }
+
+    // GROOVY-6939
+    void testParamCountCheck3() {
+        shouldFailWithMessages '''
+            def m(o) {
+                o.eachWithIndex { print it }
+            }
+        ''',
+        'Incorrect number of parameters. Expected 2 but found 1'
+    }
+
+    // GROOVY-6939
+    void testParamCountCheck4() {
+        shouldFailWithMessages '''
+            def m(... array) {
+                array.each { x, y -> }
+            }
+        ''',
+        'Incorrect number of parameters. Expected 1 but found 2'
+    }
+
+    // GROOVY-6939
+    void testParamCountCheck5() {
+        shouldFailWithMessages '''
+            def m() {
+                [:].each { -> }
+            }
+        ''',
+        'Incorrect number of parameters. Expected 1 or 2 but found 0'
+    }
+
+    // GROOVY-8816
+    void testParamCountCheck6() {
+        shouldFailWithMessages '''
+            def m() {
+                [].each { -> }
+            }
+        ''',
+        'Incorrect number of parameters. Expected 1 but found 0'
+    }
+
+    // GROOVY-9854
+    void testParamCountCheck7() {
+        shouldFailWithMessages '''
+            switch (42) { case { -> }: break; }
+        ''',
+        'Incorrect number of parameters. Expected 1 but found 0'
+    }
+
+    // GROOVY-9854
+    void testParamCountCheck8() {
+        shouldFailWithMessages '''
+            switch (42) { case { i, j -> }: break; }
+        ''',
+        'Incorrect number of parameters. Expected 1 but found 2'
+    }
+
+    // GROOVY-8499: SAM type
+    void testParamCountCheck9() {
+        shouldFailWithMessages '''
+            ['ab'.chars, '12'.chars].combinations().stream().map((x, y) -> "$x$y")
+        ''',
+        'Incorrect number of parameters. Expected 1 but found 2'
+    }
+
     void testInferenceWithSAMTypeCoercion() {
-        assertScript '''import java.util.concurrent.Callable
+        assertScript '''
             interface Action<T> {
                 void execute(T thing)
             }
 
             class Wrapper<T> {
-
                 private final T thing
-
                 Wrapper(T thing) {
                     this.thing = thing
                 }
-
                 void contravariantTake(Action<? super T> action) {
                     action.execute(thing)
                 }
-
                 void invariantTake(Action<T> action) {
                     action.execute(thing)
                 }
-
             }
 
-            static <T> Wrapper<T> wrap(Callable<T> callable) {
+            static <T> Wrapper<T> wrap(java.util.concurrent.Callable<T> callable) {
                 new Wrapper(callable.call())
             }
-
             static Integer dub(Integer integer) {
                 integer * 2
             }
@@ -1238,6 +1333,8 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    //--------------------------------------------------------------------------
+
     void testGroovy6022() {
         assertScript '''import groovy.transform.stc.SimpleType
             class Item {
@@ -1296,27 +1393,25 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
             assert extractInfo(" ab 12 cdef 34 jhg ") == [[144, 1156], [14, 38], [14, 38]]
         '''
         assertScript '''
-            def method() {
+            def foo() {
               assert "foobarbaz".findAll('b(a)([rz])') { full, a, b -> assert "BA"=="B" + a.toUpperCase() }.size() == 2
               assert "foobarbaz".findAll('ba') { String found -> assert "BA" == found.toUpperCase() }.size() == 2
             }
-
-            method()
+            foo()
         '''
     }
 
     void testGroovy9058() {
         assertScript '''
-            List<Object[]> bar() { [['fee', 'fi'] as Object[], ['fo', 'fum'] as Object[]] }
-
+            List<Object[]> table() {
+                [ ['fee', 'fi'] as Object[], ['fo', 'fum'] as Object[] ]
+            }
             def foo() {
-                def result = []
-                List<Object[]> bar = bar()
-                bar.each { row -> result << row[0].toString().toUpperCase() }
-                result
+                List<String> result = []
+                table().each { row -> result << row[0].toString().toUpperCase() }
+                assert result == ['FEE', 'FO']
             }
-
-            assert foo() == ['FEE', 'FO']
+            foo()
         '''
     }
 
@@ -1468,16 +1563,6 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
             assert result == 'positive'
         '''
 
-        shouldFailWithMessages '''
-            switch (42) { case { -> }: break; }
-        ''',
-        'Incorrect number of parameters. Expected 1 but found 0'
-
-        shouldFailWithMessages '''
-            switch (42) { case { i, j -> }: break; }
-        ''',
-        'Incorrect number of parameters. Expected 1 but found 2'
-
         shouldFailWithMessages '''
             switch (42) { case { String s -> }: break; }
         ''',
diff --git a/src/test/groovy/transform/stc/Groovy8247Bug.groovy b/src/test/groovy/transform/stc/Groovy8247Bug.groovy
deleted file mode 100644
index 7488352b8b..0000000000
--- a/src/test/groovy/transform/stc/Groovy8247Bug.groovy
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-
-package groovy.transform.stc
-
-class Groovy8247Bug extends StaticTypeCheckingTestCase {
-    void testClosureWithExplicitParamNoInferrableArguments() {
-        assertScript '''
-            def runnable(Runnable r) {
-                r.run()
-            }
-            def foo() {
-                runnable { it -> // note explicit it
-                    println it
-                }
-            }
-            foo()
-        '''
-    }
-}
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/AutoIndentAction.groovy b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/AutoIndentAction.groovy
index e7bd67bde6..f8fec3da00 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/AutoIndentAction.groovy
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/AutoIndentAction.groovy
@@ -36,16 +36,14 @@ class AutoIndentAction extends AbstractAction {
         def cursorPos = inputArea.getCaretPosition()
         int rowNum = rootElement.getElementIndex(cursorPos)
         def rowElement = rootElement.getElement(rowNum)
-        int startOffset = rowElement.getStartOffset()
-        int endOffset = rowElement.getEndOffset()
-        String rowContent = inputArea.document.getText(startOffset, endOffset - startOffset);
-        String contentBeforeCursor = inputArea.document.getText(startOffset, cursorPos - startOffset);
+        int startOffset = rowElement.startOffset, endOffset = rowElement.endOffset
+        String rowContent = inputArea.document.getText(startOffset, endOffset - startOffset)
+        String contentBeforeCursor = inputArea.document.getText(startOffset, cursorPos - startOffset)
+
         String whitespaceStr = ''
-        def matcher = (rowContent =~ /(?m)^(\s*).*\n$/)
-        matcher.each { all, ws ->
+        rowContent.eachMatch(~/(?m)^(\s*).*\n$/) { all, ws ->
             whitespaceStr = ws
         }
-
         if (contentBeforeCursor ==~ /(\s)*/) {
             whitespaceStr = contentBeforeCursor
         }
diff --git a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy
index b906279e6d..86206edf03 100644
--- a/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy
+++ b/subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/runtime/NamedTuple.groovy
@@ -18,7 +18,6 @@
  */
 package org.apache.groovy.ginq.provider.collection.runtime
 
-
 import groovy.transform.CompileStatic
 import groovy.transform.PackageScope
 import groovy.transform.stc.POJO
@@ -28,9 +27,9 @@ import groovy.transform.stc.POJO
  *
  * @since 4.0.0
  */
+@CompileStatic
 @PackageScope
 @POJO
-@CompileStatic
 class NamedTuple<E> extends Tuple<E> {
     private static final long serialVersionUID = -5067092453136522209L
     private final Map<String, E> data = new LinkedHashMap<>()
@@ -43,19 +42,19 @@ class NamedTuple<E> extends Tuple<E> {
             throw new IllegalArgumentException("names should be unique: $nameList")
         }
 
-        for (int i = 0, n = nameListSize; i < n; i++) {
+        for (int i = 0, n = nameListSize; i < n; i += 1) {
             data.put(nameList.get(i), elementList.get(i))
         }
     }
 
-    def getAt(String name) {
-        return data.get(name)
-    }
-
     def get(String name) {
         return getAt(name)
     }
 
+    def getAt(String name) {
+        return data.get(name)
+    }
+
     boolean exists(String name) {
         return data.containsKey(name)
     }
@@ -66,8 +65,10 @@ class NamedTuple<E> extends Tuple<E> {
 
     @Override
     String toString() {
-        '(' + nameList.withIndex()
-                .collect((String n, int i) -> { "${n}:${this[i]}" })
-                .join(', ') + ')'
+        StringJoiner sj = new StringJoiner(', ', '(', ')')
+        for (String name : getNameList()) {
+            sj.add(name + ':' + this[name])
+        }
+        sj.toString()
     }
 }