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

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

commit a688de615839e5171e023855480d057531fedd19
Author: Eric Milles <[email protected]>
AuthorDate: Sun Feb 15 09:41:45 2026 -0600

    GROOVY-11858: defer non-closure method for in-closure implicit-this call
---
 src/main/java/groovy/lang/MetaClassImpl.java                |  2 +-
 .../codehaus/groovy/runtime/metaclass/ClosureMetaClass.java | 13 +++++++++++++
 src/test/groovy/gls/invocation/ClosureDelegationTest.groovy | 13 +++++++++++++
 3 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java 
b/src/main/java/groovy/lang/MetaClassImpl.java
index ffeb8069c0..126509ff46 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -1265,7 +1265,7 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
         return outer;
     }
 
-    private static Class<?> getNonClosureOuter(Class<?> c) {
+    protected static Class<?> getNonClosureOuter(Class<?> c) {
         do { c = c.getEnclosingClass();
         } while (c != null && GeneratedClosure.class.isAssignableFrom(c));
 
diff --git 
a/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java 
b/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
index 247cbf2048..81c4733d53 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
@@ -261,8 +261,19 @@ public final class ClosureMetaClass extends MetaClassImpl {
             if (method == null) throw new MissingMethodException(methodName, 
theClass, theArguments, false);
         }
 
+        MetaMethod cmm = null;
         if (method == null && (resolveStrategy != Closure.DELEGATE_ONLY || 
!isInternalMethod(methodName))) {
             method = CLOSURE_METACLASS.pickMethod(methodName, argClasses);
+            // GROOVY-11858: delegate or owner may be desired target
+            if (method != null && resolveStrategy != Closure.TO_SELF
+                    && sender == getNonClosureOuter(getTheClass())) {
+                CachedClass methodOwner = method.getDeclaringClass();
+                if (methodOwner != getTheCachedClass()
+                        && methodOwner.getTheClass() != Closure.class
+                        && methodOwner.getTheClass() != GroovyObject.class) {
+                    cmm= method; method= null; // try delegate or owner first
+                }
+            }
         }
 
         if (method != null) return method.doMethodInvoke(closure, 
theArguments);
@@ -323,6 +334,8 @@ public final class ClosureMetaClass extends MetaClassImpl {
             }
         }
 
+        if (cmm != null) cmm.doMethodInvoke(closure, theArguments);
+
         throw new MissingMethodException(methodName, theClass, theArguments, 
false);
     }
 
diff --git a/src/test/groovy/gls/invocation/ClosureDelegationTest.groovy 
b/src/test/groovy/gls/invocation/ClosureDelegationTest.groovy
index 77ca5d319f..3c0c8571c7 100644
--- a/src/test/groovy/gls/invocation/ClosureDelegationTest.groovy
+++ b/src/test/groovy/gls/invocation/ClosureDelegationTest.groovy
@@ -144,4 +144,17 @@ final class ClosureDelegationTest {
             assert Bar.visited == true
         '''
     }
+
+    // GROOVY-11858
+    @Test
+    void testDelegateMethodVsClosureExtension() {
+        assertScript '''
+            def array = 'a b c'.with { split() }
+
+            assert array.length == 3
+            assert array.contains('a')
+            assert array.contains('b')
+            assert array.contains('c')
+        '''
+    }
 }

Reply via email to