Skip to content

Commit

Permalink
GROOVY-9803, GROOVY-10974, GROOVY-10975, GROOVY-11241, GROOVY-11259
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Jan 4, 2024
1 parent dca052d commit 64dfe08
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -964,12 +964,9 @@ public void testClosure11() {
"interface F<X,Y> {\n" +
" Y apply(X x)\n" +
"}\n" +
"@groovy.transform.TypeChecked\n" +
"void test() {\n" +
" def c = C.of(123)\n" +
" def d = c.map(" + toSet + ")\n" +
" def e = d.map{x -> x.first()}\n" +
"}\n";
"def c = C.of(123)\n" +
"def d = c.map(" + toSet + ")\n" +
"def e = d.map{x -> x.first()}\n";
assertType(contents, "d", "C<java.util.Set<java.lang.Integer>>");
assertType(contents, "x", "java.util.Set<java.lang.Integer>");
assertType(contents, "e", "C<java.lang.Integer>");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2023 the original author or authors.
* Copyright 2009-2024 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 @@ -7417,9 +7417,14 @@ public void testCompileStatic10047b() {
runNegativeTest(sources,
"----------\n" +
"1. ERROR in Main.groovy (at line 5)\n" +
"\tprint(['a','bc','def'].stream().collect(toMap(Function.<String>identity(), List::size)))\n" +
"\tprint(['a','bc','def'].stream().collect(toMap(Function.<String>identity(), List::size)))\n" + (!isAtLeastGroovy(40)
?
"\t ^^^^^^^^^^\n" +
"Groovy:Failed to find class method 'size(java.lang.String)' or instance method 'size()' for the type: java.util.List\n" +
"Groovy:Failed to find class method 'size(java.lang.String)' or instance method 'size()' for the type: java.util.List\n"
:
"\t ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Groovy:[Static type checking] - Cannot call <R,A> java.util.stream.Stream#collect(java.util.stream.Collector<? super java.lang.String, A, R>) with arguments [java.util.stream.Collector<java.util.List, ?, java.util.Map<java.lang.String, java.lang.Integer>>]\n"
) +
"----------\n");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2023 the original author or authors.
* Copyright 2009-2024 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 @@ -2896,6 +2896,41 @@ public void testTypeChecked9769() {
runConformTest(sources);
}

@Test
public void testTypeChecked9803() {
//@formatter:off
String[] sources = {
"Main.groovy",
"def <E> Set<E> asSet(E element) {Collections.singleton(element)}\n" +
"@groovy.transform.TypeChecked\n" +
"Try<String> test() {\n" +
" def try_of_str = Try.success('WORKS')\n" +
" def try_of_opt = try_of_str.map(this.&asSet)\n" +
" try_of_str = try_of_opt.map{it.first().toLowerCase()}\n" +
"}\n" +
"print(test().x)\n",

"Try.groovy",
"import java.util.function.Function\n" +
"class Try<X> { X x\n" +
" static <Y> Try<Y> success(Y y) {\n" +
" new Try<Y>(x: y)\n" +
" }\n" +
" def <Z> Try<Z> map(Function<? super X, ? extends Z> f) {\n" +
" new Try<Z>(x: f.apply(x))\n" +
" }\n" +
"}\n",
};
//@formatter:on

runConformTest(sources, "works");
if (isParrotParser()) {
sources[1] = sources[1]
.replace(".&", "::");
runConformTest(sources, "works");
}
}

@Test
public void testTypeChecked9821() {
//@formatter:off
Expand Down Expand Up @@ -7095,23 +7130,52 @@ public void testTypeChecked10897() {
}

@Test
public void testTypeChecked10975() {
assumeTrue(isParrotParser());
public void testTypeChecked10974() {
//@formatter:off
String[] sources = {
"Main.groovy",
"import java.util.stream.*\n" +
"import java.util.function.*\n" +
"@groovy.transform.TypeChecked\n" +
"def test(DoubleStream x, ObjDoubleConsumer<Boolean> y, BiConsumer<Boolean, Boolean> z) {\n" +
" def b = x.collect({ -> true}, y.&accept, z.&accept) // b should infer as Boolean\n" +
" // <R> R collect(Supplier<R>,ObjDoubleConsumer<R>,BiConsumer<R,R>)\n" +
" Spliterator.OfDouble s_of_d = Arrays.spliterator(new double[0])\n" +
" StreamSupport.doubleStream(s_of_d, /*parallel:*/b)\n" +
"}\n" +
"test(DoubleStream.of(0d), {Boolean b, double d -> }, {Boolean e, Boolean f -> })\n",
};
//@formatter:on

runConformTest(sources);
if (isParrotParser()) {
sources[1] = sources[1]
.replace(".&", "::");
runConformTest(sources);
}
}

@Test
public void testTypeChecked10975() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.TypeChecked\n" +
"void test() {\n" +
" Collection<Integer> c = [1]\n" +
" Map<Integer,Integer> m = [1:1]\n" +
" new Hashtable(Collections.min(c, m::put))\n" + // Cannot find matching constructor Hashtable(Object)
" new Hashtable(Collections.min(c, m.&put))\n" + // Cannot find matching constructor Hashtable(Object)
"}\n" +
"test()\n",
};
//@formatter:on

runConformTest(sources);
if (isParrotParser()) {
sources[1] = sources[1]
.replace(".&", "::");
runConformTest(sources);
}
}

@Test
Expand Down Expand Up @@ -7238,4 +7302,63 @@ public void testTypeChecked11168() {

runConformTest(sources, "works");
}

@Test
public void testTypeChecked11241() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.TypeChecked\n" +
"Try<String> test() {\n" +
" def try_of = Try.of{Optional.of('works')}\n" +
" def result = try_of.mapTry(Optional.&get)\n" +
" return result\n" +
"}\n" +
"print(test().x)\n",

"Try.groovy",
"import java.util.function.*\n" +
"class Try<X> { X x\n" +
" static <Y> Try<Y> of(Supplier<? extends Y> s) {\n" +
" new Try<Y>(x: s.get())\n" +
" }\n" +
" def <Z> Try<Z> mapTry(Function<? super X, ? extends Z> f) {\n" +
" new Try<Z>(x: f.apply(x))\n" +
" }\n" +
"}\n",
};
//@formatter:on

runConformTest(sources, "works");
if (isParrotParser()) {
sources[1] = sources[1]
.replace(".&", "::");
runConformTest(sources, "works");
}
}

