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 2021/05/02 23:12:37 UTC

[groovy] branch master updated (fb90e6b -> 356a7a2)

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

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


    from fb90e6b  Remove deprecated parser rules
     new d81e71b  GROOVY-10071: SC/STC: variadic closure parameter checks and errors
     new 356a7a2  GROOVY-10072: SC/STC: support "def c = { p = defaultValue -> return p }"

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


Summary of changes:
 .../groovy/classgen/asm/ClosureWriter.java         |   4 +-
 .../classgen/asm/sc/StaticTypesClosureWriter.java  |  38 +++++--
 ...StaticTypesMethodReferenceExpressionWriter.java |   4 +-
 .../transform/stc/StaticTypeCheckingSupport.java   |  34 +++----
 .../transform/stc/StaticTypeCheckingVisitor.java   |  75 +++++++-------
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 112 +++++++++++++--------
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  11 +-
 .../groovy/transform/stc/MethodCallsSTCTest.groovy |   2 +-
 .../asm/sc/StaticCompileClosureCallTest.groovy     |  96 +++++++-----------
 9 files changed, 195 insertions(+), 181 deletions(-)

[groovy] 02/02: GROOVY-10072: SC/STC: support "def c = { p = defaultValue -> return p }"

Posted by em...@apache.org.
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

commit 356a7a2c58915af24d105d96d98a17ae1f86d7cb
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun May 2 16:49:41 2021 -0500

    GROOVY-10072: SC/STC: support "def c = { p = defaultValue -> return p }"
---
 .../groovy/classgen/asm/ClosureWriter.java         |  4 +-
 .../classgen/asm/sc/StaticTypesClosureWriter.java  |  4 +-
 ...StaticTypesMethodReferenceExpressionWriter.java |  4 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 44 ++++++++++++----------
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 17 ++++++++-
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  1 -
 6 files changed, 47 insertions(+), 27 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
index 607d6e3..dda126d 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -31,7 +31,6 @@ import org.codehaus.groovy.ast.Variable;
 import org.codehaus.groovy.ast.VariableScope;
 import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.FieldExpression;
