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 be19a80a3b GROOVY-11394: STC: check closure call for zero, default(s), 
and variadic
be19a80a3b is described below

commit be19a80a3b9b1221883167771391ce3d32715847
Author: Eric Milles <[email protected]>
AuthorDate: Sat Jun 1 11:31:34 2024 -0500

    GROOVY-11394: STC: check closure call for zero, default(s), and variadic
---
 .../transform/stc/StaticTypeCheckingSupport.java   | 37 +-------------
 .../transform/stc/StaticTypeCheckingVisitor.java   | 59 ++++++++++++++++++++--
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 17 +++++--
 3 files changed, 69 insertions(+), 44 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 fcc00109dc..516f31a6e7 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -386,41 +386,6 @@ public abstract class StaticTypeCheckingSupport {
         return dist;
     }
 
-    /**
-     * Checks that arguments and parameter types match, expecting that the 
number of parameters is strictly greater
-     * than the number of arguments, allowing possible inclusion of default 
parameters.
-     *
-     * @return -1 if arguments do not match, 0 if arguments are of the exact 
type and >0 when one or more argument is
-     * not of the exact type but still match
-     */
-    static int allParametersAndArgumentsMatchWithDefaultParams(final 
Parameter[] parameters, final ClassNode[] argumentTypes) {
-        int dist = 0;
-        ClassNode ptype = null;
-        for (int i = 0, j = 0, n = parameters.length; i < n; i += 1) {
-            Parameter param = parameters[i];
-            ClassNode paramType = param.getType();
-            ClassNode arg = (j >= argumentTypes.length ? null : 
argumentTypes[j]);
-            if (arg == null || !isAssignableTo(arg, paramType)) {
-                if (!param.hasInitialExpression() && (ptype == null || 
!ptype.equals(paramType))) {
-                    return -1; // no default value
-                }
-                // a default value exists, we can skip this param
-                ptype = null;
-            } else {
-                j += 1;
-                if (!paramType.equals(arg)) {
-                    dist += getDistance(arg, paramType);
-                }
-                if (param.hasInitialExpression()) {
-                    ptype = arg;
-                } else {
-                    ptype = null;
-                }
-            }
-        }
-        return dist;
-    }
-
     /**
      * Checks that excess arguments match the vararg signature parameter.
      *
@@ -446,7 +411,7 @@ public abstract class StaticTypeCheckingSupport {
      * @return -1 if no match, 0 if the last argument is exactly the vararg 
type and 1 if of an assignable type
      */
     @SuppressWarnings("removal")
