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 2026/04/18 00:23:24 UTC

(groovy) 01/01: GROOVY-11929: drop abstract methods from methodsForSuper if no MOP match

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch GROOVY-11929
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit d0030175c8eeb0ab78824b59adeea6663d4cb118
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Apr 17 19:11:49 2026 -0500

    GROOVY-11929: drop abstract methods from methodsForSuper if no MOP match
---
 src/main/java/groovy/lang/MetaClassImpl.java       | 12 +++--
 .../groovy/runtime/metaclass/MetaMethodIndex.java  | 10 ++--
 src/test/groovy/bugs/Groovy11929.groovy            | 58 ++++++++++++++++++++++
 3 files changed, 72 insertions(+), 8 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 3afaf40f47..7fb33f371c 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -518,8 +518,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
                         int matchedMethod = mopArrayIndex(method, c);
                         if (matchedMethod >= 0) {
                             methods.set(i, mopMethods[matchedMethod]);
-                        } else if (!useThis && !isDGM(method) && (isBridge(method)
-                                || c == method.getDeclaringClass().getTheClass())) {
+                        } else if (!useThis && notSuperUseful(method, c)) {
                             methods.remove(i--); // not fit for super usage
                         }
                     }
@@ -534,8 +533,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
                     if (matchedMethod >= 0) {
                         if (useThis) e.methods = mopMethods[matchedMethod];
                         else e.methodsForSuper = mopMethods[matchedMethod];
-                    } else if (!useThis && !isDGM(method) && (isBridge(method)
-                            || c == method.getDeclaringClass().getTheClass())) {
+                    } else if (!useThis && notSuperUseful(method, c)) {
                         e.methodsForSuper = null; // not fit for super usage
                     }
                 }
@@ -576,6 +574,12 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
                 return -1;
             }
 
+            private boolean notSuperUseful(final MetaMethod method, final Class<?> c) {
+                return !isDGM(method) && (isBridge(method)
+                    || method.isAbstract() // GROOVY-11929
+                    || c == method.getDeclaringClass().getTheClass());
+            }
+
             private boolean isBridge(final MetaMethod method) {
                 return (method.getModifiers() & Opcodes.ACC_BRIDGE) != 0;
             }
diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
index 983baacc44..57df90a415 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
@@ -207,13 +207,15 @@ public class MetaMethodIndex {
      * should be kept.
      */
     private static boolean isOverridden(final MetaMethod inIndex, final MetaMethod toIndex) {
-        // do not overwrite private methods
-        if (inIndex.isPrivate()) return false;
+        // don't overwrite private method
+        if (inIndex.isPrivate()) {
+            return false;
+        }
 
         CachedClass inIndexDC = inIndex.getDeclaringClass();
         CachedClass toIndexDC = toIndex.getDeclaringClass();
         if (inIndexDC == toIndexDC) {
-            return isNonRealMethod(toIndex) || inIndex.isSynthetic(); // GROOVY-10136, GROOVY-10594
+            return inIndex.isSynthetic() || isNonRealMethod(toIndex); // GROOVY-10136, GROOVY-10594
         }
 
         // interface vs instance method; be careful...
@@ -221,7 +223,7 @@ public class MetaMethodIndex {
                 && inIndexDC.isInterface() != toIndexDC.isInterface()
                 && !(toIndex instanceof ClosureMetaMethod || toIndex instanceof ClosureStaticMetaMethod)) { // GROOVY-3493
             // this is the old logic created for GROOVY-2391 and GROOVY-7879, which was labeled as "do not overwrite interface methods with instance methods"
-            return (isNonRealMethod(inIndex) || !inIndexDC.isInterface() || toIndexDC.isInterface()) && !toIndexDC.isAssignableFrom(inIndexDC.getTheClass());
+            return (toIndexDC.isInterface() || !inIndexDC.isInterface() || isNonRealMethod(inIndex)) && !toIndexDC.isAssignableFrom(inIndexDC.getTheClass());
         }
 
         // prefer most-specific or most-recent for type disjunction
diff --git a/src/test/groovy/bugs/Groovy11929.groovy b/src/test/groovy/bugs/Groovy11929.groovy
new file mode 100644
index 0000000000..06429a47dd
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy11929.groovy
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package bugs
+
+import org.junit.jupiter.api.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy11929 {
+
+    @Test
+    void testInvokeSpecial() {
+        assertScript '''
+            interface Dao<T> {
+                T save(T t)
+            }
+
+            abstract class AbstractDao<T> implements Dao<T> {
+                @Override
+                T save(T t) { t }
+            }
+
+            interface EntityDao extends Dao<Entity> {
+                @Override
+                Entity save(Entity entity) // not replaced by super$2$save(Object)
+            }
+
+            class EntityDaoImpl extends AbstractDao<Entity> implements EntityDao {
+                @Override
+                Entity save(Entity entity) { super.save(entity) }
+            }
+
+            class Entity {
+                long id
+            }
+
+            def entity = new Entity()
+            def dao = new EntityDaoImpl()
+            assert dao.save(entity) === entity
+        '''
+    }
+}