Skip to content

Commit 9418c30

Browse files
committed
GROOVY-11168
1 parent c299939 commit 9418c30

File tree

9 files changed

+82
-40
lines changed

9 files changed

+82
-40
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/Groovy21InferencingTests.java

+25-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2020 the original author or authors.
2+
* Copyright 2009-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
2929
public final class Groovy21InferencingTests extends InferencingTestSuite {
3030

3131
@Test
32-
public void testDelegatesToValue() {
32+
public void testDelegatesToValue1() {
3333
//@formatter:off
3434
String contents =
3535
"class Other { }\n" +
@@ -136,7 +136,7 @@ public void testDelegatesToTarget2() {
136136
}
137137

138138
@Test // uses constant instead of literal for target
139-
public void testDelegatesToTarget2a() {
139+
public void testDelegatesToTarget3() {
140140
createUnit("C",
141141
"class C {\n" +
142142
" private static final String SELF = 'self'\n" +
@@ -164,7 +164,7 @@ public void testDelegatesToTarget2a() {
164164
}
165165

166166
@Test
167-
public void testDelegatesToTarget3() {
167+
public void testDelegatesToTarget4() {
168168
createUnit("C", "class C { static def cat(\n" +
169169
"@DelegatesTo.Target('self') Object self, @DelegatesTo(target='self', strategy=Closure.DELEGATE_ONLY) Closure code) {}\n}");
170170
//@formatter:off
@@ -187,7 +187,7 @@ public void testDelegatesToTarget3() {
187187
}
188188

189189
@Test
190-
public void testDelegatesToTarget4() {
190+
public void testDelegatesToTarget5() {
191191
createUnit("C", "class C { static def cat(\n" +
192192
"@DelegatesTo.Target('self') Object self, @DelegatesTo(target='self', strategy=Closure.OWNER_FIRST) Closure code) {}\n}");
193193
//@formatter:off
@@ -211,7 +211,7 @@ public void testDelegatesToTarget4() {
211211
}
212212

213213
@Test
214-
public void testDelegatesToTarget5() {
214+
public void testDelegatesToTarget6() {
215215
createUnit("C", "class C { static def cat(\n" +
216216
"@DelegatesTo.Target('self') Object self, @DelegatesTo(target='self', strategy=Closure.OWNER_ONLY) Closure code) {}\n}");
217217
//@formatter:off
@@ -233,7 +233,7 @@ public void testDelegatesToTarget5() {
233233
}
234234

235235
@Test // seemingly invalid combination
236-
public void testDelegatesToTarget6() {
236+
public void testDelegatesToTarget7() {
237237
createUnit("C", "class C { static def cat(\n" +
238238
"@DelegatesTo.Target('self') Object self, @DelegatesTo(target='self', strategy=Closure.TO_SELF) Closure code) {}\n}");
239239
//@formatter:off
@@ -257,7 +257,7 @@ public void testDelegatesToTarget6() {
257257
}
258258

259259
@Test // https://github.com/groovy/groovy-eclipse/issues/1147
260-
public void testDelegatesToTarget7() {
260+
public void testDelegatesToTarget8() {
261261
//@formatter:off
262262
String contents =
263263
"abstract class A {\n" +
@@ -329,7 +329,7 @@ public void testDelegatesToTypeName3() {
329329
}
330330

331331
@Test // https://github.com/groovy/groovy-eclipse/issues/966
332-
public void testDelegatesToTypeName3a() {
332+
public void testDelegatesToTypeName4() {
333333
//@formatter:off
334334
createUnit("p", "A",
335335
"package p\n" +
@@ -352,7 +352,7 @@ public void testDelegatesToTypeName3a() {
352352
}
353353

354354
@Test // https://github.com/groovy/groovy-eclipse/issues/966
355-
public void testDelegatesToTypeName3b() {
355+
public void testDelegatesToTypeName5() {
356356
//@formatter:off
357357
createUnit("p", "A",
358358
"package p\n" +
@@ -378,8 +378,18 @@ public void testDelegatesToTypeName3b() {
378378
assertType(contents, "number", "java.lang.Number");
379379
}
380380

381+
@Test // https://issues.apache.org/jira/browse/GROOVY-11168
382+
public void testDelegatesToTypeName6() {
383+
//@formatter:off
384+
String contents =
385+
"def <T> T m(int i, @DelegatesTo(type='T') Closure block) { }\n" +
386+
"this.<String>m(2) { delegate }";
387+
//@formatter:on
388+
assertType(contents, "delegate", "java.lang.String");
389+
}
390+
381391
@Test
382-
public void testDelegatesToResolveStrategy2() {
392+
public void testDelegatesToResolveStrategy1() {
383393
//@formatter:off
384394
String contents =
385395
"class A {}\n" +
@@ -399,7 +409,7 @@ public void testDelegatesToResolveStrategy2() {
399409
}
400410

401411
@Test // https://github.com/groovy/groovy-eclipse/issues/657
402-
public void testDelegatesToResolveStrategy3() {
412+
public void testDelegatesToResolveStrategy2() {
403413
//@formatter:off
404414
String contents =
405415
"class A {}\n" +
@@ -419,7 +429,7 @@ public void testDelegatesToResolveStrategy3() {
419429
}
420430

421431
@Test
422-
public void testDelegatesToResolveStrategy4() {
432+
public void testDelegatesToResolveStrategy3() {
423433
//@formatter:off
424434
String contents =
425435
"class A {}\n" +
@@ -519,7 +529,7 @@ public void testEnumOverrides4() {
519529
}
520530

521531
@Test // https://github.com/groovy/groovy-eclipse/issues/1100
522-
public void testEnumOverrides4a() {
532+
public void testEnumOverrides5() {
523533
//@formatter:off
524534
String contents =
525535
"@groovy.transform.CompileStatic\n" +
@@ -539,7 +549,7 @@ public void testEnumOverrides4a() {
539549
}
540550

541551
@Test // https://github.com/groovy/groovy-eclipse/issues/1100
542-
public void testEnumOverrides4b() {
552+
public void testEnumOverrides6() {
543553
//@formatter:off
544554
String contents =
545555
"@groovy.transform.CompileStatic\n" +

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

+19
Original file line numberDiff line numberDiff line change
@@ -7122,4 +7122,23 @@ public void testTypeChecked11083() {
71227122
"Groovy:[Static type checking] - Cannot assign value of type java.util.Date to variable of type java.lang.Number\n" +
71237123
"----------\n");
71247124
}
7125+
7126+
@Test
7127+
public void testTypeChecked11168() {
7128+
//@formatter:off
7129+
String[] sources = {
7130+
"Main.groovy",
7131+
"def <T> T m(@DelegatesTo(type='T',strategy=Closure.DELEGATE_FIRST) Closure c) {\n" +
7132+
" 'WORKS'.with(c)\n" +
7133+
"}\n" +
7134+
"@groovy.transform.TypeChecked\n" +
7135+
"void test() {\n" +
7136+
" this.<String>m { print toLowerCase() }\n" +
7137+
"}\n" +
7138+
"test()\n",
7139+
};
7140+
//@formatter:on
7141+
7142+
runConformTest(sources, "works");
7143+
}
71257144
}

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -3461,7 +3461,7 @@ private void processClosureParams(final ClassNode receiver, final Expression arg
34613461
* of failure.
34623462
*/
34633463
private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
3464-
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
3464+
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, null).getPlainNodeReference();
34653465
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));
34663466

34673467
MethodNode methodNode = selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod;
@@ -3483,11 +3483,12 @@ private void resolveGenericsFromTypeHint(final ClassNode receiver, final Express
34833483
methodNode.setGenericsTypes(selectedMethod.getGenericsTypes());
34843484
}
34853485

3486-
GenericsType[] typeArguments = null; // GROOVY-7789
3487-
Expression emc = typeCheckingContext.getEnclosingMethodCall();
3488-
if (emc instanceof MethodCallExpression) {
3489-
MethodCallExpression call = (MethodCallExpression) emc;
3490-
if (arguments == call.getArguments()) typeArguments = call.getGenericsTypes();
3486+
GenericsType[] typeArguments = null;
3487+
Expression emc = typeCheckingContext.getEnclosingMethodCall(); // GROOVY-7789, GROOVY-11168
3488+
if (emc instanceof MethodCallExpression) { MethodCallExpression call = (MethodCallExpression) emc;
3489+
if (arguments == call.getArguments() || InvocationWriter.makeArgumentList(arguments).getExpressions().stream().anyMatch(arg ->
3490+
arg instanceof ClosureExpression && DefaultGroovyMethods.contains(InvocationWriter.makeArgumentList(call.getArguments()), arg)))
3491+
typeArguments = call.getGenericsTypes();
34913492
}
34923493

34933494
returnType = inferReturnTypeGenerics(receiver, methodNode, arguments, typeArguments);

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

+7-6
Original file line numberDiff line numberDiff line change
@@ -3159,7 +3159,7 @@ private void processClosureParams(final ClassNode receiver, final Expression arg
31593159
* of failure.
31603160
*/
31613161
private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
3162-
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
3162+
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, null).getPlainNodeReference();
31633163
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));
31643164

31653165
MethodNode methodNode = selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod;
@@ -3181,11 +3181,12 @@ private void resolveGenericsFromTypeHint(final ClassNode receiver, final Express
31813181
methodNode.setGenericsTypes(selectedMethod.getGenericsTypes());
31823182
}
31833183

3184-
GenericsType[] typeArguments = null; // GROOVY-7789
3185-
Expression emc = typeCheckingContext.getEnclosingMethodCall();
3186-
if (emc instanceof MethodCallExpression) {
3187-
MethodCallExpression call = (MethodCallExpression) emc;
3188-
if (arguments == call.getArguments()) typeArguments = call.getGenericsTypes();
3184+
GenericsType[] typeArguments = null;
3185+
Expression emc = typeCheckingContext.getEnclosingMethodCall(); // GROOVY-7789, GROOVY-11168
3186+
if (emc instanceof MethodCallExpression) { MethodCallExpression call = (MethodCallExpression) emc;
3187+
if (arguments == call.getArguments() || InvocationWriter.makeArgumentList(arguments).getExpressions().stream().anyMatch(arg ->
3188+
arg instanceof ClosureExpression && DefaultGroovyMethods.contains(InvocationWriter.makeArgumentList(call.getArguments()), arg)))
3189+
typeArguments = call.getGenericsTypes();
31893190
}
31903191

31913192
returnType = inferReturnTypeGenerics(receiver, methodNode, arguments, typeArguments);

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

+8-8
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,6 @@
240240
import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
241241
import static org.codehaus.groovy.runtime.ArrayGroovyMethods.asBoolean;
242242
import static org.codehaus.groovy.runtime.ArrayGroovyMethods.init;
243-
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
244243
import static org.codehaus.groovy.syntax.Types.ASSIGN;
245244
import static org.codehaus.groovy.syntax.Types.COMPARE_EQUAL;
246245
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
@@ -3305,7 +3304,7 @@ private ClassLoader getTransformLoader() {
33053304
* of failure.
33063305
*/
33073306
private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
3308-
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
3307+
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, null).getPlainNodeReference();
33093308
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));
33103309

33113310
MethodNode methodNode = selectedMethod instanceof ExtensionMethodNode ? ((ExtensionMethodNode) selectedMethod).getExtensionMethodNode() : selectedMethod;
@@ -3327,11 +3326,12 @@ private void resolveGenericsFromTypeHint(final ClassNode receiver, final Express
33273326
methodNode.setGenericsTypes(selectedMethod.getGenericsTypes());
33283327
}
33293328

3330-
GenericsType[] typeArguments = null; // GROOVY-7789
3331-
Expression emc = typeCheckingContext.getEnclosingMethodCall();
3332-
if (emc instanceof MethodCallExpression) {
3333-
MethodCallExpression call = (MethodCallExpression) emc;
3334-
if (arguments == call.getArguments()) typeArguments = call.getGenericsTypes();
3329+
GenericsType[] typeArguments = null;
3330+
Expression emc = typeCheckingContext.getEnclosingMethodCall(); // GROOVY-7789, GROOVY-11168
3331+
if (emc instanceof MethodCallExpression) { MethodCallExpression call = (MethodCallExpression) emc;
3332+
if (arguments == call.getArguments() || InvocationWriter.makeArgumentList(arguments).getExpressions().stream().anyMatch(arg ->
3333+
arg instanceof ClosureExpression && DefaultGroovyMethods.contains(InvocationWriter.makeArgumentList(call.getArguments()), arg)))
3334+
typeArguments = call.getGenericsTypes();
33353335
}
33363336

33373337
returnType = inferReturnTypeGenerics(receiver, methodNode, arguments, typeArguments);
@@ -4082,7 +4082,7 @@ public void visitCaseStatement(final CaseStatement statement) {
40824082
private static boolean maybeFallsThrough(Statement statement) {
40834083
if (statement.isEmpty()) return true;
40844084
if (statement instanceof BlockStatement)
4085-
statement = last(((BlockStatement) statement).getStatements());
4085+
statement = DefaultGroovyMethods.last(((BlockStatement) statement).getStatements());
40864086
// end break, continue, return or throw
40874087
if (statement instanceof BreakStatement
40884088
|| statement instanceof ContinueStatement

base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/SimpleTypeLookup.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ protected static Boolean isTypeCompatible(final List<ClassNode> arguments, final
12571257
protected static Boolean isTypeCompatible(final ClassNode source, final ClassNode target) {
12581258
Boolean result = Boolean.TRUE;
12591259
if (!target.equals(source) &&
1260-
!(VariableScope.NULL_TYPE == source && !ClassHelper.isPrimitiveType(target))) {
1260+
!(VariableScope.NULL_TYPE == source && !ClassHelper.isPrimitiveType(target))) {
12611261
// NOTE: Exact match of Closure to SAM Type creates tie for m(Closure) and m(Comparator)
12621262
result = !GroovyUtils.isAssignable(source, target) && !(VariableScope.CLOSURE_CLASS_NODE.equals(source) && ClassHelper.isSAMType(target)) ? Boolean.FALSE : null; // not an exact match
12631263
}

base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/TypeInferencingVisitorWithRequestor.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ private void visitAnnotationKeys(final AnnotationNode node) {
738738
attr = meth; // no Groovy AST node exists for name
739739
noLookup = new TypeLookupResult(meth.getReturnType(), meth.getDeclaringClass(), meth, TypeConfidence.EXACT, scope);
740740
} else {
741-
attr = new ConstantExpression(name);
741+
attr = GeneralUtils.constX(name);
742742
ClassNode type = node.getClassNode();
743743
// this is very rough; it only works for an attribute that directly follows '('
744744
attr.setStart(type.getEnd() + 1);
@@ -838,7 +838,7 @@ public void visitBinaryExpression(final BinaryExpression node) {
838838
scopes.getLast().setMethodCallArgumentTypes(Arrays.asList(dependentExprType, node.getNodeMetaData("rhsType")));
839839
}
840840
// there is an overloadable method associated with this operation; convert to a constant expression and look it up
841-
TypeLookupResult result = lookupExpressionType(new ConstantExpression(associatedMethod), primaryExprType, false, scopes.getLast());
841+
TypeLookupResult result = lookupExpressionType(GeneralUtils.constX(associatedMethod), primaryExprType, false, scopes.getLast());
842842
if (result.confidence != TypeConfidence.UNKNOWN) completeExprType = result.type;
843843
// special case DefaultGroovyMethods.getAt -- the problem is that DGM has too many variants of getAt
844844
if ("getAt".equals(associatedMethod) && VariableScope.DGM_CLASS_NODE.equals(result.declaringType)) {
@@ -1931,7 +1931,7 @@ private void visitUnaryExpression(final Expression node, final Expression operan
19311931
String associatedMethod = findUnaryOperatorName(operation);
19321932
if (associatedMethod != null && !operandType.isDerivedFrom(VariableScope.NUMBER_CLASS_NODE)) {
19331933
scope.setMethodCallArgumentTypes(Collections.emptyList());
1934-
TypeLookupResult result = lookupExpressionType(new ConstantExpression(associatedMethod), operandType, false, scope);
1934+
TypeLookupResult result = lookupExpressionType(GeneralUtils.constX(associatedMethod), operandType, false, scope);
19351935

19361936
exprType = result.confidence.isAtLeast(TypeConfidence.LOOSELY_INFERRED) ? result.type : operandType;
19371937
} else if (node instanceof BooleanExpression) {
@@ -2529,7 +2529,7 @@ private ClassNode[] inferClosureParamTypes(final ClosureExpression node, final V
25292529
Parameter methodParam = findTargetParameter(node, cat.call, methodNode, !methodNode.getDeclaringClass().equals(cat.getPerceivedDeclaringType()));
25302530
if (methodParam != null) {
25312531
if (VariableScope.CLOSURE_CLASS_NODE.equals(methodParam.getType())) {
2532-
GroovyUtils.getAnnotations(methodParam, VariableScope.CLOSURE_PARAMS.getName()).findFirst().ifPresent(cp -> {
2532+
GroovyUtils.getAnnotations(methodParam, ClosureParams.class.getName()).findFirst().ifPresent(cp -> {
25332533
SourceUnit sourceUnit = enclosingModule.getContext();
25342534
CompilationUnit compilationUnit = resolver.compilationUnit;
25352535
try {

base/org.eclipse.jdt.groovy.core/src/org/eclipse/jdt/groovy/search/VariableScope.java

+6
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.codehaus.groovy.ast.expr.ClosureExpression;
6767
import org.codehaus.groovy.ast.expr.Expression;
6868
import org.codehaus.groovy.ast.expr.MethodCall;
69+
import org.codehaus.groovy.ast.expr.MethodCallExpression;
6970
import org.codehaus.groovy.ast.expr.TupleExpression;
7071
import org.codehaus.groovy.ast.stmt.BlockStatement;
7172
import org.codehaus.groovy.ast.stmt.ReturnStatement;
@@ -1300,6 +1301,11 @@ public CallAndType(final MethodCall call, final ASTNode declaration, final Class
13001301
compilationUnit = ((org.codehaus.jdt.groovy.control.EclipseSourceUnit) enclosingModule.getContext()).resolver.compilationUnit;
13011302
}
13021303
ClassNode[] resolved = parseClassNodesFromString(typeName, enclosingModule.getContext(), compilationUnit, methodNode, delegatesToType);
1304+
if (GenericsUtils.hasUnresolvedGenerics(resolved[0])) { // @DelegatesTo(type="T") or @DelegatesTo(type="List<T>")
1305+
GenericsMapper mapper = GenericsMapper.gatherGenerics(GroovyUtils.getParameterTypes(methodNode.getParameters()), declaringType,
1306+
methodNode.getOriginal(), (call instanceof MethodCallExpression ? ((MethodCallExpression) call).getGenericsTypes() : null));
1307+
resolved[0] = resolveTypeParameterization(mapper, resolved[0]);
1308+
}
13031309
addDelegatesToClosure(closure, resolved[0], strategy);
13041310

13051311
} else if (delegatesToValue == null || (delegatesToValue instanceof ClassExpression && delegatesToValue.getType().getName().equals("groovy.lang.DelegatesTo$Target"))) {

0 commit comments

Comments
 (0)