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 22:07:43 UTC
[groovy] 01/02: 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 GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 03a4828e2d5b44e4d9a7d1f997f234751d2408e6
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 e4aa9b0..a17dc7e 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;
@@ -2254,48 +2256,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);
}
}
@@ -3995,7 +3996,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
@@ -5406,6 +5407,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) { }