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 23:40:50 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-10482: STC: resolve type parameter of ` T m()` in call args
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new ff3ef86 GROOVY-10482: STC: resolve type parameter of `<T> T m()` in call args
ff3ef86 is described below
commit ff3ef86865870868f5d77ac9466aff7c5a024e8b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Mar 12 17:22:26 2022 -0600
GROOVY-10482: STC: resolve type parameter of `<T> T m()` in call args
3_0_X backport
---
.../transform/stc/StaticTypeCheckingVisitor.java | 98 +++++++++++-----------
.../groovy/transform/stc/ClosuresSTCTest.groovy | 32 +++++++
.../groovy/transform/stc/GenericsSTCTest.groovy | 31 +++++++
3 files changed, 114 insertions(+), 47 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 9194c46..4e3a82c 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -209,6 +209,8 @@ 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.classgen.AsmClassGenerator.MINIMUM_BYTECODE_VERSION;
+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;
@@ -2225,48 +2227,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-8961, GROOVY-9734, GROOVY-9915, GROOVY-10482, et al.
+ Map<GenericsTypeName, GenericsType> context = extractPlaceHolders(null, receiver, ctor.getDeclaringClass());
+ 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);
}
}
@@ -2642,7 +2643,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (!mn.isEmpty()) {
if (mn.size() == 1) {
// GROOVY-8961, GROOVY-9734, GROOVY-9915
- resolvePlaceholdersFromImplicitTypeHints(args, argumentList, mn.get(0));
+ resolvePlaceholdersFromImplicitTypeHints(args, argumentList, mn.get(0).getParameters());
typeCheckMethodsWithGenericsOrFail(currentReceiver.getType(), args, mn.get(0), call);
}
chosenReceiver = currentReceiver;
@@ -3543,7 +3544,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
}
// GROOVY-8961, GROOVY-9734
- resolvePlaceholdersFromImplicitTypeHints(args, argumentList, directMethodCallCandidate);
+ resolvePlaceholdersFromImplicitTypeHints(args, argumentList, directMethodCallCandidate.getParameters());
if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, directMethodCallCandidate, call)) {
returnType = adjustWithTraits(directMethodCallCandidate, chosenReceiver.getType(), args, returnType);
@@ -4059,7 +4060,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
@@ -5306,12 +5307,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* argument is {@code List<T>} without explicit type arguments. Knowning the
* method target of "m", {@code T} could be resolved.
*/
- private static void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final MethodNode inferredMethod) {
+ private static void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final Parameter[] parameterArray) {
for (int i = 0, n = actuals.length; i < n; i += 1) {
- // check for method call with known target
Expression a = argumentList.getExpression(i);
- if (!(a instanceof MethodCallExpression)) continue;
- if (((MethodCallExpression) a).isUsingGenerics()) continue;
+ // check for method call without type arguments, with a known target
+ if (!(a instanceof MethodCall) || (a instanceof MethodCallExpression
+ && ((MethodCallExpression) a).isUsingGenerics())) continue;
MethodNode aNode = a.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
if (aNode == null || aNode.getGenericsTypes() == null) continue;
@@ -5319,8 +5320,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
ClassNode at = actuals[i];
if (!GenericsUtils.hasUnresolvedGenerics(at)) continue;
- int np = inferredMethod.getParameters().length;
- Parameter p = inferredMethod.getParameters()[Math.min(i, np - 1)];
+ int np = parameterArray.length;
+ Parameter p = parameterArray[Math.min(i, np - 1)];
ClassNode pt = p.getOriginType();
if (i >= (np - 1) && pt.isArray() && !at.isArray()) pt = pt.getComponentType();
@@ -5331,6 +5332,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 510d07b..a910068 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -813,4 +813,36 @@ 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
+ import groovy.transform.NamedParams
+
+ class Foo {
+ Foo(@NamedParams([
+ @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 c16b875..0a5a761 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1455,6 +1455,37 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ void testCompatibleArgumentsForPlaceholders1() {
+ 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 testCompatibleArgumentsForPlaceholders2() {
+ ['', 'def', 'public', 'static', '@Deprecated'].each {
+ assertScript """
+ class Foo<X> {
+ Foo(X x) {
+ }
+ }
+ $it <Y> Y bar() {
+ }
+ $it <Z> void baz() {
+ new Foo<Z>(bar()) // Cannot call Foo#<init>(Z) with arguments [#Y]
+ }
+ this.<String>baz()
+ """
+ }
+ }
+
void testIncompatibleGenericsForTwoArguments() {
shouldFailWithMessages '''
public <T> void printEqual(T arg1, T arg2) {