Skip to content

Commit 8816503

Browse files
committed
GROOVY-10482
1 parent 5e77ef9 commit 8816503

File tree

5 files changed

+197
-118
lines changed

5 files changed

+197
-118
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ public void testCompileStatic12() {
333333
"1. ERROR in Main.groovy (at line 3)\n" +
334334
"\tdef list = new LinkedList<String>([1,2,3])\n" +
335335
"\t ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
336-
"Groovy:[Static type checking] - Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.List<java.lang.Integer>]\n" +
336+
"Groovy:[Static type checking] - Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.ArrayList<java.lang.Integer>]\n" +
337337
"----------\n");
338338
}
339339

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+64
Original file line numberDiff line numberDiff line change
@@ -5166,4 +5166,68 @@ public void testTypeChecked10419() {
51665166

51675167
runConformTest(sources, "C(x)");
51685168
}
5169+
5170+
@Test
5171+
public void testTypeChecked10482() {
5172+
//@formatter:off
5173+
String[] sources = {
5174+
"Main.groovy",
5175+
"class Foo<X> {\n" +
5176+
" Foo(X x) {\n" +
5177+
" }\n" +
5178+
"}\n" +
5179+
"def <Y> Y bar() {\n" +
5180+
"}\n" +
5181+
"@groovy.transform.TypeChecked\n" +
5182+
"def <Z> void baz() {\n" +
5183+
" new Foo<Z>(bar())\n" + // Cannot call Foo#<init>(Z) with arguments [#Y]
5184+
"}\n" +
5185+
"this.<String>baz()\n",
5186+
};
5187+
//@formatter:on
5188+
5189+
runConformTest(sources);
5190+
}
5191+
5192+
@Test
5193+
public void testTypeChecked10482a() {
5194+
//@formatter:off
5195+
String[] sources = {
5196+
"Main.groovy",
5197+
"class Foo<X> {\n" +
5198+
" Foo(X x) {\n" +
5199+
" }\n" +
5200+
"}\n" +
5201+
"static <Y> Y bar() {\n" +
5202+
"}\n" +
5203+
"@groovy.transform.TypeChecked\n" +
5204+
"static <Z> void baz() {\n" +
5205+
" new Foo<Z>(bar())\n" + // Cannot call Foo#<init>(Z) with arguments [#Y]
5206+
"}\n" +
5207+
"Main.<String>baz()\n",
5208+
};
5209+
//@formatter:on
5210+
5211+
runConformTest(sources);
5212+
}
5213+
5214+
@Test
5215+
public void testTypeChecked10482b() {
5216+
//@formatter:off
5217+
String[] sources = {
5218+
"Main.groovy",
5219+
"def <X> X foo(X x) {\n" +
5220+
"}\n" +
5221+
"def <Y> Y bar() {\n" +
5222+
"}\n" +
5223+
"@groovy.transform.TypeChecked\n" +
5224+
"def <Z> void baz() {\n" +
5225+
" this.<Z>foo(bar())\n" +
5226+
"}\n" +
5227+
"this.<String>baz()\n",
5228+
};
5229+
//@formatter:on
5230+
5231+
runConformTest(sources);
5232+
}
51695233
}

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+37-30
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@
208208
import static org.codehaus.groovy.classgen.AsmClassGenerator.MINIMUM_BYTECODE_VERSION;
209209
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
210210
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.first;
211+
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.init;
212+
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
211213
import static org.codehaus.groovy.syntax.Types.ASSIGN;
212214
import static org.codehaus.groovy.syntax.Types.ASSIGNMENT_OPERATOR;
213215
import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
@@ -2788,32 +2790,34 @@ public void visitConstructorCallExpression(ConstructorCallExpression call) {
27882790

27892791
checkForbiddenSpreadArgument(argumentList);
27902792
visitMethodCallArguments(receiver, argumentList, false, null);
2793+
final ClassNode[] argumentTypes = getArgumentTypes(argumentList);
27912794

2792-
ClassNode[] args = getArgumentTypes(argumentList);
2793-
2794-
MethodNode node;
2795-
if (looksLikeNamedArgConstructor(receiver, args)
2796-
&& findMethod(receiver, "<init>", DefaultGroovyMethods.init(args)).size() == 1
2797-
&& findMethod(receiver, "<init>", args).isEmpty()) {
2798-
// bean-style constructor
2799-
node = typeCheckMapConstructor(call, receiver, arguments);
2800-
if (node != null) {
2801-
storeTargetMethod(call, node);
2802-
extension.afterMethodCall(call);
2803-
return;
2795+
MethodNode ctor;
2796+
if (looksLikeNamedArgConstructor(receiver, argumentTypes)
2797+
&& findMethod(receiver, "<init>", argumentTypes).isEmpty()
2798+
&& findMethod(receiver, "<init>", init(argumentTypes)).size() == 1) {
2799+
ctor = typeCheckMapConstructor(call, receiver, arguments);
2800+
} else {
2801+
ctor = findMethodOrFail(call, receiver, "<init>", argumentTypes);
2802+
if (ctor != null) {
2803+
Parameter[] parameters = ctor.getParameters();
2804+
if (looksLikeNamedArgConstructor(receiver, argumentTypes)
2805+
&& parameters.length == argumentTypes.length - 1) {
2806+
ctor = typeCheckMapConstructor(call, receiver, arguments);
2807+
} else {
2808+
if (parameters.length > 0 && receiver.getGenericsTypes() != null) { // GROOVY-8961, GROOVY-9734, GROOVY-9915, GROOVY-10482, et al.
2809+
Map<GenericsTypeName, GenericsType> context = extractPlaceHolders(null, receiver, ctor.getDeclaringClass());
2810+
parameters = parameters.clone(); for (int i = 0; i < parameters.length; i += 1)
2811+
parameters[i] = new Parameter(applyGenericsContext(context, parameters[i].getType()), parameters[i].getName());
2812+
}
2813+
resolvePlaceholdersFromImplicitTypeHints(argumentTypes, argumentList, parameters);
2814+
typeCheckMethodsWithGenericsOrFail(receiver, argumentTypes, ctor, call);
2815+
visitMethodCallArguments(receiver, argumentList, true, ctor);
2816+
}
28042817
}
28052818
}
2806-
node = findMethodOrFail(call, receiver, "<init>", args);
2807-
if (node != null) {
2808-
if (looksLikeNamedArgConstructor(receiver, args) && node.getParameters().length + 1 == args.length) {
2809-
node = typeCheckMapConstructor(call, receiver, arguments);
2810-
} else {
2811-
typeCheckMethodsWithGenericsOrFail(receiver, args, node, call);
2812-
}
2813-
if (node != null) {
2814-
storeTargetMethod(call, node);
2815-
visitMethodCallArguments(receiver, argumentList, true, node);
2816-
}
2819+
if (ctor != null) {
2820+
storeTargetMethod(call, ctor);
28172821
}
28182822

28192823
// GROOVY-9327: check for AIC in STC method with non-STC enclosing class
@@ -4723,7 +4727,7 @@ public BinaryExpression findInstanceOfNotReturnExpression(IfStatement ifElse) {
47234727
if (bs.getStatements().size() == 0) {
47244728
return null;
47254729
}
4726-
Statement last = DefaultGroovyMethods.last(bs.getStatements());
4730+
Statement last = last(bs.getStatements());
47274731
if (!(last instanceof ReturnStatement)) {
47284732
return null;
47294733
}
@@ -6376,10 +6380,10 @@ private static void resolvePlaceholdersFromExplicitTypeHints(final MethodNode me
63766380
private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final Parameter[] parameterArray) {
63776381
/* GRECLIPSE edit
63786382
for (int i = 0, n = actuals.length; i < n; i += 1) {
6379-
// check for method call with known target
63806383
Expression a = argumentList.getExpression(i);
6381-
if (!(a instanceof MethodCallExpression)) continue;
6382-
if (((MethodCallExpression) a).isUsingGenerics()) continue;
6384+
// check for method call without type arguments, with a known target
6385+
if (!(a instanceof MethodCall) || (a instanceof MethodCallExpression
6386+
&& ((MethodCallExpression) a).isUsingGenerics())) continue;
63836387
MethodNode aNode = a.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
63846388
if (aNode == null || aNode.getGenericsTypes() == null) continue;
63856389
@@ -6416,9 +6420,9 @@ private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals,
64166420
actuals[i] = getType(a);
64176421
}
64186422

6419-
// check for method call with known target
6420-
if (!(a instanceof MethodCallExpression)) continue;
6421-
if (((MethodCallExpression) a).isUsingGenerics()) continue;
6423+
// check for method call without type arguments, with a known target
6424+
if (!(a instanceof MethodCall) || (a instanceof MethodCallExpression
6425+
&& ((MethodCallExpression) a).isUsingGenerics())) continue;
64226426
MethodNode aNode = a.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
64236427
if (aNode == null || aNode.getGenericsTypes() == null) continue;
64246428

@@ -6437,6 +6441,9 @@ private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals,
64376441
Map<GenericsTypeName, GenericsType> source = GenericsUtils.extractPlaceholders(at);
64386442
Map<GenericsTypeName, GenericsType> target = GenericsUtils.extractPlaceholders(pt);
64396443

6444+
if (at.isGenericsPlaceHolder()) // GROOVY-10482: call argument via "def <T> T m()"
6445+
target.put(new GenericsTypeName(at.getUnresolvedName()), pt.asGenericsType());
6446+
64406447
// connect E:T from source to E:Type from target
64416448
for (GenericsType placeholder : aNode.getGenericsTypes()) {
64426449
for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+49-45
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@
212212
import static org.codehaus.groovy.ast.tools.WideningCategories.isNumberCategory;
213213
import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
214214
import static org.codehaus.groovy.classgen.AsmClassGenerator.MINIMUM_BYTECODE_VERSION;
215+
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.init;
216+
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
215217
import static org.codehaus.groovy.syntax.Types.ASSIGN;
216218
import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
217219
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
@@ -2604,48 +2606,47 @@ protected void addClosureReturnType(final ClassNode returnType) {
26042606

26052607
@Override
26062608
public void visitConstructorCallExpression(final ConstructorCallExpression call) {
2607-
if (extension.beforeMethodCall(call)) {
2608-
extension.afterMethodCall(call);
2609-
return;
2610-
}
2611-
ClassNode receiver;
2612-
if (call.isThisCall()) {
2613-
receiver = makeThis();
2614-
} else if (call.isSuperCall()) {
2615-
receiver = makeSuper();
2616-
} else {
2617-
receiver = call.getType();
2618-
}
2619-
Expression arguments = call.getArguments();
2620-
ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(arguments);
2621-
2622-
checkForbiddenSpreadArgument(argumentList);
2623-
visitMethodCallArguments(receiver, argumentList, false, null);
2609+
if (!extension.beforeMethodCall(call)) {
2610+
ClassNode receiver;
2611+
if (call.isThisCall()) {
2612+
receiver = makeThis();
2613+
} else if (call.isSuperCall()) {
2614+
receiver = makeSuper();
2615+
} else {
2616+
receiver = call.getType();
2617+
}
2618+
Expression arguments = call.getArguments();
2619+
ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(arguments);
26242620

2625-
ClassNode[] args = getArgumentTypes(argumentList);
2621+
checkForbiddenSpreadArgument(argumentList);
2622+
visitMethodCallArguments(receiver, argumentList, false, null);
2623+
final ClassNode[] argumentTypes = getArgumentTypes(argumentList);
26262624

2627-
MethodNode node;
2628-
if (looksLikeNamedArgConstructor(receiver, args)
2629-
&& findMethod(receiver, "<init>", DefaultGroovyMethods.init(args)).size() == 1
2630-
&& findMethod(receiver, "<init>", args).isEmpty()) {
2631-
// bean-style constructor
2632-
node = typeCheckMapConstructor(call, receiver, arguments);
2633-
if (node != null) {
2634-
storeTargetMethod(call, node);
2635-
extension.afterMethodCall(call);
2636-
return;
2637-
}
2638-
}
2639-
node = findMethodOrFail(call, receiver, "<init>", args);
2640-
if (node != null) {
2641-
if (looksLikeNamedArgConstructor(receiver, args) && node.getParameters().length + 1 == args.length) {
2642-
node = typeCheckMapConstructor(call, receiver, arguments);
2625+
MethodNode ctor;
2626+
if (looksLikeNamedArgConstructor(receiver, argumentTypes)
2627+
&& findMethod(receiver, "<init>", argumentTypes).isEmpty()
2628+
&& findMethod(receiver, "<init>", init(argumentTypes)).size() == 1) {
2629+
ctor = typeCheckMapConstructor(call, receiver, arguments);
26432630
} else {
2644-
typeCheckMethodsWithGenericsOrFail(receiver, args, node, call);
2631+
ctor = findMethodOrFail(call, receiver, "<init>", argumentTypes);
2632+
if (ctor != null) {
2633+
Parameter[] parameters = ctor.getParameters();
2634+
if (looksLikeNamedArgConstructor(receiver, argumentTypes)
2635+
&& parameters.length == argumentTypes.length - 1) {
2636+
ctor = typeCheckMapConstructor(call, receiver, arguments);
2637+
} else {
2638+
if (receiver.getGenericsTypes() != null) { // GROOVY-8961, GROOVY-9734, GROOVY-9915, GROOVY-10482, et al.
2639+
Map<GenericsTypeName, GenericsType> context = extractPlaceHolders(null, receiver, ctor.getDeclaringClass());
2640+
parameters = Arrays.stream(parameters).map(p -> new Parameter(applyGenericsContext(context, p.getType()), p.getName())).toArray(Parameter[]::new);
2641+
}
2642+
resolvePlaceholdersFromImplicitTypeHints(argumentTypes, argumentList, parameters);
2643+
typeCheckMethodsWithGenericsOrFail(receiver, argumentTypes, ctor, call);
2644+
visitMethodCallArguments(receiver, argumentList, true, ctor);
2645+
}
2646+
}
26452647
}
2646-
if (node != null) {
2647-
storeTargetMethod(call, node);
2648-
visitMethodCallArguments(receiver, argumentList, true, node);
2648+
if (ctor != null) {
2649+
storeTargetMethod(call, ctor);
26492650
}
26502651
}
26512652

@@ -4617,7 +4618,7 @@ protected BinaryExpression findNotInstanceOfReturnExpression(final IfStatement i
46174618

46184619
private static boolean notReturningBlock(final Statement statement) {
46194620
return statement.isEmpty() || !(statement instanceof BlockStatement)
4620-
|| !(DefaultGroovyMethods.last(((BlockStatement) statement).getStatements()) instanceof ReturnStatement);
4621+
|| !(last(((BlockStatement) statement).getStatements()) instanceof ReturnStatement);
46214622
}
46224623

46234624
@Override
@@ -6220,10 +6221,10 @@ private static void resolvePlaceholdersFromExplicitTypeHints(final MethodNode me
62206221
private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final Parameter[] parameterArray) {
62216222
/* GRECLIPSE edit
62226223
for (int i = 0, n = actuals.length; i < n; i += 1) {
6223-
// check for method call with known target
62246224
Expression a = argumentList.getExpression(i);
6225-
if (!(a instanceof MethodCallExpression)) continue;
6226-
if (((MethodCallExpression) a).isUsingGenerics()) continue;
6225+
// check for method call without type arguments, with a known target
6226+
if (!(a instanceof MethodCall) || (a instanceof MethodCallExpression
6227+
&& ((MethodCallExpression) a).isUsingGenerics())) continue;
62276228
MethodNode aNode = a.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
62286229
if (aNode == null || aNode.getGenericsTypes() == null) continue;
62296230
@@ -6260,9 +6261,9 @@ private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals,
62606261
actuals[i] = getType(a);
62616262
}
62626263

6263-
// check for method call with known target
6264-
if (!(a instanceof MethodCallExpression)) continue;
6265-
if (((MethodCallExpression) a).isUsingGenerics()) continue;
6264+
// check for method call without type arguments, with a known target
6265+
if (!(a instanceof MethodCall) || (a instanceof MethodCallExpression
6266+
&& ((MethodCallExpression) a).isUsingGenerics())) continue;
62666267
MethodNode aNode = a.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
62676268
if (aNode == null || aNode.getGenericsTypes() == null) continue;
62686269

@@ -6281,6 +6282,9 @@ private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals,
62816282
Map<GenericsTypeName, GenericsType> source = GenericsUtils.extractPlaceholders(at);
62826283
Map<GenericsTypeName, GenericsType> target = GenericsUtils.extractPlaceholders(pt);
62836284

6285+
if (at.isGenericsPlaceHolder()) // GROOVY-10482: call argument via "def <T> T m()"
6286+
target.put(new GenericsTypeName(at.getUnresolvedName()), pt.asGenericsType());
6287+
62846288
// connect E:T from source to E:Type from target
62856289
for (GenericsType placeholder : aNode.getGenericsTypes()) {
62866290
for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {

0 commit comments

Comments
 (0)