@@ -54,6 +53,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
@@ -196,7 +196,7 @@ public class ClosureWriter {
             parameters = Parameter.EMPTY_ARRAY;
         } else if (parameters.length == 0) {
             // let's create a default 'it' parameter
-            Parameter it = param(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
+            Parameter it = param(ClassHelper.OBJECT_TYPE, "it", nullX());
             parameters = new Parameter[]{it};
             Variable ref = expression.getVariableScope().getDeclaredVariable("it");
             if (ref != null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
index 89020f1..a1ad0da 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
@@ -96,7 +96,9 @@ public class StaticTypesClosureWriter extends ClosureWriter {
 
     private static Expression defaultArgument(final Parameter parameter) {
         Expression argument;
-        if (parameter.getType().isArray()) {
+        if (parameter.hasInitialExpression()) {
+            argument = parameter.getInitialExpression();
+        } else if (parameter.getType().isArray()) {
             ClassNode elementType = parameter.getType().getComponentType();
             argument = new ArrayExpression(elementType, null, Collections.singletonList(constX(0, true)));
         } else {
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 6294174..222e9ee 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
@@ -28,7 +28,6 @@ import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
 import org.codehaus.groovy.ast.expr.ArrayExpression;
 import org.codehaus.groovy.ast.expr.ClassExpression;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
 import org.codehaus.groovy.ast.expr.MethodReferenceExpression;
@@ -53,6 +52,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
 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;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersCompatible;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.filterMethodsByVisibility;
@@ -173,7 +173,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
     private MethodNode addSyntheticMethodForDGSM(final MethodNode mn) {
         Parameter[] parameters = removeFirstParameter(mn.getParameters());
         ArgumentListExpression args = args(parameters);
-        args.getExpressions().add(0, ConstantExpression.NULL);
+        args.getExpressions().add(0, nullX());
 
         MethodCallExpression returnValue = callX(classX(mn.getDeclaringClass()), mn.getName(), args);
         returnValue.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, mn);
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 3aad94a..d3d5159 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -238,7 +238,7 @@ import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Matche
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.NUMBER_OPS;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.UNKNOWN_PARAMETER_TYPE;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.addMethodLevelDeclaredGenerics;
-import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.allParametersAndArgumentsMatch;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.allParametersAndArgumentsMatchWithDefaultParams;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsConnections;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsContext;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.applyGenericsContextToParameterClass;
@@ -1852,30 +1852,32 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             typeCheckingContext.isInStaticContext = node.isInStaticContext();
             currentField = node;
             visitAnnotations(node);
-            Expression init = node.getInitialExpression();
-            if (init != null) {
-                ClassNode lType = getType(node);
-                if (isFunctionalInterface(lType)) { // GROOVY-9977
-                    processFunctionalInterfaceAssignment(lType, init);
-                } else if (isClosureWithType(lType) && init instanceof ClosureExpression) {
-                    storeInferredReturnType(init, getCombinedBoundType(lType.getGenericsTypes()[0]));
-                }
-                init.visit(this);
-                ClassNode rType = getType(init);
-                if (init instanceof ConstructorCallExpression) {
-                    inferDiamondType((ConstructorCallExpression) init, lType);
-                }
-
-                FieldExpression left = new FieldExpression(node);
-                BinaryExpression bexp = assignX(left, init, node);
-                typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
-            }
+            visitInitialExpression(node.getInitialExpression(), new FieldExpression(node), node);
         } finally {
             currentField = null;
             typeCheckingContext.isInStaticContext = osc;
         }
     }
 
+    private void visitInitialExpression(final Expression value, final Expression target, final ASTNode position) {
+        if (value != null) {
+            ClassNode lType = target.getType();
+            if (isFunctionalInterface(lType)) { // GROOVY-9977
+                processFunctionalInterfaceAssignment(lType, value);
+            } else if (isClosureWithType(lType) && value instanceof ClosureExpression) {
+                storeInferredReturnType(value, getCombinedBoundType(lType.getGenericsTypes()[0]));
+            }
+            value.visit(this);
+            ClassNode rType = getType(value);
+            if (value instanceof ConstructorCallExpression) {
+                inferDiamondType((ConstructorCallExpression) value, lType);
+            }
+
+            BinaryExpression bexp = assignX(target, value, position);
+            typeCheckAssignment(bexp, target, lType, value, getResultType(lType, ASSIGN, rType, bexp));
+        }
+    }
+
     @Override
     public void visitForLoop(final ForStatement forLoop) {
         // collect every variable expression used in the loop body
@@ -2405,6 +2407,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         typeCheckingContext.isInStaticContext = oldStaticContext;
         for (Parameter parameter : getParametersSafe(expression)) {
             typeCheckingContext.controlStructureVariables.remove(parameter);
+            // GROOVY-10071: visit param default argument expression if present
+            visitInitialExpression(parameter.getInitialExpression(), varX(parameter), parameter);
         }
     }
 
@@ -3880,7 +3884,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     protected void typeCheckClosureCall(final Expression arguments, final ClassNode[] argumentTypes, final Parameter[] parameters) {
-        if (allParametersAndArgumentsMatch(parameters, argumentTypes) < 0 && lastArgMatchesVarg(parameters, argumentTypes) < 0) {
+        if (allParametersAndArgumentsMatchWithDefaultParams(parameters, argumentTypes) < 0 && lastArgMatchesVarg(parameters, argumentTypes) < 0) {
             addStaticTypeError("Cannot call closure that accepts " + formatArgumentList(extractTypesFromParameters(parameters)) + " with " + formatArgumentList(argumentTypes), arguments);
         }
     }
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 3af0ba9..fb6ffe9 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -55,6 +55,21 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-10072
+    void testClosureWithoutArguments5() {
+        assertScript '''
+            def c = { p = 'foo' -> return p }
+            assert c('bar') == 'bar'
+            assert c() == 'foo'
+        '''
+
+        assertScript '''
+            def c = { p, q = 'baz' -> '' + p + q }
+            assert c('foo', 'bar') == 'foobar'
+            assert c('foo') == 'foobaz'
+        '''
+    }
+
     void testClosureWithArguments1() {
         assertScript '''
             def c = { int a, int b -> a + b }
@@ -62,7 +77,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         '''
 
         shouldFailWithMessages '''
-            def c = { int a, int b -> print a + b }
+            def c = { int a, int b -> a + b }
             c('5', '7')
         ''',
         'Cannot call closure that accepts [int, int] with [java.lang.String, java.lang.String]'
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index b1ae721..811a5d9 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -650,7 +650,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented
     void testDiamondInferrenceFromConstructor9b() {
         assertScript '''
             @groovy.transform.TupleConstructor(defaults=false)

[groovy] 01/02: GROOVY-10071: SC/STC: variadic closure parameter checks and errors

Posted by em...@apache.org.
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

commit d81e71b79fa1a1d19c5c4112982196c9d0d9f744
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun May 2 16:27:28 2021 -0500

    GROOVY-10071: SC/STC: variadic closure parameter checks and errors
---
 .../classgen/asm/sc/StaticTypesClosureWriter.java  | 36 +++++---
 .../transform/stc/StaticTypeCheckingSupport.java   | 34 ++++----
 .../transform/stc/StaticTypeCheckingVisitor.java   | 33 +++-----
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 99 ++++++++++++----------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 10 +--
 .../groovy/transform/stc/MethodCallsSTCTest.groovy |  2 +-
 .../asm/sc/StaticCompileClosureCallTest.groovy     | 96 ++++++++-------------
 7 files changed, 152 insertions(+), 158 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
index e10c8c9..89020f1 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesClosureWriter.java
@@ -24,6 +24,7 @@ import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
 import org.codehaus.groovy.ast.expr.ClosureExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
@@ -34,13 +35,14 @@ import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
 import org.codehaus.groovy.transform.stc.StaticTypesMarker;
 import org.objectweb.asm.Opcodes;
 
+import java.util.Collections;
 import java.util.List;
 
 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.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
 
@@ -77,33 +79,47 @@ public class StaticTypesClosureWriter extends ClosureWriter {
     }
 
     private static void createDirectCallMethod(final ClassNode closureClass, final MethodNode doCallMethod) {
-        // in case there is no "call" method on the closure, we can create a "fast invocation" paths
+        // in case there is no "call" method on the closure, create a "fast invocation" path
         // to avoid going through ClosureMetaClass by call(Object...) method
 
-        // we can't have a specialized version of call(Object...) because the dispatch logic in ClosureMetaClass
-        // is too complex!
+        // we can't have a specialized version of call(Object...) because the dispatch logic
+        // in ClosureMetaClass is too complex!
 
         // call(Object)
-        Parameter args = param(ClassHelper.OBJECT_TYPE, "args");
+        Parameter doCallParam = doCallMethod.getParameters()[0];
+        Parameter args = new Parameter(doCallParam.getType(), "args");
         addGeneratedCallMethod(closureClass, doCallMethod, varX(args), new Parameter[]{args});
 
         // call()
-        addGeneratedCallMethod(closureClass, doCallMethod, constX(null), Parameter.EMPTY_ARRAY);
+        addGeneratedCallMethod(closureClass, doCallMethod, defaultArgument(doCallParam), Parameter.EMPTY_ARRAY);
+    }
+
+    private static Expression defaultArgument(final Parameter parameter) {
+        Expression argument;
+        if (parameter.getType().isArray()) {
+            ClassNode elementType = parameter.getType().getComponentType();
+            argument = new ArrayExpression(elementType, null, Collections.singletonList(constX(0, true)));
+        } else {
+            argument = nullX();
+        }
+        return argument;
     }
 
     private static void addGeneratedCallMethod(ClassNode closureClass, MethodNode doCallMethod, Expression expression, Parameter[] params) {
-        MethodCallExpression doCallarg = callX(varX("this", closureClass), "doCall", args(expression));
-        doCallarg.setImplicitThis(true);
-        doCallarg.setMethodTarget(doCallMethod);
+        MethodCallExpression callDoCall = callX(varX("this", closureClass), "doCall", args(expression));
+        callDoCall.setImplicitThis(true);
+        callDoCall.setMethodTarget(doCallMethod);
         MethodNode call = new MethodNode("call",
                 Opcodes.ACC_PUBLIC,
                 ClassHelper.OBJECT_TYPE,
                 params,
                 ClassNode.EMPTY_ARRAY,
-                returnS(doCallarg));
+                returnS(callDoCall));
         addGeneratedMethod(closureClass, call, true);
     }
 
+    //--------------------------------------------------------------------------
+
     private static final class MethodTargetCompletionVisitor extends ClassCodeVisitorSupport {
 
         private final MethodNode doCallMethod;
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 7a9274c..658ae95 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -65,6 +65,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.TreeSet;
 import java.util.UUID;
 import java.util.regex.Matcher;
@@ -377,7 +378,6 @@ public abstract class StaticTypeCheckingSupport {
     static int allParametersAndArgumentsMatchWithDefaultParams(final Parameter[] parameters, final ClassNode[] argumentTypes) {
         int dist = 0;
         ClassNode ptype = null;
-        // we already know the lengths are equal
         for (int i = 0, j = 0, n = parameters.length; i < n; i += 1) {
             Parameter param = parameters[i];
             ClassNode paramType = param.getType();
@@ -429,16 +429,17 @@ public abstract class StaticTypeCheckingSupport {
      */
     static int lastArgMatchesVarg(final Parameter[] parameters, final ClassNode... argumentTypes) {
         if (!isVargs(parameters)) return -1;
-        // case length ==0 handled already
-        // we have now two cases,
+        int lastParamIndex = parameters.length - 1;
+        if (lastParamIndex == argumentTypes.length) return 0;
+        // two cases remain:
         // the argument is wrapped in the vargs array or
         // the argument is an array that can be used for the vargs part directly
-        // we test only the wrapping part, since the non wrapping is done already
-        ClassNode lastParamType = parameters[parameters.length - 1].getType();
-        ClassNode ptype = lastParamType.getComponentType();
-        ClassNode arg = argumentTypes[argumentTypes.length - 1];
-        if (isNumberType(ptype) && isNumberType(arg) && !getWrapper(ptype).equals(getWrapper(arg))) return -1;
-        return isAssignableTo(arg, ptype) ? min(getDistance(arg, lastParamType), getDistance(arg, ptype)) : -1;
+        // testing only the wrapping case since the non-wrapping is done already
+        ClassNode arrayType = parameters[lastParamIndex].getType();
+        ClassNode elementType = arrayType.getComponentType();
+        ClassNode argumentType = argumentTypes[argumentTypes.length - 1];
+        if (isNumberType(elementType) && isNumberType(argumentType) && !getWrapper(elementType).equals(getWrapper(argumentType))) return -1;
+        return isAssignableTo(argumentType, elementType) ? min(getDistance(argumentType, arrayType), getDistance(argumentType, elementType)) : -1;
     }
 
     /**
@@ -849,16 +850,13 @@ public abstract class StaticTypeCheckingSupport {
     }
 
     static String toMethodParametersString(final String methodName, final ClassNode... parameters) {
-        StringBuilder sb = new StringBuilder(methodName);
-        sb.append('(');
-        if (parameters != null) {
-            for (int i = 0, n = parameters.length; i < n; i += 1) {
-                sb.append(prettyPrintType(parameters[i]));
-                if (i < n - 1) sb.append(", ");
-            }
+        if (parameters == null || parameters.length == 0) return methodName + "()";
+
+        StringJoiner joiner = new StringJoiner(", ", methodName + "(", ")");
+        for (ClassNode parameter : parameters) {
+            joiner.add(prettyPrintType(parameter));
         }
-        sb.append(')');
-        return sb.toString();
+        return joiner.toString();
     }
 
     /**
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 bc27c05..3aad94a 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -131,6 +131,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiPredicate;
@@ -3398,11 +3399,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 if (fieldNode != null) {
                     GenericsType[] genericsTypes = getType(fieldNode).getGenericsTypes();
                     if (genericsTypes != null) {
-                        ClassNode closureReturnType = genericsTypes[0].getType();
                         Parameter[] parameters = fieldNode.getNodeMetaData(CLOSURE_ARGUMENTS);
                         if (parameters != null) {
                             typeCheckClosureCall(callArguments, args, parameters);
                         }
+                        ClassNode closureReturnType = genericsTypes[0].getType();
                         storeType(call, closureReturnType);
                     }
                 } else if (objectExpression instanceof VariableExpression) {
@@ -3427,7 +3428,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 } else if (objectExpression instanceof ClosureExpression) {
                     // we can get actual parameters directly
                     Parameter[] parameters = ((ClosureExpression) objectExpression).getParameters();
-                    typeCheckClosureCall(callArguments, args, parameters);
+                    if (parameters != null) {
+                        typeCheckClosureCall(callArguments, args, parameters);
+                    }
                     ClassNode type = getInferredReturnType(objectExpression);
                     if (type != null) {
                         storeType(call, type);
@@ -3876,17 +3879,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    protected void typeCheckClosureCall(final Expression callArguments, final ClassNode[] args, final Parameter[] parameters) {
-        if (allParametersAndArgumentsMatch(parameters, args) < 0 &&
-                lastArgMatchesVarg(parameters, args) < 0) {
-            StringBuilder sb = new StringBuilder("[");
-            for (int i = 0, parametersLength = parameters.length; i < parametersLength; i += 1) {
-                Parameter parameter = parameters[i];
-                sb.append(parameter.getType().getName());
-                if (i < parametersLength - 1) sb.append(", ");
-            }
-            sb.append("]");
-            addStaticTypeError("Closure argument types: " + sb + " do not match with parameter types: " + formatArgumentList(args), callArguments);
+    protected void typeCheckClosureCall(final Expression arguments, final ClassNode[] argumentTypes, final Parameter[] parameters) {
+        if (allParametersAndArgumentsMatch(parameters, argumentTypes) < 0 && lastArgMatchesVarg(parameters, argumentTypes) < 0) {
+            addStaticTypeError("Cannot call closure that accepts " + formatArgumentList(extractTypesFromParameters(parameters)) + " with " + formatArgumentList(argumentTypes), arguments);
         }
     }
 
@@ -5678,16 +5673,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     protected static String formatArgumentList(final ClassNode[] nodes) {
         if (nodes == null || nodes.length == 0) return "[]";
-        StringBuilder sb = new StringBuilder(24 * nodes.length);
-        sb.append('[');
+
+        StringJoiner joiner = new StringJoiner(", ", "[", "]");
         for (ClassNode node : nodes) {
-            sb.append(prettyPrintType(node));
-            sb.append(", ");
+            joiner.add(prettyPrintType(node));
         }
-        if (sb.length() > 1) {
-            sb.setCharAt(sb.length() - 2, ']');
-        }
-        return sb.toString();
+        return joiner.toString();
     }
 
     private static void putSetterInfo(final Expression exp, final SetterInfo info) {
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index c6e34c6..3af0ba9 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -23,88 +23,101 @@ package groovy.transform.stc
  */
 class ClosuresSTCTest extends StaticTypeCheckingTestCase {
 
-    void testClosureWithoutArguments() {
+    void testClosureWithoutArguments1() {
         assertScript '''
-        def clos = { println "hello!" }
-
-        println "Executing the Closure:"
-        clos() //prints "hello!"
+            def c = { return 'foo' }
+            assert c() == 'foo'
         '''
     }
 
-    // GROOVY-9079: no params to statically type check but shouldn't get NPE
-    void testClosureWithoutArgumentsExplicit() {
+    void testClosureWithoutArguments2() {
         assertScript '''
-            import java.util.concurrent.Callable
+            def c = { -> return 'foo' }
+            assert c() == 'foo'
+        '''
+    }
 
-            String makeFoo() {
-                Callable<String> call = { -> 'foo' }
-                call()
-            }
+    // GROOVY-9079
+    void testClosureWithoutArguments3() {
+        assertScript '''
+            java.util.concurrent.Callable<String> c = { -> return 'foo' }
+            assert c() == 'foo'
+        '''
+    }
 
-            assert makeFoo() == 'foo'
+    // GROOVY-10071
+    void testClosureWithoutArguments4() {
+        assertScript '''
+            def c = { ... zeroOrMore -> return 'foo' + zeroOrMore }
+            assert c('bar', 'baz') == 'foo[bar, baz]'
+            assert c('bar') == 'foo[bar]'
+            assert c() == 'foo[]'
         '''
     }
 
-    void testClosureWithArguments() {
+    void testClosureWithArguments1() {
         assertScript '''
-            def printSum = { int a, int b -> print a+b }
-            printSum( 5, 7 ) //prints "12"
+            def c = { int a, int b -> a + b }
+            assert c(5, 7) == 12
         '''
 
         shouldFailWithMessages '''
-            def printSum = { int a, int b -> print a+b }
-            printSum( '5', '7' ) //prints "12"
-        ''', 'Closure argument types: [int, int] do not match with parameter types: [java.lang.String, java.lang.String]'
+            def c = { int a, int b -> print a + b }
+            c('5', '7')
+        ''',
+        'Cannot call closure that accepts [int, int] with [java.lang.String, java.lang.String]'
     }
 
-    void testClosureWithArgumentsAndNoDef() {
+    void testClosureWithArguments2() {
         assertScript '''
-            { int a, int b -> print a+b }(5,7)
+            def result = { int a, int b -> a + b }(5, 7)
+            assert result == 12
         '''
-    }
 
-    void testClosureWithArgumentsNoDefAndWrongType() {
         shouldFailWithMessages '''
-            { int a, int b -> print a+b }('5',7)
-        ''', 'Closure argument types: [int, int] do not match with parameter types: [java.lang.String, int]'
+            { int a, int b -> a + b }('5', 7)
+        ''',
+        'Cannot call closure that accepts [int, int] with [java.lang.String, int]'
     }
 
-    void testClosureReturnTypeInference1() {
+    // GROOVY-6365
+    void testClosureWithArguments3() {
         assertScript '''
-            def closure = { int x, int y -> return x+y }
-            int total = closure(2,3)
+            def c = { Object[] args -> args.length }
+            assert c('one', 'two') == 2
         '''
     }
 
-    void testClosureReturnTypeInference2() {
-        shouldFailWithMessages '''
-            def closure = { int x, int y -> return x+y }
-            int total = closure('2',3)
-        ''', 'Closure argument types: [int, int] do not match with parameter types: [java.lang.String, int]'
+    void testClosureReturnTypeInference1() {
+        assertScript '''
+            def c = { int a, int b -> return a + b }
+            int total = c(2, 3)
+            assert total == 5
+        '''
     }
 
-    void testClosureReturnTypeInference3() {
+    void testClosureReturnTypeInference2() {
         assertScript '''
-            int total = { int x, int y -> return x+y }(2,3)
+            int total = { int a, int b -> return a + b }(2, 3)
         '''
     }
 
-    void testClosureReturnTypeInference4() {
+    void testClosureReturnTypeInference3() {
         shouldFailWithMessages '''
-            def cl = { int x ->
-                if (x==0) {
-                    1L
+            def c = { int x ->
+                if (x == 0) {
+                    1L // long
                 } else {
                     x // int
                 }
             }
-            byte res = cl(0) // should throw an error because return type inference should be a long
-        ''', 'Possible loss of precision from long to byte'
+            byte res = c(0)
+        ''',
+        'Possible loss of precision from long to byte'
     }
 
     // GROOVY-9907
-    void testClosureReturnTypeInference5() {
+    void testClosureReturnTypeInference4() {
         assertScript '''
             Integer foo(x) {
                 if (x instanceof Integer) {
@@ -118,7 +131,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-9971
-    void testClosureReturnTypeInference6() {
+    void testClosureReturnTypeInference5() {
         assertScript '''
             def m(Closure<String> c) {
                 c.call()
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 058f328..b1ae721 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -81,7 +81,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<String> list = []
             list << 1
-        ''', '[Static type checking] - Cannot call <T> java.util.ArrayList#leftShift(T) with arguments [int] '
+        ''', 'Cannot call <T> java.util.ArrayList#leftShift(T) with arguments [int]'
     }
 
     void testAddOnList2UsingLeftShift() {
@@ -129,14 +129,14 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<Integer> list = new LinkedList<>()
             list.add 'Hello'
-        ''', '[Static type checking] - Cannot find matching method java.util.LinkedList#add(java.lang.String). Please check if the declared type is correct and if the method exists.'
+        ''', 'Cannot find matching method java.util.LinkedList#add(java.lang.String). Please check if the declared type is correct and if the method exists.'
     }
 
     void testAddOnListWithDiamondAndWrongTypeUsingLeftShift() {
         shouldFailWithMessages '''
             List<Integer> list = new LinkedList<>()
             list << 'Hello'
-        ''', '[Static type checking] - Cannot call <T> java.util.LinkedList#leftShift(T) with arguments [java.lang.String]'
+        ''', 'Cannot call <T> java.util.LinkedList#leftShift(T) with arguments [java.lang.String]'
     }
 
     void testAddOnListWithDiamondAndNullUsingLeftShift() {
@@ -2221,7 +2221,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 }
             }
             Baz.qux(['abc'])
-        ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List<java.lang.String>] '
+        ''', 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.List<java.lang.String>]'
     }
 
     // GROOVY-5721
@@ -2627,7 +2627,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             }
             GoodCodeRed.foo()
         ''',
-        "Cannot call <T> GoodCodeRed#attach(GoodCodeRed<java.lang.Long>) with arguments [GoodCodeRed<java.lang.Integer>]"
+        'Cannot call <T> GoodCodeRed#attach(GoodCodeRed<java.lang.Long>) with arguments [GoodCodeRed<java.lang.Integer>]'
     }
 
     void testHiddenGenerics() {
diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
index 495d369..4c0f0d8 100644
--- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
@@ -1158,7 +1158,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             closure(*strings)
         ''',
         'The spread operator cannot be used as argument of method or closure calls with static type checking because the number of arguments cannot be determined at compile time',
-        'Closure argument types: [java.lang.String, java.lang.String, java.lang.String] do not match with parameter types: [java.lang.Object]'
+        'Cannot call closure that accepts [java.lang.String, java.lang.String, java.lang.String] with [java.lang.Object]'
     }
 
     void testBoxingShouldCostMore() {
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureCallTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureCallTest.groovy
index 5189c9f..c680d91 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureCallTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileClosureCallTest.groovy
@@ -23,7 +23,8 @@ import org.codehaus.groovy.classgen.asm.AbstractBytecodeTestCase
 /**
  * Tests for static compilation: checks that closures are called properly.
  */
-class StaticCompileClosureCallTest extends AbstractBytecodeTestCase {
+final class StaticCompileClosureCallTest extends AbstractBytecodeTestCase {
+
     void testShouldCallClosure() {
         def bytecode = compile([method:'m'],'''
             @groovy.transform.CompileStatic
@@ -117,61 +118,61 @@ class StaticCompileClosureCallTest extends AbstractBytecodeTestCase {
 
     void testWriteSharedVariableInClosure() {
         def bytecode = compile([method:'m'],'''
-        @groovy.transform.CompileStatic
-        void m() {
-            String test = 'test'
-            def cl = { test = 'TEST' }
-            cl()
-            assert test == 'TEST'
-        }
+            @groovy.transform.CompileStatic
+            void m() {
+                String test = 'test'
+                def cl = { test = 'TEST' }
+                cl()
+                assert test == 'TEST'
+            }
         ''')
         clazz.newInstance().main()
     }
 
     void testCallPrivateMethodFromClosure() {
         assertScript '''
-        @groovy.transform.CompileStatic
-        class Foo {
-            void m() {
-                String test = 'test'
-                def cl = { test = bar() }
-                cl()
-                assert test == 'TEST'
+            @groovy.transform.CompileStatic
+            class Foo {
+                void m() {
+                    String test = 'test'
+                    def cl = { test = bar() }
+                    cl()
+                    assert test == 'TEST'
+                }
+                private String bar() { 'TEST' }
             }
-            private String bar() { 'TEST' }
-        }
-        new Foo().m()
+            new Foo().m()
         '''
     }
 
     void testCallStaticPrivateMethodFromClosure() {
         assertScript '''
-        @groovy.transform.CompileStatic
-        class Foo {
-            void m() {
-                String test = 'test'
-                def cl = { test = bar() }
-                cl()
-                assert test == 'TEST'
+            @groovy.transform.CompileStatic
+            class Foo {
+                void m() {
+                    String test = 'test'
+                    def cl = { test = bar() }
+                    cl()
+                    assert test == 'TEST'
+                }
+                private static String bar() { 'TEST' }
             }
-            private static String bar() { 'TEST' }
-        }
-        new Foo().m()
+            new Foo().m()
         '''
     }
 
     void testCallMethodWithinClosure() {
         assertScript '''
-        @groovy.transform.CompileStatic
-        class Foo {
-            static void m(StackTraceElement[] trace) {
-                trace.each { StackTraceElement stackTraceElement -> !stackTraceElement.className.startsWith('foo') }
+            @groovy.transform.CompileStatic
+            class Foo {
+                static void m(StackTraceElement[] trace) {
+                    trace.each { StackTraceElement stackTraceElement -> !stackTraceElement.className.startsWith('foo') }
+                }
             }
-        }
-        1
+            1
         '''
     }
-    
+
     // GROOVY-6199
     void testCallClassMethodFromNestedClosure() {
         assertScript '''
@@ -193,29 +194,4 @@ class StaticCompileClosureCallTest extends AbstractBytecodeTestCase {
             assert mc.bool
         '''
     }
-    
-    //GROOVY-6365
-    void testClosureDoCallNotWrapped() {
-        assertScript """
-            @groovy.transform.CompileStatic
-            class MyClass {
-              def method() {
-                final cl = { Object[] args -> args.length }
-                cl('c1-1', 'c1-2')
-              }
-            }
-
-            assert new MyClass().method() == 2
-        """
-        assertScript """
-            class MyClass {
-              def method() {
-                final cl = { Object[] args -> args.length }
-                cl('c1-1', 'c1-2')
-              }
-            }
-
-            assert new MyClass().method() == 2
-        """
-    }
 }