-    static int lastArgMatchesVarg(final Parameter[] parameters, final 
ClassNode... argumentTypes) {
+    private static int lastArgMatchesVarg(final Parameter[] parameters, final 
ClassNode... argumentTypes) {
         if (!isVargs(parameters)) return -1;
         int lastParamIndex = parameters.length - 1;
         if (lastParamIndex == argumentTypes.length) return 0;
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 2e637cf1d5..4d8effca1e 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -245,6 +245,7 @@ import static 
org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory;
 import static 
org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
 import static org.codehaus.groovy.runtime.ArrayGroovyMethods.asBoolean;
 import static org.codehaus.groovy.runtime.ArrayGroovyMethods.init;
+import static org.codehaus.groovy.runtime.ArrayGroovyMethods.last;
 import static org.codehaus.groovy.syntax.Types.ASSIGN;
 import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
 import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
@@ -274,7 +275,6 @@ import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Linked
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.Matcher_TYPE;
 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.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.boundUnboundedWildcards;
@@ -311,7 +311,6 @@ import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isShif
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isTraitSelf;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isUsingGenericsOrIsArrayUsingGenerics;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isWildcardLeftHandSide;
-import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.lastArgMatchesVarg;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.missesGenericsTypes;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.prettyPrintType;
 import static 
org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.prettyPrintTypeName;
@@ -951,7 +950,9 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
                 // propagate closure parameter type information
                 if (leftExpression instanceof VariableExpression) {
                     if (rightExpression instanceof ClosureExpression) {
-                        leftExpression.putNodeMetaData(CLOSURE_ARGUMENTS, 
((ClosureExpression) rightExpression).getParameters());
+                        ClosureExpression closure = (ClosureExpression) 
rightExpression;
+                        if (!hasImplicitParameter(closure)) // GROOVY-11394: 
arrow means zero parameters
+                            leftExpression.putNodeMetaData(CLOSURE_ARGUMENTS, 
getParametersSafe(closure));
                     } else if (rightExpression instanceof VariableExpression
                             && ((VariableExpression) 
rightExpression).getAccessedVariable() instanceof Expression
                             && ((Expression) ((VariableExpression) 
rightExpression).getAccessedVariable()).getNodeMetaData(CLOSURE_ARGUMENTS) != 
null) {
@@ -4088,9 +4089,57 @@ out:                if (mn.size() != 1) {
     }
 
     protected void typeCheckClosureCall(final Expression arguments, final 
ClassNode[] argumentTypes, final Parameter[] parameters) {
-        if (allParametersAndArgumentsMatchWithDefaultParams(parameters, 
argumentTypes) < 0 && lastArgMatchesVarg(parameters, argumentTypes) < 0) {
-            addStaticTypeError("Cannot call closure that accepts " + 
formatArgumentList(extractTypesFromParameters(parameters)) + " with " + 
formatArgumentList(argumentTypes), arguments);
+        int nArguments = argumentTypes.length;
+        List<ClassNode[]> signatures = new LinkedList<>();
+        signatures.add(extractTypesFromParameters(parameters));
+
+        var n = 
Arrays.stream(parameters).filter(Parameter::hasInitialExpression).count();
+        for (int i = 1; i <= n; i += 1) { // drop parameters with value from 
right to left
+            ClassNode[] signature = new ClassNode[parameters.length - i];
+            int j = 1, index = 0;
+            for (Parameter parameter : parameters) {
+                if (j > n - i && parameter.hasInitialExpression()) {
+                    // skip parameter with default argument
+                } else {
+                    signature[index++] = parameter.getType();
+                }
+                if (parameter.hasInitialExpression()) j += 1;
+            }
+            signatures.add(signature);
+        }
+
+        for (var it = signatures.listIterator(); it.hasNext(); ) { var 
signature = it.next();
+            if (asBoolean(signature) && last(signature).isArray()) {
+                int vaIndex = signature.length - 1;
+                if (vaIndex == nArguments) { // empty array
+                    it.add(Arrays.copyOf(signature, nArguments));
+                } else if (vaIndex < nArguments) { // spread arg(s)?
+                    var subType = last(signature).getComponentType();
+                    signature = Arrays.copyOf(signature, nArguments);
+                    Arrays.fill(signature, vaIndex, nArguments, subType);
+                    it.add(signature);
+                }
+            }
         }
+
+        ClassNode[] firstMatch = null;
+
+trying: for (ClassNode[] signature : signatures) {
+            if (nArguments == signature.length) {
+                if (firstMatch == null)
+                    firstMatch = signature;
+                for (int i = 0; i < nArguments; i += 1) {
+                    if (!isAssignableTo(argumentTypes[i], signature[i])) {
+                        continue trying;
+                    }
+                }
+                return; // arguments match
+            }
+        }
+
+        String actual = formatArgumentList(argumentTypes);
+        String expect = formatArgumentList(firstMatch != null ? firstMatch : 
signatures.get(0));
+        addStaticTypeError("Cannot call closure that accepts " + expect + " 
with " + actual, arguments);
     }
 
     @Override
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy 
b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 80968d78c0..b6354e9642 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -18,8 +18,6 @@
  */
 package groovy.transform.stc
 
-import groovy.test.NotYetImplemented
-
 /**
  * Unit tests for static type checking : closures.
  */
@@ -39,7 +37,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    @NotYetImplemented
+    // GROOVY-11394
     void testCallClosure3() {
         shouldFailWithMessages '''
             def c = { -> }
@@ -150,6 +148,19 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    // GROOVY-11394
+    void testCallClosure16() {
+        assertScript '''
+            def c = { Object[] arr, opt = null -> Arrays.toString(arr) + opt }
+            def x = c()
+            assert x == '[]null'
+            def y = c(1)
+            assert y == '[1]null'
+            def z = c(null)
+            assert z == 'nullnull'
+        '''
+    }
+
     void testClosureReturnTypeInference1() {
         assertScript '''
             def c = { int a, int b -> return a + b }

Reply via email to