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 '''