You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/02/03 18:43:40 UTC
[groovy] branch GROOVY_2_5_X updated: GROOVY-9974: extract generics connections: Closure vs SAM return type
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
new 048925d GROOVY-9974: extract generics connections: Closure<T> vs SAM return type
048925d is described below
commit 048925dcba0331aa4282878ba0b18bacf6f0c13b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Feb 3 12:34:18 2022 -0600
GROOVY-9974: extract generics connections: Closure<T> vs SAM return type
2_5_X backport
---
.../transform/stc/StaticTypeCheckingSupport.java | 64 +++++++++++++---------
.../groovy/transform/stc/ClosuresSTCTest.groovy | 32 ++++++++++-
2 files changed, 69 insertions(+), 27 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index dcdbd15..00045c2 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -100,6 +100,7 @@ import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.byte_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.findSAM;
import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.getUnwrapper;
import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
@@ -112,7 +113,6 @@ import static org.codehaus.groovy.ast.ClassHelper.make;
import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
-import static org.codehaus.groovy.ast.tools.GenericsUtils.getSuperClass;
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
import static org.codehaus.groovy.syntax.Types.ASSIGN;
import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
@@ -1743,37 +1743,51 @@ public abstract class StaticTypeCheckingSupport {
}
/**
- * use supplied type to make a connection from usage to declaration
- * The method operates in two modes.
- * * For type !instanceof target a structural compare will be done
- * (for example Dummy<T> and List<R> to get T=R)
- * * If type equals target, a structural match is done as well
- * (for example Collection<U> and Collection<E> to get U=E)
- * * otherwise we climb the hierarchy to find a case of type equals target
- * to then execute the structural match, while applying possibly existing
- * generics contexts on the way (for example for IntRange and Collection<E>
- * to get E=Integer, since IntRange is an AbstractList<Integer>)
+ * Uses supplied type to make a connection from usage to declaration.
+ * <p>
+ * The method operates in two modes:
+ * <ul>
+ * <li>For type !instanceof target a structural compare will be done
+ * (for example Type<T> and List<E> to get E -> T)
+ * <li>If type equals target, a structural match is done as well
+ * (for example Collection<T> and Collection<E> to get E -> T)
+ * <li>Otherwise we climb the hierarchy to find a case of type equals target
+ * to then execute the structural match, while applying possibly existing
+ * generics contexts on the way (for example for IntRange and Collection<E>
+ * to get E -> Integer, since IntRange is an AbstractList<Integer>)
+ * </ul>
* Should the target not have any generics this method does nothing.
*/
static void extractGenericsConnections(Map<GenericsTypeName, GenericsType> connections, ClassNode type, ClassNode target) {
- if (target == null || type == target || !isUsingGenericsOrIsArrayUsingGenerics(target)) return;
+ if (target == null || target == type || !isUsingGenericsOrIsArrayUsingGenerics(target)) return;
if (type == null || type == UNKNOWN_PARAMETER_TYPE) return;
- if (type.isArray() && target.isArray()) {
+
+ MethodNode sam;
+
+ if (target.isGenericsPlaceHolder()) {
+ connections.put(new GenericsTypeName(target.getUnresolvedName()), new GenericsType(type));
+
+ } else if (type.isArray() && target.isArray()) {
extractGenericsConnections(connections, type.getComponentType(), target.getComponentType());
- } else if (target.isGenericsPlaceHolder() || type.equals(target) || !implementsInterfaceOrIsSubclassOf(type, target)) {
- // structural match route
- if (target.isGenericsPlaceHolder()) {
- connections.put(new GenericsTypeName(target.getGenericsTypes()[0].getName()), new GenericsType(type));
- } else {
- extractGenericsConnections(connections, type.getGenericsTypes(), target.getGenericsTypes());
- }
- } else {
- // have first to find matching super class or interface
- ClassNode superClass = getSuperClass(type, target);
+ } else if (type.equals(CLOSURE_TYPE) && (sam = findSAM(target)) != null) { // GROOVY-9974
+ ClassNode returnType = StaticTypeCheckingVisitor.wrapTypeIfNecessary(sam.getReturnType());
+ extractGenericsConnections(connections, type.getGenericsTypes(), new GenericsType[] {new GenericsType(returnType)});
+
+ } else if (type.equals(target) || !implementsInterfaceOrIsSubclassOf(type, target)) {
+ extractGenericsConnections(connections, type.getGenericsTypes(), target.getGenericsTypes());
+
+ } else {
+ // find matching super class or interface
+ ClassNode superClass = GenericsUtils.getSuperClass(type, target);
if (superClass != null) {
- ClassNode corrected = getCorrectedClassNode(type, superClass, true);
- extractGenericsConnections(connections, corrected, target);
+ if (GenericsUtils.hasUnresolvedGenerics(superClass)) {
+ Map<String, ClassNode> spec = GenericsUtils.createGenericsSpec(type);
+ if (!spec.isEmpty()) {
+ superClass = GenericsUtils.correctToGenericsSpecRecurse(spec, superClass);
+ }
+ }
+ extractGenericsConnections(connections, superClass, target);
} else {
// if we reach here, we have an unhandled case
throw new GroovyBugError("The type " + type + " seems not to normally extend " + target + ". Sorry, I cannot handle this.");
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index abaca4d..64a7bcc 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -413,7 +413,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
''', 'Cannot find matching method'
}
- //GROOVY-6189
+ // GROOVY-6189
void testSAMsInMethodSelection(){
// simple direct case
assertScript """
@@ -595,6 +595,35 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
""", "Cannot assign"
}
+ // GROOVY-9974
+ void testStreamsAPI() {
+ try {
+ Class.forName('java.util.function.Predicate')
+ } catch (ClassNotFoundException e) {
+ return
+ }
+ assertScript '''
+ import groovy.transform.*
+
+ @ToString
+ class Pogo {
+ String foo
+ }
+ @Newify(Pogo)
+ List<Pogo> m() {
+ [Pogo(foo:'bar'),Pogo(foo:'baz')]
+ }
+ String test() {
+ m().with { list ->
+ def other = []
+ list.stream().filter{ it.foo.startsWith('ba') }.toList()
+ }
+ }
+
+ assert test() == '[Pogo(bar), Pogo(baz)]'
+ '''
+ }
+
// GROOVY-6238
void testDirectMethodCallOnClosureExpression() {
assertScript '''
@@ -686,4 +715,3 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
}
}
}
-