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 e484ac5a3e GROOVY-2582, GROOVY-11549: no bridge method for interface 
default method
e484ac5a3e is described below

commit e484ac5a3e85b25bb64247b84f56a047606cc54b
Author: Eric Milles <[email protected]>
AuthorDate: Sun Feb 16 13:42:27 2025 -0600

    GROOVY-2582, GROOVY-11549: no bridge method for interface default method
---
 .../org/codehaus/groovy/classgen/Verifier.java     | 31 ++++++++---
 src/test/gls/invocation/CovariantReturnTest.groovy | 26 +++++----
 src/test/groovy/bugs/Groovy4116Bug.groovy          | 62 ++++++++--------------
 3 files changed, 60 insertions(+), 59 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java 
b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 195ac34d77..29bc833155 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -1416,10 +1416,31 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
     }
 
     protected void addCovariantMethods(final ClassNode classNode) {
-        // unimplemented abstract methods from interfaces
-        Map<String, MethodNode> absInterfaceMethods = 
ClassNodeUtils.getDeclaredMethodsFromInterfaces(classNode);
-        Map<String, MethodNode> allInterfaceMethods = new 
HashMap<>(absInterfaceMethods);
-        ClassNodeUtils.addDeclaredMethodsFromAllInterfaces(classNode, 
allInterfaceMethods);
+        Map<String, MethodNode> absInterfaceMethods = new HashMap<>();
+        Map<String, MethodNode> allInterfaceMethods = new HashMap<>();
+        Set<ClassNode> allInterfaces = getAllInterfaces(classNode);
+        allInterfaces.remove(classNode);
+
+        for (ClassNode in : allInterfaces) {
+            for (MethodNode mn : in.getMethods()) {
+                // interfaces may have private/static methods
+                if (mn.isPrivate() || mn.isStatic()) continue;
+
+                if (mn.isAbstract()) {
+                    allInterfaceMethods.putIfAbsent(mn.getTypeDescriptor(), 
mn);
+                } else { // GROOVY-11549: default method replaces an abstract
+                    mn = allInterfaceMethods.put(mn.getTypeDescriptor(), mn);
+                    if (mn != null && !mn.isAbstract())
+                        allInterfaceMethods.put(mn.getTypeDescriptor(), mn);
+                }
+            }
+        }
+
+        for (Map.Entry<String, MethodNode> entry : 
allInterfaceMethods.entrySet()) {
+            if (entry.getValue().isAbstract()/* || 
entry.getValue().isDefault()*/) {
+                absInterfaceMethods.putIfAbsent(entry.getKey(), 
entry.getValue());
+            }
+        }
 
         List<MethodNode> declaredMethods = new 
ArrayList<>(classNode.getMethods());
         for (Iterator<MethodNode> methodsIterator = 
declaredMethods.iterator(); methodsIterator.hasNext(); ) {
@@ -1434,8 +1455,6 @@ public class Verifier implements GroovyClassVisitor, 
Opcodes {
                 throw new RuntimeParserException("The method " + m.getName() + 
" should be public as it implements the corresponding method from interface " + 
interfaceMethod.getDeclaringClass(), sourceOf(m));
             }
         }
-        // interfaces may have private/static methods in JDK9
-        absInterfaceMethods.values().removeIf(interfaceMethod -> 
interfaceMethod.isPrivate() || interfaceMethod.isStatic());
 
         Map<String, MethodNode> methodsToAdd = new HashMap<>();
         Map<String, ClassNode > genericsSpec = Collections.emptyMap();
diff --git a/src/test/gls/invocation/CovariantReturnTest.groovy 
b/src/test/gls/invocation/CovariantReturnTest.groovy
index 98510ce6f7..e90a29fd12 100644
--- a/src/test/gls/invocation/CovariantReturnTest.groovy
+++ b/src/test/gls/invocation/CovariantReturnTest.groovy
@@ -146,20 +146,18 @@ final class CovariantReturnTest {
             assert c.foo() == ""
         '''
 
-        // basically the same as above, but with an example from an error 
report
-        // Properties has a method "String getProperty(String)", this class
-        // is also a GroovyObject, meaning a "Object getProperty(String)" 
method
-        // should be implemented. But this method should not be the usual 
automatically
-        // added getProperty, but a bridge to the getProperty method provided 
by Properties
-        assertScript '''
-            class Configuration extends java.util.Properties {
-            }
-
-            assert Configuration.declaredMethods.count{ it.name=="getProperty" 
} == 1
-            def conf = new Configuration()
-            conf.setProperty("a","b")
-            // the following assert would fail if standard getProperty method 
was added by the compiler
-            assert conf.getProperty("a") == "b"
+        // an example from an error report
+        // Properties has a method "String getProperty(String)"
+        // GroovyObject has a default method "Object getProperty(String)"
+        assertScript '''import static groovy.test.GroovyAssert.shouldFail
+            class C extends Properties { }
+            shouldFail(NoSuchMethodException) {
+                C.getDeclaredMethod("getProperty")
+            }
+
+            def c = new C()
+            c.setProperty("a", "b")
+            assert c.getProperty("a") == "b" // fails if standard method added 
by the compiler
         '''
 
         assertScript '''
diff --git a/src/test/groovy/bugs/Groovy4116Bug.groovy 
b/src/test/groovy/bugs/Groovy4116Bug.groovy
index 4821e8993a..9297542952 100644
--- a/src/test/groovy/bugs/Groovy4116Bug.groovy
+++ b/src/test/groovy/bugs/Groovy4116Bug.groovy
@@ -21,47 +21,31 @@ package groovy.bugs
 import groovy.test.GroovyTestCase
 import org.codehaus.groovy.control.MultipleCompilationErrorsException
 
-class Groovy4116Bug extends GroovyTestCase {
-    void testAnInterfaceMethodNotImplementedPublic() {
-        try {
-            new GroovyShell().parse """
-                class C4116 implements I4116 {
-                    protected foo() {}
-                }
-
-                interface I4116 {
-                    def foo()
-                }
+final class Groovy4116Bug extends GroovyTestCase {
 
-                def c = new C4116()
-                c.foo()
-            """
-            fail('The compilation should have failed as the interface method 
is not implemented as public')
-        } catch (MultipleCompilationErrorsException e) {
-            def syntaxError = e.errorCollector.getSyntaxError(0)
-            assert syntaxError.message.contains('The method foo should be 
public as it implements the corresponding method from interface I4116')
-        }
+    void testAnInterfaceMethodNotImplementedPublic() {
+        def err = shouldFail MultipleCompilationErrorsException, '''
+            class C4116 implements I4116 {
+                protected foo() {}
+            }
+            interface I4116 {
+                def foo()
+            }
+        '''
+        assert err =~ /The method foo should be public as it implements the 
corresponding method from interface I4116/
     }
 
     void testAnInterfaceMethodNotImplementedPublicV2SuperClassInterface() {
-        try {
-            new GroovyShell().parse """
-                abstract class S4116V2 implements I4116V2 { }
-                class C4116V2 extends S4116V2 {
-                    protected foo() {}
-                }
-
-                interface I4116V2 {
-                    def foo()
-                }
-
-                def c = new C4116V2()
-                c.foo()
-            """
-            fail('The compilation should have failed as the interface method 
is not implemented as public')
-        } catch (MultipleCompilationErrorsException e) {
-            def syntaxError = e.errorCollector.getSyntaxError(0)
-            assert syntaxError.message.contains('The method foo should be 
public as it implements the corresponding method from interface I4116')
-        }
+        def err = shouldFail MultipleCompilationErrorsException, '''
+            abstract class A4116 implements I4116 {
+            }
+            class C4116 extends A4116 {
+                protected foo() {}
+            }
+            interface I4116 {
+                def foo()
+            }
+        '''
+        assert err =~ /The method foo should be public as it implements the 
corresponding method from interface I4116/
     }
-}
\ No newline at end of file
+}

Reply via email to