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/05/17 16:23:27 UTC
[groovy] branch master updated: GROOVY-10603: LUB: include transitive interfaces
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new b66abe42b5 GROOVY-10603: LUB: include transitive interfaces
b66abe42b5 is described below
commit b66abe42b56b7e3e3b48cdd442fbcdf9ea247e8b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue May 17 11:07:09 2022 -0500
GROOVY-10603: LUB: include transitive interfaces
---
.../groovy/ast/tools/WideningCategories.java | 44 +++++++---------------
.../transform/stc/TernaryOperatorSTCTest.groovy | 15 ++++++++
2 files changed, 29 insertions(+), 30 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
index 4758317396..e919103564 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
@@ -299,7 +299,7 @@ public class WideningCategories {
return gt.getType();
}
- private static ClassNode lowestUpperBound(final ClassNode a, final ClassNode b, List<ClassNode> interfacesImplementedByA, List<ClassNode> interfacesImplementedByB) {
+ private static ClassNode lowestUpperBound(final ClassNode a, final ClassNode b, Set<ClassNode> interfacesImplementedByA, Set<ClassNode> interfacesImplementedByB) {
// first test special cases
if (a == null || b == null) {
// this is a corner case, you should not
@@ -418,47 +418,31 @@ public class WideningCategories {
return buildTypeWithInterfaces(a, b, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
}
- // Look at the super classes
ClassNode sa = a.getUnresolvedSuperClass();
ClassNode sb = b.getUnresolvedSuperClass();
- // extract implemented interfaces before "going up"
- Set<ClassNode> ifa = new HashSet<>();
- extractInterfaces(a, ifa);
- Set<ClassNode> ifb = new HashSet<>();
- extractInterfaces(b, ifb);
- interfacesImplementedByA = interfacesImplementedByA == null ? new LinkedList<>(ifa) : interfacesImplementedByA;
- interfacesImplementedByB = interfacesImplementedByB == null ? new LinkedList<>(ifb) : interfacesImplementedByB;
-
- // check if no superclass is defined
- // meaning that we reached the top of the object hierarchy
- if (sa == null || sb == null)
- return buildTypeWithInterfaces(OBJECT_TYPE, OBJECT_TYPE, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
+ if (interfacesImplementedByA == null)
+ interfacesImplementedByA = GeneralUtils.getInterfacesAndSuperInterfaces(a);
+ if (interfacesImplementedByB == null)
+ interfacesImplementedByB = GeneralUtils.getInterfacesAndSuperInterfaces(b);
- // if one superclass is derived (or equals) another
- // then it is the common super type
+ // check if no superclass is defined, meaning that we reached the top of the object hierarchy
+ if (sa == null || sb == null) {
+ return buildTypeWithInterfaces(OBJECT_TYPE, OBJECT_TYPE, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
+ }
+ // if one superclass is derived (or equals) another then it is the common super type
if (sa.isDerivedFrom(sb) || sb.isDerivedFrom(sa)) {
return buildTypeWithInterfaces(sa, sb, keepLowestCommonInterfaces(interfacesImplementedByA, interfacesImplementedByB));
}
- // superclasses are on distinct hierarchy branches, so we
- // recurse on them
+ // superclasses are on distinct hierarchy branches, so we recurse on them
return lowestUpperBound(sa, sb, interfacesImplementedByA, interfacesImplementedByB);
}
- private static void extractInterfaces(final ClassNode node, final Set<ClassNode> interfaces) {
- if (node == null) return;
- Collections.addAll(interfaces, node.getInterfaces());
- extractInterfaces(node.getSuperClass(), interfaces);
- }
-
/**
- * Given the list of interfaces implemented by two class nodes, returns the list of the most specific common
- * implemented interfaces.
- * @param fromA
- * @param fromB
- * @return the list of the most specific common implemented interfaces
+ * Given the interfaces implemented by two types, returns a list of the most
+ * specific common implemented interfaces.
*/
- private static List<ClassNode> keepLowestCommonInterfaces(final List<ClassNode> fromA, final List<ClassNode> fromB) {
+ private static List<ClassNode> keepLowestCommonInterfaces(final Set<ClassNode> fromA, final Set<ClassNode> fromB) {
if (fromA == null || fromB == null) return Collections.emptyList();
Set<ClassNode> common = new HashSet<>(fromA);
common.retainAll(fromB);
diff --git a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
index 7fd3ab0fe2..c8708bccea 100644
--- a/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TernaryOperatorSTCTest.groovy
@@ -210,6 +210,21 @@ class TernaryOperatorSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10603
+ void testCommonInterface2() {
+ assertScript '''
+ interface I {}
+ interface J extends I {}
+ class Foo implements I {}
+ class Bar implements J {}
+
+ I test(Foo x, Bar y) {
+ true ? x : y // Cannot return value of type GroovyObject for method returning I
+ }
+ test(null, null)
+ '''
+ }
+
// GROOVY-10130
void testInstanceofGuard() {
assertScript '''