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

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

commit 6b526a9def70971e98d22a9a1cfeb7da59a3f9ea
Author: Eric Milles <[email protected]>
AuthorDate: Sun Dec 28 19:36:56 2025 -0600

    GROOVY-11823: try `super.propertyMissing(name)` before outer class
---
 src/main/java/groovy/lang/MetaClassImpl.java       |  4 +-
 .../classgen/InnerClassCompletionVisitor.java      | 50 +++++++++++++++++++---
 .../groovy/gls/innerClass/InnerClassTest.groovy    | 20 +++++++++
 3 files changed, 65 insertions(+), 9 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java 
b/src/main/java/groovy/lang/MetaClassImpl.java
index 767ad24801..541fcbd3fb 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -866,8 +866,8 @@ public class MetaClassImpl implements MetaClass, 
MutableMetaClass {
         } catch (InvokerInvocationException iie) {
             boolean shouldHandle = isGetter && propertyMissingGet != null;
             if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet 
!= null;
-            if (shouldHandle && iie.getCause() instanceof 
MissingPropertyException) {
-                throw (MissingPropertyException) iie.getCause();
+            if (shouldHandle && iie.getCause() instanceof 
MissingPropertyException mpe) {
+                throw mpe;
             }
             throw iie;
         }
diff --git 
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java 
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 4c7ade06d9..71df5ddfdb 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -18,8 +18,6 @@
  */
 package org.codehaus.groovy.classgen;
 
-import groovy.lang.MissingMethodException;
-import groovy.lang.MissingPropertyException;
 import groovy.transform.CompileStatic;
 import groovy.transform.stc.POJO;
 import org.codehaus.groovy.ast.ClassHelper;
@@ -29,11 +27,13 @@ import org.codehaus.groovy.ast.FieldNode;
 import org.codehaus.groovy.ast.InnerClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.TupleExpression;
 import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.ast.stmt.TryCatchStatement;
 import org.codehaus.groovy.classgen.asm.BytecodeHelper;
 import org.codehaus.groovy.control.CompilationUnit;
@@ -56,17 +56,22 @@ import static 
org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callSuperX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.catchS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorThisX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.notX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
@@ -348,25 +353,56 @@ public class InnerClassCompletionVisitor extends 
InnerClassVisitorHelper {
             final ClassNode returnType, final Parameter[] parameters, final 
BiConsumer<BlockStatement, Parameter[]> consumer) {
         MethodNode method = innerClass.getDeclaredMethod(methodName, 
parameters);
         if (method == null) {
+            ClassNode mme = 
ClassHelper.make(groovy.lang.MissingMethodException.class);
+            ClassNode mpe = 
ClassHelper.make(groovy.lang.MissingPropertyException.class);
+
+            // try {
+            //   <consumer dispatch>
+            // } catch (MissingMethodException notFound) {
+            //   throw new MissingMethodException(notFound.method, this, 
notFound.arguments)
+            // }
             Parameter catchParam = param(OBJECT_TYPE, "notFound"); // dummy 
type
             ClassNode exceptionT;
             Expression newException;
             Expression selfType = varX("this");
             if ((modifiers & ACC_STATIC) == 0) selfType = callX(selfType, 
"getClass");
             if (methodName.endsWith("methodMissing")) {
-                exceptionT = ClassHelper.make(MissingMethodException.class);
+                exceptionT = mme;
                 newException = ctorX(exceptionT, 
args(propX(varX(catchParam),"method"), selfType, 
propX(varX(catchParam),"arguments")));
             } else {
-                exceptionT = ClassHelper.make(MissingPropertyException.class);
+                exceptionT = mpe;
                 newException = ctorX(exceptionT, args(propX(varX(catchParam), 
"property"), selfType, propX(varX(catchParam), "cause")));
             }
-
             catchParam.setType(exceptionT);
             catchParam.setOriginType(exceptionT);
             BlockStatement handleMissing = block();
             consumer.accept(handleMissing, parameters);
-            TryCatchStatement methodBody = tryCatchS(handleMissing);
-            methodBody.addCatch(catchS(catchParam, throwS(newException)));
+            TryCatchStatement tryCatch = tryCatchS(handleMissing);
+            tryCatch.addCatch(catchS(catchParam, throwS(newException)));
+
+            Statement methodBody = tryCatch;
+            if ((modifiers & ACC_STATIC) == 0) { // GROOVY-11823
+                // if (!"methodMissing".equals(name))
+                Expression noRecursion = notX(callX(constX(methodName), 
"equals", varX(parameters[0])));
+                // try {
+                //   return super.methodMissing(name,args)
+                // } catch (MissingMethodException ignore) {
+                // } catch (MissingPropertyException ignore) {
+                // }
+                BlockStatement trySuperClass = block();
+                if (!ClassHelper.isPrimitiveVoid(returnType)) {
+                    trySuperClass.addStatement(returnS(callSuperX(methodName, 
args(parameters))));
+                } else {
+                    trySuperClass.addStatement(stmt(callSuperX(methodName, 
args(parameters))));
+                    
trySuperClass.addStatement(returnS(ConstantExpression.EMPTY_EXPRESSION));
+                }
+                tryCatch = tryCatchS(trySuperClass);
+                tryCatch.addCatch(catchS(param(mme, "ignore"), block()));
+                if (methodName.equals("propertyMissing"))
+                  tryCatch.addCatch(catchS(param(mpe, "ignore"), block()));
+
+                methodBody = block(ifS(noRecursion, tryCatch), methodBody);
+            }
             innerClass.addSyntheticMethod(methodName, modifiers, returnType, 
parameters, ClassNode.EMPTY_ARRAY, methodBody);
 
             // if there is a user-defined method, add compiler error and 
continue
diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy 
b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
index a4dfd08bc7..5d58874aab 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -2228,6 +2228,7 @@ final class InnerClassTest {
         def err = shouldFail """
             class Upper {
                 $returnType propertyMissing(String name, Object value) {
+                    throw new MissingPropertyException(name, getClass())
                 }
             }
             class Outer {
@@ -2239,6 +2240,25 @@ final class InnerClassTest {
         assert err =~ /No such property: missing for class: Outer.Inner/
     }
 
+    // GROOVY-11823
+    @Test
+    void testNestedPropertyHandling5() {
+        assertScript '''
+            class Upper {
+                Object propertyMissing(String name) {
+                    if (name == 'fizz') return 'buzz'
+                    throw new MissingPropertyException(name, getClass())
+                }
+            }
+            class Outer {
+                static class Inner extends Upper {
+                }
+            }
+            def inner = new Outer.Inner()
+            assert inner.fizz == 'buzz'
+        '''
+    }
+
     // GROOVY-7312
     @Test
     void testInnerClassOfInterfaceIsStatic() {

Reply via email to