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/03/12 20:11:32 UTC

[groovy] branch master updated: GROOVY-10482: STC: resolve return type of `def T m()` in call args

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 7b7ebbb  GROOVY-10482: STC: resolve return type of `def <T> T m()` in call args
7b7ebbb is described below

commit 7b7ebbbf05c7d840da36df4545bfbf1979ae23db
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Mar 12 14:11:19 2022 -0600

    GROOVY-10482: STC: resolve return type of `def <T> T m()` in call args
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 82 ++++++++++++----------
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 29 ++++++++
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 31 +++++++-
 3 files changed, 102 insertions(+), 40 deletions(-)

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 805053d..b8a5d58 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -233,6 +233,8 @@ import static org.codehaus.groovy.ast.tools.WideningCategories.isIntCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory;
 import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.init;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.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;
@@ -2258,48 +2260,47 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     @Override
     public void visitConstructorCallExpression(final ConstructorCallExpression call) {
-        if (extension.beforeMethodCall(call)) {
-            extension.afterMethodCall(call);
-            return;
-        }
-        ClassNode receiver;
-        if (call.isThisCall()) {
-            receiver = makeThis();
-        } else if (call.isSuperCall()) {
-            receiver = makeSuper();
-        } else {
-            receiver = call.getType();
-        }
-        Expression arguments = call.getArguments();
-        ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(arguments);
-
-        checkForbiddenSpreadArgument(argumentList);
-        visitMethodCallArguments(receiver, argumentList, false, null);
+        if (!extension.beforeMethodCall(call)) {
+            ClassNode receiver;
+            if (call.isThisCall()) {
+                receiver = makeThis();
+            } else if (call.isSuperCall()) {
+                receiver = makeSuper();
+            } else {
+                receiver = call.getType();
+            }
+            Expression arguments = call.getArguments();
+            ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(arguments);
 
-        ClassNode[] args = getArgumentTypes(argumentList);
+            checkForbiddenSpreadArgument(argumentList);
+            visitMethodCallArguments(receiver, argumentList, false, null);
+            final ClassNode[] argumentTypes = getArgumentTypes(argumentList);
 
-        MethodNode node;
-        if (looksLikeNamedArgConstructor(receiver, args)
-                && findMethod(receiver, "<init>", DefaultGroovyMethods.init(args)).size() == 1
-                && findMethod(receiver, "<init>", args).isEmpty()) {
-            // bean-style constructor
-            node = typeCheckMapConstructor(call, receiver, arguments);
-            if (node != null) {
-                storeTargetMethod(call, node);
-                extension.afterMethodCall(call);
-                return;
-            }
-        }
-        node = findMethodOrFail(call, receiver, "<init>", args);
-        if (node != null) {
-            if (looksLikeNamedArgConstructor(receiver, args) && node.getParameters().length + 1 == args.length) {
-                node = typeCheckMapConstructor(call, receiver, arguments);
+            MethodNode ctor;
+            if (looksLikeNamedArgConstructor(receiver, argumentTypes)
+                    && findMethod(receiver, "<init>", argumentTypes).isEmpty()
+                    && findMethod(receiver, "<init>", init(argumentTypes)).size() == 1) {
+                ctor = typeCheckMapConstructor(call, receiver, arguments);
             } else {
-                typeCheckMethodsWithGenericsOrFail(receiver, args, node, call);
+                ctor = findMethodOrFail(call, receiver, "<init>", argumentTypes);
+                if (ctor != null) {
+                    Parameter[] parameters = ctor.getParameters();
+                    if (looksLikeNamedArgConstructor(receiver, argumentTypes)
+                            && parameters.length == argumentTypes.length - 1) {
+                        ctor = typeCheckMapConstructor(call, receiver, arguments);
+                    } else {
+                        if (receiver.getGenericsTypes() != null) { // GROOVY-8909, GROOVY-9734, GROOVY-9844, GROOVY-9915, GROOVY-10482, et al.
+                            Map<GenericsTypeName, GenericsType> context = extractPlaceHoldersVisibleToDeclaration(receiver, ctor, argumentList);
+                            parameters = Arrays.stream(parameters).map(p -> new Parameter(applyGenericsContext(context, p.getType()), p.getName())).toArray(Parameter[]::new);
+                        }
+                        resolvePlaceholdersFromImplicitTypeHints(argumentTypes, argumentList, parameters);
+                        typeCheckMethodsWithGenericsOrFail(receiver, argumentTypes, ctor, call);
+                        visitMethodCallArguments(receiver, argumentList, true, ctor);
+                    }
+                }
             }
-            if (node != null) {
-                storeTargetMethod(call, node);
-                visitMethodCallArguments(receiver, argumentList, true, node);
+            if (ctor != null) {
+                storeTargetMethod(call, ctor);
             }
         }
 
@@ -3998,7 +3999,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     private static boolean notReturningBlock(final Statement statement) {
         return statement.isEmpty() || !(statement instanceof BlockStatement)
-            || !(DefaultGroovyMethods.last(((BlockStatement) statement).getStatements()) instanceof ReturnStatement);
+            || !(last(((BlockStatement) statement).getStatements()) instanceof ReturnStatement);
     }
 
     @Override
@@ -5409,6 +5410,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             Map<GenericsTypeName, GenericsType> source = GenericsUtils.extractPlaceholders(at);
             Map<GenericsTypeName, GenericsType> target = GenericsUtils.extractPlaceholders(pt);
 
+            if (at.isGenericsPlaceHolder()) // GROOVY-10482: call argument via "def <T> T m()"
+                target.put(new GenericsTypeName(at.getUnresolvedName()), pt.asGenericsType());
+
             // connect E:T from source to E:Type from target
             for (GenericsType placeholder : aNode.getGenericsTypes()) {
                 for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index e3c5366..02ea1b0 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -933,4 +933,33 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
             """
         }
     }
+
+    void testTypeCheckingOfClosureInMapConstructorCalls() {
+        shouldFailWithMessages '''
+            class Foo {
+                Number bar
+                Object baz
+            }
+
+            new Foo(bar: 123, baz: { ->
+                int i = Integer
+            })
+        ''',
+        'Cannot assign value of type java.lang.Class<java.lang.Integer> to variable of type int'
+
+        shouldFailWithMessages '''
+            import groovy.transform.NamedParam
+
+            class Foo {
+                Foo(@NamedParam(value='bar',type=Number) Map<String,?> named, Object positional) {
+                }
+            }
+
+            new Foo(bar: Number, { ->
+                int i = Integer
+            })
+        ''',
+        'Cannot assign value of type java.lang.Class<java.lang.Integer> to variable of type int',
+        "named param 'bar' has type 'java.lang.Class<java.lang.Number>' but expected 'java.lang.Number'"
+    }
 }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 089871f..5cc5b7c 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1203,7 +1203,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         shouldFailWithMessages '''
             List<String> list = new LinkedList<String>([1,2,3])
         ''',
-        'Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.List<java.lang.Integer>]'
+        'Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.ArrayList<java.lang.Integer>]'
     }
 
     void testGenericAssignmentWithSubClass() {
@@ -2528,6 +2528,35 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
+    void testCompatibleArgumentsForPlaceholders5() {
+        assertScript '''
+            def <X> X foo(X x) {
+            }
+            def <Y> Y bar() {
+            }
+            def <Z> void baz() {
+                this.<Z>foo(bar())
+            }
+            this.<String>baz()
+        '''
+    }
+
+    // GROOVY-10482
+    void testCompatibleArgumentsForPlaceholders6() {
+        assertScript '''
+            class Foo<X> {
+                Foo(X x) {
+                }
+            }
+            def <Y> Y bar() {
+            }
+            def <Z> void baz() {
+                new Foo<Z>(bar()) // Cannot call Foo#<init>(Z) with arguments [#Y]
+            }
+            this.<String>baz()
+        '''
+    }
+
     void testIncompatibleArgumentsForPlaceholders1() {
         shouldFailWithMessages '''
             def <T extends Number> T test(T one, T two) { }