@Test
public void testTypeChecked11259() {
//@formatter:off
String[] sources = {
"Main.groovy",
"void consume(Set<String> keys){print(keys)}\n" +
"@groovy.transform.TypeChecked\n" +
"void test(Map<String, String> map) {\n" +
" def keys = map.entrySet().stream()\n" +
" .map(Map.Entry.&getKey)\n" +
" .toSet()\n" +
" consume(keys)\n" +
"}\n" +
"test(foo:'bar', fizz:'buzz')\n",
};
//@formatter:on

runConformTest(sources, "[foo, fizz]");
if (isParrotParser()) {
sources[1] = sources[1]
.replace(".&", "::");
runConformTest(sources, "[foo, fizz]");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5599,9 +5599,15 @@ protected ClassNode inferReturnTypeGenerics(final ClassNode receiver, final Meth
// 1) resolve type parameters of method

if (methodGenericTypes != null) {
Parameter[] parameters = method.getParameters();
// GRECLIPSE add -- GROOVY-11241, GROOVY-11259, et al.
if (asBoolean(context)) parameters = Arrays.stream(parameters).map(p ->
new Parameter(applyGenericsContext(context, p.getType()), p.getName())
).toArray(Parameter[]::new);
// GRECLIPSE end
Map<GenericsTypeName, GenericsType> resolvedPlaceholders = new HashMap<>();
for (GenericsType gt : methodGenericTypes) resolvedPlaceholders.put(new GenericsTypeName(gt.getName()), gt);
applyGenericsConnections(extractGenericsConnectionsFromArguments(methodGenericTypes, method.getParameters(), arguments, explicitTypeHints), resolvedPlaceholders);
applyGenericsConnections(extractGenericsConnectionsFromArguments(methodGenericTypes, parameters, arguments, explicitTypeHints), resolvedPlaceholders);

returnType = applyGenericsContext(resolvedPlaceholders, returnType);
}
Expand Down Expand Up @@ -5825,6 +5831,17 @@ private static ClassNode[] collateMethodReferenceParameterTypes(final MethodPoin
System.arraycopy(target.getParameters(), 0, params, 1, n);
} else {
params = target.getParameters();
// GRECLIPSE add -- GROOVY-10974
if (!target.isStatic() && target.getDeclaringClass().getGenericsTypes() != null) {
ClassNode objectExprType = source.getExpression().getNodeMetaData(INFERRED_TYPE);
if (objectExprType == null && source.getExpression() instanceof VariableExpression) {
Variable variable = ((VariableExpression) source.getExpression()).getAccessedVariable();
if (variable instanceof ASTNode) objectExprType = ((ASTNode) variable).getNodeMetaData(INFERRED_TYPE);
}
Map<GenericsTypeName,GenericsType> spec = extractPlaceHolders(objectExprType, target.getDeclaringClass());
return applyGenericsContext(spec, extractTypesFromParameters(params));
}
// GRECLIPSE end
}

return extractTypesFromParameters(params);
Expand Down Expand Up @@ -5856,16 +5873,29 @@ private static ClassNode convertClosureTypeToSAMType(Expression expression, fina
applyGenericsContext(placeholders, extractTypesFromParameters(parameters))
);
if (mn != null) {
// GRECLIPSE add -- GROOVY-10067, GROOVY-11259
if (GenericsUtils.hasUnresolvedGenerics(closureReturnType))
closureReturnType = mn.getReturnType(); // reset any #T
// GRECLIPSE end
ClassNode[] pTypes = collateMethodReferenceParameterTypes(mp, mn);
Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
// GRECLIPSE add -- GROOVY-11241
if (!mn.isStatic() && mp.getExpression() instanceof ClassExpression) {
ClassNode objectType = parameters[0].getType();
objectType = applyGenericsContext(placeholders, objectType);
extractGenericsConnections(connections, objectType, pTypes[0].redirect());
}
// GRECLIPSE end
for (int i = 0, n = parameters.length; i < n; i += 1) {
// SAM parameters should align one-for-one with the referenced method's parameters
extractGenericsConnections(connections, parameters[i].getOriginType(), pTypes[i]);
}
// convert the method reference's generics into the SAM's generics domain
closureReturnType = applyGenericsContext(connections, closureReturnType);
pTypes = applyGenericsContext(connections, pTypes);
// apply known generics connections to the placeholders of the return type
closureReturnType = applyGenericsContext(placeholders, closureReturnType);
pTypes = applyGenericsContext(placeholders, pTypes); // and the parameters

expression = new ClosureExpression(Arrays.stream(pTypes).map(t -> new Parameter(t,"")).toArray(Parameter[]::new), null);
}
Expand All @@ -5876,11 +5906,13 @@ private static ClassNode convertClosureTypeToSAMType(Expression expression, fina

// repeat the same for each parameter given in the ClosureExpression
if (parameters.length > 0 && expression instanceof ClosureExpression) {
ClassNode[] paramTypes = applyGenericsContext(placeholders, extractTypesFromParameters(parameters));
int i = 0;
// GROOVY-10054, GROOVY-10699, GROOVY-10749, et al.
for (Parameter p : getParametersSafe((ClosureExpression) expression))
if (!p.isDynamicTyped()) extractGenericsConnections(placeholders, p.getType(), paramTypes[i++]);
// GROOVY-10054, GROOVY-10699, GROOVY-10749, GROOVY-10974, GROOVY-11241
for (Parameter p : getParametersSafe((ClosureExpression) expression)) {
if (!p.isDynamicTyped())
extractGenericsConnections(placeholders, p.getType(), parameters[i].getType());
i += 1;
}
}
}

Expand Down Expand Up @@ -6357,4 +6389,4 @@ public void visitVariableExpression(final VariableExpression expression) {
super.visitVariableExpression(expression);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5484,15 +5484,20 @@ private Map<GenericsTypeName, GenericsType> extractGenericsConnectionsFromArgume
List<MethodNode> candidates = argument.getNodeMetaData(MethodNode.class);
if (candidates != null && !candidates.isEmpty()) {
MethodPointerExpression methodPointer = (MethodPointerExpression) argument;
p = methodPointer.getNodeMetaData(CLOSURE_ARGUMENTS); // GROOVY-10974, GROOVY-10975
if (p == null) p = collateMethodReferenceParameterTypes(methodPointer, candidates.get(0));
p = collateMethodReferenceParameterTypes(methodPointer, candidates.get(0));
if (p.length > 0 && GenericsUtils.hasUnresolvedGenerics(returnType)) {
// GRECLIPSE add -- GROOVY-11241, GROOVY-11259
returnType = candidates.get(0).getReturnType();
if (!candidates.get(0).isStatic() && methodPointer.getExpression() instanceof ClassExpression)
extractGenericsConnections(connections, q[0], p[0].redirect());
// GRECLIPSE end
for (int j = 0; j < q.length; j += 1) {
// SAM parameters are like arguments in this case
extractGenericsConnections(connections, q[j], p[j]);
}
// convert the method's generics into the SAM's generics
returnType = applyGenericsContext(connections, returnType);
p = applyGenericsContext(connections, p );

connections.clear();
}
Expand Down Expand Up @@ -5647,6 +5652,17 @@ private static ClassNode[] collateMethodReferenceParameterTypes(final MethodPoin
System.arraycopy(target.getParameters(), 0, params, 1, n);
} else {
params = target.getParameters();
// GRECLIPSE add -- GROOVY-10974, GROOVY-10975
if (!target.isStatic() && target.getDeclaringClass().getGenericsTypes() != null) {
ClassNode objectExprType = source.getExpression().getNodeMetaData(INFERRED_TYPE);
if (objectExprType == null && source.getExpression() instanceof VariableExpression) {
Variable variable = ((VariableExpression) source.getExpression()).getAccessedVariable();
if (variable instanceof ASTNode) objectExprType = ((ASTNode) variable).getNodeMetaData(INFERRED_TYPE);
}
Map<GenericsTypeName,GenericsType> spec = extractPlaceHolders(objectExprType, target.getDeclaringClass());
return applyGenericsContext(spec, extractTypesFromParameters(params));
}
// GRECLIPSE end
}

return extractTypesFromParameters(params);
Expand Down
Loading

0 comments on commit 64dfe08

Please sign in to comment.