GROOVY-11364: STC: propagate receiver generics to candidate return types
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 5b04334..7d609f5 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2584,8 +2584,17 @@
receiverType = GenericsUtils.parameterizeType(arguments[0], receiverType); // GROOVY-11241
}
- Map<GenericsTypeName, GenericsType> gts = GenericsUtils.extractPlaceholders(receiverType);
- candidates.stream().map(candidate -> applyGenericsContext(gts, candidate.getReturnType()))
+ ClassNode ownerType = receiverType;
+ candidates.stream()
+ .map(candidate -> {
+ ClassNode returnType = candidate.getReturnType();
+ if (!candidate.isStatic() && GenericsUtils.hasUnresolvedGenerics(returnType)) {
+ Map<GenericsTypeName, GenericsType> spec = new HashMap<>(); // GROOVY-11364
+ extractGenericsConnections(spec, ownerType, candidate.getDeclaringClass());
+ returnType = applyGenericsContext(spec, returnType);
+ }
+ return returnType;
+ })
.reduce(WideningCategories::lowestUpperBound).ifPresent(returnType -> {
ClassNode closureType = wrapClosureType(returnType);
storeType(expression, closureType);
@@ -2603,7 +2612,7 @@
ClassNode[] parameters = collateMethodReferenceParameterTypes(expression, candidates.get(0));
for (int i = 0; i < arguments.length; i += 1) {
ClassNode at = arguments[i];
- ClassNode pt = applyGenericsContext(gts, parameters[Math.min(i, parameters.length - 1)]);
+ ClassNode pt = parameters[Math.min(i, parameters.length - 1)];
if (!pt.equals(at) && (at.isInterface() ? pt.implementsInterface(at) : pt.isDerivedFrom(at)))
arguments[i] = pt; // GROOVY-10734, GROOVY-10807, GROPOVY-11026: expected type is refined
}
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index a75a3f5..1fc2833 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -38,7 +38,7 @@
void testFunction() {
assertScript shell, '''
def f() {
- [1, 2, 3].stream().map(e -> e + 1).collect(Collectors.toList())
+ [1, 2, 3].stream().map(i -> i + 1).toList()
}
assert f() == [2, 3, 4]
'''
@@ -48,27 +48,50 @@
void testFunction2() {
assertScript shell, '''
def f() {
- [1, 2, 3].stream().map(e -> e.plus(1)).collect(Collectors.toList())
+ [1, 2, 3].stream().map(i -> i.plus(1)).toList()
}
assert f() == [2, 3, 4]
'''
}
@Test
- void testFunctionWithTypeArgument() {
+ void testFunction3() {
assertScript shell, '''
def f() {
- [1, 2, 3].stream().<String>map(i -> null).collect(Collectors.toList())
+ [1, 2, 3].stream().<String>map(i -> null).toList()
}
assert f() == [null, null, null]
'''
}
+ // GROOVY-11364
+ @Test
+ void testFunction4() {
+ assertScript shell, '''
+ abstract class A<N extends Number> {
+ protected N process(N n) { n }
+ }
+
+ class C extends A<Integer> {
+ static void consume(Optional<Integer> option) {
+ def result = option.orElse(null)
+ assert result instanceof Integer
+ assert result == 42
+ }
+ void test() {
+ consume(Optional.of(42).map(i -> process(i)))
+ }
+ }
+
+ new C().test()
+ '''
+ }
+
@Test
void testBinaryOperator() {
assertScript shell, '''
def f() {
- [1, 2, 3].stream().reduce(7, (Integer r, Integer e) -> r + e)
+ [1, 2, 3].stream().reduce(7, (Integer r, Integer i) -> r + i)
}
assert f() == 13
'''
@@ -79,7 +102,7 @@
void testBinaryOperatorWithoutExplicitTypes() {
assertScript shell, '''
def f() {
- [1, 2, 3].stream().reduce(7, (r, e) -> r + e)
+ [1, 2, 3].stream().reduce(7, (r, i) -> r + i)
}
assert f() == 13
'''
@@ -89,7 +112,7 @@
void testBinaryOperatorWithoutExplicitTypes2() {
assertScript shell, '''
def f() {
- BinaryOperator<Integer> accumulator = (r, e) -> r + e
+ BinaryOperator<Integer> accumulator = (r, i) -> r + i
return [1, 2, 3].stream().reduce(7, accumulator)
}
assert f() == 13
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 1ebe929..3aac78c 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -613,6 +613,29 @@
'''
}
+ @Test // instance::instanceMethod -- GROOVY-11364
+ void testFunctionII5() {
+ assertScript shell, '''
+ abstract class A<N extends Number> {
+ protected N process(N n) { n }
+ }
+
+ @CompileStatic
+ class C extends A<Integer> {
+ static void consume(Optional<Integer> option) {
+ def result = option.orElse(null)
+ assert result instanceof Integer
+ assert result == 42
+ }
+ void test() {
+ consume(Optional.of(42).map(this::process))
+ }
+ }
+
+ new C().test()
+ '''
+ }
+
@Test // instance::instanceMethod -- GROOVY-10057
void testPredicateII() {
assertScript shell, '''