Skip to content

Commit 2d684e9

Browse files
committed
GROOVY-9915
1 parent b9cfd0a commit 2d684e9

File tree

4 files changed

+84
-33
lines changed

4 files changed

+84
-33
lines changed

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

+21
Original file line numberDiff line numberDiff line change
@@ -796,4 +796,25 @@ public void testTypeChecked9907() {
796796

797797
runConformTest(sources, "1");
798798
}
799+
800+
@Test
801+
public void testTypeChecked9915() {
802+
//@formatter:off
803+
String[] sources = {
804+
"Main.groovy",
805+
"@groovy.transform.TypeChecked\n" +
806+
"class C {\n" +
807+
" void m() {\n" +
808+
" init(Collections.emptyList())\n" + // Cannot call C#init(List<String>) with arguments [List<T>]
809+
" }\n" +
810+
" private static void init(List<String> strings) {\n" +
811+
" print strings\n" +
812+
" }\n" +
813+
"}\n" +
814+
"new C().m()\n",
815+
};
816+
//@formatter:on
817+
818+
runConformTest(sources, "[]");
819+
}
799820
}

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

+53-31
Original file line numberDiff line numberDiff line change
@@ -2867,8 +2867,12 @@ public void visitStaticMethodCallExpression(final StaticMethodCallExpression cal
28672867
for (Receiver<String> currentReceiver : receivers) {
28682868
mn = findMethod(currentReceiver.getType(), name, args);
28692869
if (!mn.isEmpty()) {
2870-
if (mn.size() == 1)
2870+
if (mn.size() == 1) {
2871+
// GRECLIPSE add -- GROOVY-8961, GROOVY-9734, GROOVY-9915
2872+
resolvePlaceholdersFromImplicitTypeHints(args, argumentList, mn.get(0));
2873+
// GRECLIPSE end
28712874
typeCheckMethodsWithGenericsOrFail(currentReceiver.getType(), args, mn.get(0), call);
2875+
}
28722876
chosenReceiver = currentReceiver;
28732877
break;
28742878
}
@@ -3768,36 +3772,7 @@ public void visitMethodCallExpression(MethodCallExpression call) {
37683772
}
37693773
}
37703774
// GRECLIPSE add -- GROOVY-8961, GROOVY-9734
3771-
for (int i = 0, n = args.length; i < n; i += 1) {
3772-
Expression a = argumentList.getExpression(i); ClassNode at = args[i];
3773-
if (a instanceof MethodCallExpression && !((MethodCallExpression) a).isUsingGenerics() && isUsingUncheckedGenerics(at)) {
3774-
// try to resolve unresolved placeholders in argument type using parameter type
3775-
3776-
MethodNode aNode = a.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
3777-
if (aNode == null || aNode.getGenericsTypes() == null) continue;
3778-
3779-
int np = directMethodCallCandidate.getParameters().length;
3780-
Parameter p = directMethodCallCandidate.getParameters()[Math.min(i, np)];
3781-
ClassNode pt = getType(p); if (i >= (np - 1) && pt.isArray() && !at.isArray()) pt = pt.getComponentType();
3782-
3783-
Map<GenericsTypeName, GenericsType> source = extractPlaceholders(at);
3784-
Map<GenericsTypeName, GenericsType> target = extractPlaceholders(pt);
3785-
Map<GenericsTypeName, GenericsType> linked = new HashMap<>();
3786-
3787-
// connect E:T from source to E:Type from target
3788-
for (GenericsType placeholder : aNode.getGenericsTypes()) {
3789-
for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {
3790-
if (e.getValue() == placeholder) {
3791-
Optional.ofNullable(target.get(e.getKey()))
3792-
.filter(gt -> isAssignableTo(gt.getType(), placeholder.getType()))
3793-
.ifPresent(gt -> linked.put(new GenericsTypeName(placeholder.getName()), gt));
3794-
break;
3795-
}
3796-
}
3797-
}
3798-
args[i] = applyGenericsContext(linked, at);
3799-
}
3800-
}
3775+
resolvePlaceholdersFromImplicitTypeHints(args, argumentList, directMethodCallCandidate);
38013776
// GRECLIPSE end
38023777
if (typeCheckMethodsWithGenericsOrFail(chosenReceiver.getType(), args, directMethodCallCandidate, call)) {
38033778
returnType = adjustWithTraits(directMethodCallCandidate, chosenReceiver.getType(), args, returnType);
@@ -5575,6 +5550,53 @@ private static void resolvePlaceholdersFromExplicitTypeHints(final MethodNode me
55755550
}
55765551
}
55775552

5553+
/**
5554+
* Given method call like "m(Collections.emptyList())", the type of the call
5555+
* argument is {@code List<T>} without explicit type arguments. Knowning the
5556+
* method target of "m", {@code T} could be resolved.
5557+
*/
5558+
private static void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final MethodNode inferredMethod) {
5559+
for (int i = 0, n = actuals.length; i < n; i += 1) {
5560+
// check for method call with known target
5561+
Expression a = argumentList.getExpression(i);
5562+
if (!(a instanceof MethodCallExpression)) continue;
5563+
if (((MethodCallExpression) a).isUsingGenerics()) continue;
5564+
MethodNode aNode = a.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
5565+
if (aNode == null || aNode.getGenericsTypes() == null) continue;
5566+
5567+
// and unknown generics
5568+
ClassNode at = actuals[i];
5569+
if (!GenericsUtils.hasUnresolvedGenerics(at)) continue;
5570+
5571+
int np = inferredMethod.getParameters().length;
5572+
Parameter p = inferredMethod.getParameters()[Math.min(i, np - 1)];
5573+
5574+
ClassNode pt = p.getOriginType();
5575+
if (i >= (np - 1) && pt.isArray() && !at.isArray()) pt = pt.getComponentType();
5576+
5577+
// try to resolve placeholder(s) in argument type using parameter type
5578+
5579+
Map<GenericsTypeName, GenericsType> linked = new HashMap<>();
5580+
Map<GenericsTypeName, GenericsType> source = GenericsUtils.extractPlaceholders(at);
5581+
Map<GenericsTypeName, GenericsType> target = GenericsUtils.extractPlaceholders(pt);
5582+
5583+
// connect E:T from source to E:Type from target
5584+
for (GenericsType placeholder : aNode.getGenericsTypes()) {
5585+
for (Map.Entry<GenericsTypeName, GenericsType> e : source.entrySet()) {
5586+
if (e.getValue() == placeholder) {
5587+
Optional.ofNullable(target.get(e.getKey()))
5588+
// skip "f(g())" for "f(T<String>)" and "<U extends Number> U g()"
5589+
.filter(gt -> isAssignableTo(gt.getType(), placeholder.getType()))
5590+
.ifPresent(gt -> linked.put(new GenericsTypeName(placeholder.getName()), gt));
5591+
break;
5592+
}
5593+
}
5594+
}
5595+
5596+
actuals[i] = applyGenericsContext(linked, at);
5597+
}
5598+
}
5599+
55785600
private static void extractGenericsConnectionsForSuperClassAndInterfaces(final Map<GenericsTypeName, GenericsType> resolvedPlaceholders, final Map<GenericsTypeName, GenericsType> connections) {
55795601
for (GenericsType value : new HashSet<GenericsType>(connections.values())) {
55805602
if (!value.isPlaceholder() && !value.isWildcard()) {

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -2662,8 +2662,12 @@ public void visitStaticMethodCallExpression(final StaticMethodCallExpression cal
26622662
for (Receiver<String> currentReceiver : receivers) {
26632663
mn = findMethod(currentReceiver.getType(), name, args);
26642664
if (!mn.isEmpty()) {
2665-
if (mn.size() == 1)
2665+
if (mn.size() == 1) {
2666+
// GRECLIPSE add -- GROOVY-8961, GROOVY-9734, GROOVY-9915
2667+
resolvePlaceholdersFromImplicitTypeHints(args, argumentList, mn.get(0));
2668+
// GRECLIPSE end
26662669
typeCheckMethodsWithGenericsOrFail(currentReceiver.getType(), args, mn.get(0), call);
2670+
}
26672671
chosenReceiver = currentReceiver;
26682672
break;
26692673
}

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -2647,8 +2647,12 @@ public void visitStaticMethodCallExpression(final StaticMethodCallExpression cal
26472647
for (Receiver<String> currentReceiver : receivers) {
26482648
mn = findMethod(currentReceiver.getType(), name, args);
26492649
if (!mn.isEmpty()) {
2650-
if (mn.size() == 1)
2650+
if (mn.size() == 1) {
2651+
// GRECLIPSE add -- GROOVY-8961, GROOVY-9734, GROOVY-9915
2652+
resolvePlaceholdersFromImplicitTypeHints(args, argumentList, mn.get(0));
2653+
// GRECLIPSE end
26512654
typeCheckMethodsWithGenericsOrFail(currentReceiver.getType(), args, mn.get(0), call);
2655+
}
26522656
chosenReceiver = currentReceiver;
26532657
break;
26542658
}

0 commit comments

Comments
 (0)