Skip to content

Commit

Permalink
GROOVY-11168
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Sep 20, 2023
1 parent c299939 commit 9418c30
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,7 +29,7 @@
public final class Groovy21InferencingTests extends InferencingTestSuite {

@Test
public void testDelegatesToValue() {
public void testDelegatesToValue1() {
//@formatter:off
String contents =
"class Other { }\n" +
Expand Down Expand Up @@ -136,7 +136,7 @@ public void testDelegatesToTarget2() {
}

@Test // uses constant instead of literal for target
public void testDelegatesToTarget2a() {
public void testDelegatesToTarget3() {
createUnit("C",
"class C {\n" +
" private static final String SELF = 'self'\n" +
Expand Down Expand Up @@ -164,7 +164,7 @@ public void testDelegatesToTarget2a() {
}

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

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

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

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

@Test // https://github.com/groovy/groovy-eclipse/issues/1147
public void testDelegatesToTarget7() {
public void testDelegatesToTarget8() {
//@formatter:off
String contents =
"abstract class A {\n" +
Expand Down Expand Up @@ -329,7 +329,7 @@ public void testDelegatesToTypeName3() {
}

@Test // https://github.com/groovy/groovy-eclipse/issues/966
public void testDelegatesToTypeName3a() {
public void testDelegatesToTypeName4() {
//@formatter:off
createUnit("p", "A",
"package p\n" +
Expand All @@ -352,7 +352,7 @@ public void testDelegatesToTypeName3a() {
}

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

@Test // https://issues.apache.org/jira/browse/GROOVY-11168
public void testDelegatesToTypeName6() {
//@formatter:off
String contents =
"def <T> T m(int i, @DelegatesTo(type='T') Closure block) { }\n" +
"this.<String>m(2) { delegate }";
//@formatter:on
assertType(contents, "delegate", "java.lang.String");
}

@Test
public void testDelegatesToResolveStrategy2() {
public void testDelegatesToResolveStrategy1() {
//@formatter:off
String contents =
"class A {}\n" +
Expand All @@ -399,7 +409,7 @@ public void testDelegatesToResolveStrategy2() {
}

@Test // https://github.com/groovy/groovy-eclipse/issues/657
public void testDelegatesToResolveStrategy3() {
public void testDelegatesToResolveStrategy2() {
//@formatter:off
String contents =
"class A {}\n" +
Expand All @@ -419,7 +429,7 @@ public void testDelegatesToResolveStrategy3() {
}

@Test
public void testDelegatesToResolveStrategy4() {
public void testDelegatesToResolveStrategy3() {
//@formatter:off
String contents =
"class A {}\n" +
Expand Down Expand Up @@ -519,7 +529,7 @@ public void testEnumOverrides4() {
}

@Test // https://github.com/groovy/groovy-eclipse/issues/1100
public void testEnumOverrides4a() {
public void testEnumOverrides5() {
//@formatter:off
String contents =
"@groovy.transform.CompileStatic\n" +
Expand All @@ -539,7 +549,7 @@ public void testEnumOverrides4a() {
}

@Test // https://github.com/groovy/groovy-eclipse/issues/1100
public void testEnumOverrides4b() {
public void testEnumOverrides6() {
//@formatter:off
String contents =
"@groovy.transform.CompileStatic\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7122,4 +7122,23 @@ public void testTypeChecked11083() {
"Groovy:[Static type checking] - Cannot assign value of type java.util.Date to variable of type java.lang.Number\n" +
"----------\n");
}

@Test
public void testTypeChecked11168() {
//@formatter:off
String[] sources = {
"Main.groovy",
"def <T> T m(@DelegatesTo(type='T',strategy=Closure.DELEGATE_FIRST) Closure c) {\n" +
" 'WORKS'.with(c)\n" +
"}\n" +
"@groovy.transform.TypeChecked\n" +
"void test() {\n" +
" this.<String>m { print toLowerCase() }\n" +
"}\n" +
"test()\n",
};
//@formatter:on

runConformTest(sources, "works");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3461,7 +3461,7 @@ private void processClosureParams(final ClassNode receiver, final Expression arg
* of failure.
*/
private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, null).getPlainNodeReference();
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));

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

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

returnType = inferReturnTypeGenerics(receiver, methodNode, arguments, typeArguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3159,7 +3159,7 @@ private void processClosureParams(final ClassNode receiver, final Expression arg
* of failure.
*/
private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, null).getPlainNodeReference();
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));

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

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

returnType = inferReturnTypeGenerics(receiver, methodNode, arguments, typeArguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@
import static org.codehaus.groovy.ast.tools.WideningCategories.lowestUpperBound;
import static org.codehaus.groovy.runtime.ArrayGroovyMethods.asBoolean;
import static org.codehaus.groovy.runtime.ArrayGroovyMethods.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;
Expand Down Expand Up @@ -3305,7 +3304,7 @@ private ClassLoader getTransformLoader() {
* of failure.
*/
private void resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
ClassNode returnType = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, null).getPlainNodeReference();
returnType.setGenericsTypes(Arrays.stream(signature).map(ClassNode::asGenericsType).toArray(GenericsType[]::new));

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

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

returnType = inferReturnTypeGenerics(receiver, methodNode, arguments, typeArguments);
Expand Down Expand Up @@ -4082,7 +4082,7 @@ public void visitCaseStatement(final CaseStatement statement) {
private static boolean maybeFallsThrough(Statement statement) {
if (statement.isEmpty()) return true;
if (statement instanceof BlockStatement)
statement = last(((BlockStatement) statement).getStatements());
statement = DefaultGroovyMethods.last(((BlockStatement) statement).getStatements());
// end break, continue, return or throw
if (statement instanceof BreakStatement
|| statement instanceof ContinueStatement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1257,7 +1257,7 @@ protected static Boolean isTypeCompatible(final List<ClassNode> arguments, final
protected static Boolean isTypeCompatible(final ClassNode source, final ClassNode target) {
Boolean result = Boolean.TRUE;
if (!target.equals(source) &&
!(VariableScope.NULL_TYPE == source && !ClassHelper.isPrimitiveType(target))) {
!(VariableScope.NULL_TYPE == source && !ClassHelper.isPrimitiveType(target))) {
// NOTE: Exact match of Closure to SAM Type creates tie for m(Closure) and m(Comparator)
result = !GroovyUtils.isAssignable(source, target) && !(VariableScope.CLOSURE_CLASS_NODE.equals(source) && ClassHelper.isSAMType(target)) ? Boolean.FALSE : null; // not an exact match
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ private void visitAnnotationKeys(final AnnotationNode node) {
attr = meth; // no Groovy AST node exists for name
noLookup = new TypeLookupResult(meth.getReturnType(), meth.getDeclaringClass(), meth, TypeConfidence.EXACT, scope);
} else {
attr = new ConstantExpression(name);
attr = GeneralUtils.constX(name);
ClassNode type = node.getClassNode();
// this is very rough; it only works for an attribute that directly follows '('
attr.setStart(type.getEnd() + 1);
Expand Down Expand Up @@ -838,7 +838,7 @@ public void visitBinaryExpression(final BinaryExpression node) {
scopes.getLast().setMethodCallArgumentTypes(Arrays.asList(dependentExprType, node.getNodeMetaData("rhsType")));
}
// there is an overloadable method associated with this operation; convert to a constant expression and look it up
TypeLookupResult result = lookupExpressionType(new ConstantExpression(associatedMethod), primaryExprType, false, scopes.getLast());
TypeLookupResult result = lookupExpressionType(GeneralUtils.constX(associatedMethod), primaryExprType, false, scopes.getLast());
if (result.confidence != TypeConfidence.UNKNOWN) completeExprType = result.type;
// special case DefaultGroovyMethods.getAt -- the problem is that DGM has too many variants of getAt
if ("getAt".equals(associatedMethod) && VariableScope.DGM_CLASS_NODE.equals(result.declaringType)) {
Expand Down Expand Up @@ -1931,7 +1931,7 @@ private void visitUnaryExpression(final Expression node, final Expression operan
String associatedMethod = findUnaryOperatorName(operation);
if (associatedMethod != null && !operandType.isDerivedFrom(VariableScope.NUMBER_CLASS_NODE)) {
scope.setMethodCallArgumentTypes(Collections.emptyList());
TypeLookupResult result = lookupExpressionType(new ConstantExpression(associatedMethod), operandType, false, scope);
TypeLookupResult result = lookupExpressionType(GeneralUtils.constX(associatedMethod), operandType, false, scope);

exprType = result.confidence.isAtLeast(TypeConfidence.LOOSELY_INFERRED) ? result.type : operandType;
} else if (node instanceof BooleanExpression) {
Expand Down Expand Up @@ -2529,7 +2529,7 @@ private ClassNode[] inferClosureParamTypes(final ClosureExpression node, final V
Parameter methodParam = findTargetParameter(node, cat.call, methodNode, !methodNode.getDeclaringClass().equals(cat.getPerceivedDeclaringType()));
if (methodParam != null) {
if (VariableScope.CLOSURE_CLASS_NODE.equals(methodParam.getType())) {
GroovyUtils.getAnnotations(methodParam, VariableScope.CLOSURE_PARAMS.getName()).findFirst().ifPresent(cp -> {
GroovyUtils.getAnnotations(methodParam, ClosureParams.class.getName()).findFirst().ifPresent(cp -> {
SourceUnit sourceUnit = enclosingModule.getContext();
CompilationUnit compilationUnit = resolver.compilationUnit;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCall;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
Expand Down Expand Up @@ -1300,6 +1301,11 @@ public CallAndType(final MethodCall call, final ASTNode declaration, final Class
compilationUnit = ((org.codehaus.jdt.groovy.control.EclipseSourceUnit) enclosingModule.getContext()).resolver.compilationUnit;
}
ClassNode[] resolved = parseClassNodesFromString(typeName, enclosingModule.getContext(), compilationUnit, methodNode, delegatesToType);
if (GenericsUtils.hasUnresolvedGenerics(resolved[0])) { // @DelegatesTo(type="T") or @DelegatesTo(type="List<T>")
GenericsMapper mapper = GenericsMapper.gatherGenerics(GroovyUtils.getParameterTypes(methodNode.getParameters()), declaringType,
methodNode.getOriginal(), (call instanceof MethodCallExpression ? ((MethodCallExpression) call).getGenericsTypes() : null));
resolved[0] = resolveTypeParameterization(mapper, resolved[0]);
}
addDelegatesToClosure(closure, resolved[0], strategy);

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

0 comments on commit 9418c30

Please sign in to comment.