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/01/31 21:07:59 UTC

[groovy] 01/06: GROOVY-9890: STC: always search for default methods

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

commit e542f9f54b381ed682072176a85e8c5545a443f9
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Jan 17 17:58:13 2021 -0600

    GROOVY-9890: STC: always search for default methods
    
    2_5_X backport
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
---
 .../java/org/codehaus/groovy/ast/MethodNode.java   |   7 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   |  30 +++--
 .../groovy/transform/stc/MethodCallsSTCTest.groovy | 150 +++++++++++++++++----
 3 files changed, 149 insertions(+), 38 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/MethodNode.java b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
index c4289b3..8ce5415 100644
--- a/src/main/java/org/codehaus/groovy/ast/MethodNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/MethodNode.java
@@ -148,6 +148,11 @@ public class MethodNode extends AnnotatedNode implements Opcodes {
         return (modifiers & ACC_ABSTRACT) != 0;
     }
 
+    public boolean isDefault() {
+        return (modifiers & (ACC_ABSTRACT | ACC_PUBLIC | ACC_STATIC)) == ACC_PUBLIC
+                && getDeclaringClass() != null && getDeclaringClass().isInterface();
+    }
+
     public boolean isStatic() {
         return (modifiers & ACC_STATIC) != 0;
     }
@@ -260,7 +265,7 @@ public class MethodNode extends AnnotatedNode implements Opcodes {
 
     /**
      * Provides a nicely formatted string of the method definition. For simplicity, generic types on some of the elements
-     * are not displayed. 
+     * are not displayed.
      * @return
      *      string form of node with some generic elements suppressed
      */
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 f505312..e0cad0c 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2409,7 +2409,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
 
                 candidates = findMethodsWithGenerated(receiverType, nameText);
-                collectAllInterfaceMethodsByName(receiverType, nameText, candidates);
                 if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
                 candidates.addAll(findDGMMethodsForClassNode(getTransformLoader(), receiverType, nameText));
                 candidates = filterMethodsByVisibility(candidates);
@@ -4462,10 +4461,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      */
     protected List<MethodNode> findMethodsWithGenerated(ClassNode receiver, String name) {
         List<MethodNode> methods = receiver.getMethods(name);
-        if (methods.isEmpty() || receiver.isResolved()) return methods;
-        List<MethodNode> result = addGeneratedMethods(receiver, methods);
+        if (receiver.isAbstract()) {
+            collectAllInterfaceMethodsByName(receiver, name, methods);
+        } else { // GROOVY-9890: always search for default methods
+            List<MethodNode> interfaceMethods = new ArrayList<>();
+            collectAllInterfaceMethodsByName(receiver, name, interfaceMethods);
+            for (MethodNode method : interfaceMethods) {
+                if (method.isDefault()) methods.add(method);
+            }
+        }
+        if (receiver.isInterface()) {
+            methods.addAll(OBJECT_TYPE.getMethods(name));
+        }
 
-        return result;
+        if (methods.isEmpty() || receiver.isResolved()) {
+            return methods;
+        }
+        return addGeneratedMethods(receiver, methods);
     }
 
     private static List<MethodNode> addGeneratedMethods(final ClassNode receiver, final List<MethodNode> methods) {
@@ -4543,10 +4555,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
         } else {
             methods = findMethodsWithGenerated(receiver, name);
-            if (receiver.isInterface()) {
-                collectAllInterfaceMethodsByName(receiver, name, methods);
-                methods.addAll(OBJECT_TYPE.getMethods(name));
-            }
             // TODO: investigate the trait exclusion a bit further, needed otherwise
             // CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying
             // not static method can't be called from a static context
@@ -4614,12 +4622,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
         }
 
-        if (methods.isEmpty()) {
-            // look at the interfaces, there's a chance that a method is not implemented and we should not hide the
-            // error from the compiler
-            collectAllInterfaceMethodsByName(receiver, name, methods);
-        }
-
         if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
             // lookup in DGM methods too
             findDGMMethodsByNameAndArguments(getSourceUnit().getClassLoader(), receiver, name, args, methods);
diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
index 6971243..2f61d0c 100644
--- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
@@ -18,8 +18,8 @@
  */
 package groovy.transform.stc
 
-import org.codehaus.groovy.control.customizers.ImportCustomizer;
-import static org.codehaus.groovy.control.CompilerConfiguration.DEFAULT as config
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
 
 /**
  * Unit tests for static type checking : method calls.
@@ -34,7 +34,6 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
         } catch (ClassNotFoundException e) {
             IS_PRE_8 = true
         }
-
     }
 
     @Override
@@ -439,7 +438,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
         assertScript '''
                 1.with { obj ->
                     if (obj instanceof String) {
-                        obj.toUpperCase() 
+                        obj.toUpperCase()
                     }
                 }
             '''
@@ -450,11 +449,11 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             public interface SAM {
                 boolean run(String var1, Thread th);
             }
-            
+
             static boolean foo(SAM sam) {
                sam.run("foo",  new Thread())
             }
-            
+
             static def callSAM() {
                 foo { str, th ->
                     str.toUpperCase().equals(th.getName())
@@ -468,11 +467,11 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
 
         assertScript '''
             import java.util.function.Predicate
-            
+
             static boolean foo(Predicate<? super String> p) {
                 p.test("foo")
             }
-            
+
             static def testPredicate() {
                 foo { it ->
                     it.toUpperCase()
@@ -501,9 +500,9 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
                     filter { s -> s.length() < 10 }.
                     toArray()
             }
-            
-            final words = ["orange", "sit", "test", "flabbergasted", "honorific"] 
-            
+
+            final words = ["orange", "sit", "test", "flabbergasted", "honorific"]
+
             println doIt(words)
             '''
     }
@@ -552,7 +551,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
                 }
             '''
     }
-    
+
     void testOneDefaultParam() {
         assertScript '''
             String m(String val = 'hello') {
@@ -590,7 +589,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             m('test', new Object())
         ''', 'm(java.lang.String, java.lang.Object)'
     }
-    
+
     void testMultipleDefaultArgs() {
         assertScript '''
             String m(String first = 'first', String second, String third = 'third') {
@@ -996,17 +995,122 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             Test.test(null)
         '''
     }
+
     void testShouldFindInheritedInterfaceMethod() {
         assertScript '''
-            interface Top { void close() }
+            interface Top { void foo() }
             interface Middle extends Top {}
             interface Bottom extends Middle {}
-            void foo(Bottom obj) {
-               obj.close()
+
+            void test(Bottom b) {
+               b.foo()
+            }
+        '''
+    }
+
+    void testShouldFindInheritedInterfaceMethod2() {
+        assertScript '''
+            interface Top { int foo(int i) }
+            interface Middle extends Top { int foo(String s) }
+            interface Bottom extends Middle {}
+
+            void test(Bottom b) {
+                b.foo(123)
+            }
+        '''
+    }
+
+    void testShouldFindInheritedInterfaceMethod3() {
+        assertScript '''
+            interface Top { int foo(int i) }
+            interface Middle extends Top { }
+            interface Bottom extends Middle { int foo(String s) }
+
+            void test(Bottom b) {
+                b.foo(123)
+            }
+        '''
+    }
+
+    void testShouldFindInheritedInterfaceMethod4() {
+        assertScript '''
+            interface Top { int foo(int i) }
+            interface Middle extends Top { int foo(String s) }
+            abstract class Bottom implements Middle {}
+
+            int test(Bottom b) {
+                b.foo(123)
             }
+            def bot = new Bottom() {
+                int foo(int i) { 1 }
+                int foo(String s) { 2 }
+            }
+            assert test(bot) == 1
+        '''
+    }
+
+    void testShouldFindInheritedInterfaceMethod5() {
+        assertScript '''
+            interface Top { int foo(int i) }
+            interface Middle extends Top { }
+            abstract class Bottom implements Middle { abstract int foo(String s) }
+
+            int test(Bottom b) {
+                b.foo(123)
+            }
+            def bot = new Bottom() {
+                int foo(int i) { 1 }
+                int foo(String s) { 2 }
+            }
+            assert test(bot) == 1
         '''
     }
 
+    // GROOVY-9890
+    void testShouldFindInheritedInterfaceDefaultMethod() {
+        if (IS_PRE_8) return
+
+        def parentDir = File.createTempDir()
+        config.targetDirectory = File.createTempDir()
+        config.jointCompilationOptions = [stubDir: File.createTempDir()]
+        try {
+            def a = new File(parentDir, 'Face.java')
+            a.write '''
+                public interface Face {
+                    default Object foo(long n) {
+                        return n;
+                    }
+                    Object foo(String s);
+                }
+            '''
+            def b = new File(parentDir, 'Impl.groovy')
+            b.write '''
+                class Impl implements Face {
+                    @Override def foo(String s) {
+                        return s
+                    }
+                    // def foo(long n)
+                }
+            '''
+            def c = new File(parentDir, 'Main.groovy')
+            c.write '''
+                def result = new Impl().foo(42L)
+                assert result.class == Long.class
+            '''
+
+            def loader = new GroovyClassLoader(this.class.classLoader)
+            def cu = new JavaAwareCompilationUnit(config, loader)
+            cu.addSources(a, b, c)
+            cu.compile()
+
+            loader.loadClass('Main').main()
+        } finally {
+            parentDir.deleteDir()
+            config.targetDirectory.deleteDir()
+            config.jointCompilationOptions.stubDir.deleteDir()
+        }
+    }
+
     // GROOVY-5743
     void testClosureAsParameter() {
         assertScript '''
@@ -1115,7 +1219,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             assert b.overload('a','b') == 2
         '''
     }
-    
+
     // GROOVY-5883 and GROOVY-6270
     void testClosureUpperBound() {
         assertScript '''
@@ -1148,7 +1252,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             String.doSomething()
         ''', 'Cannot find matching method java.lang.String#doSomething()'
     }
-    
+
     // GROOVY-6646
     void testNPlusVargsCallInOverloadSituation() {
         assertScript '''
@@ -1166,7 +1270,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             assert foo("2","1") == "Strings"
         '''
     }
-    
+
     //GROOVY-6776
     void testPrimtiveParameterAndNullArgument() {
         shouldFailWithMessages '''
@@ -1252,13 +1356,13 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
         import groovy.transform.CompileStatic
         import java.util.stream.Collectors
         import java.util.stream.Stream
-        
+
         @CompileStatic
         public class Test1 {
             public static void main(String[] args) {
                 p();
             }
-            
+
             public static void p() {
                 assert 13 == [1, 2, 3].stream().reduce(7, {Integer r, Integer e -> r + e});
             }
@@ -1272,9 +1376,9 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
 
         assertScript '''
         import groovy.transform.CompileStatic
-        
+
         import static java.util.stream.Collectors.toList
-        
+
         @CompileStatic
         class Test {
             static void main(String[] args) {