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 969ca9c75ead9d59edc6074bb6e474ba76fd426e
Author: Eric Milles <[email protected]>
AuthorDate: Sat Jan 24 09:42:01 2026 -0600

    GROOVY-11823: try `super.propertyMissing(name)` before outer class
---
 .../classgen/InnerClassCompletionVisitor.java      | 39 ++++++++++++++++++++--
 .../groovy/gls/innerClass/InnerClassTest.groovy    | 20 +++++++++++
 2 files changed, 56 insertions(+), 3 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java 
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index fb70379836..592984b983 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -27,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;
@@ -54,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;
@@ -341,6 +348,9 @@ 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) {
@@ -352,10 +362,10 @@ public class InnerClassCompletionVisitor extends 
InnerClassVisitorHelper {
             Expression selfType = varX("this");
             if ((modifiers & ACC_STATIC) == 0) selfType = callX(selfType, 
"getClass");
             if (methodName.endsWith("methodMissing")) {
-                exceptionT = 
ClassHelper.make(groovy.lang.MissingMethodException.class);
+                exceptionT = mme;
                 newException = ctorX(exceptionT, 
args(propX(varX(catchParam),"method"), selfType, 
propX(varX(catchParam),"arguments")));
             } else {
-                exceptionT = 
ClassHelper.make(groovy.lang.MissingPropertyException.class);
+                exceptionT = mpe;
                 newException = ctorX(exceptionT, args(propX(varX(catchParam), 
"property"), selfType, propX(varX(catchParam), "cause")));
             }
             catchParam.setType(exceptionT);
@@ -365,7 +375,30 @@ public class InnerClassCompletionVisitor extends 
InnerClassVisitorHelper {
             TryCatchStatement tryCatch = tryCatchS(handleMissing);
             tryCatch.addCatch(catchS(catchParam, throwS(newException)));
 
-            innerClass.addSyntheticMethod(methodName, modifiers, returnType, 
parameters, ClassNode.EMPTY_ARRAY, tryCatch);
+            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
         } else if (isStatic(innerClass) && (method.getModifiers() & 
ACC_SYNTHETIC) == 0) {
diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy 
b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
index 2aac220e3f..d5c9c14d34 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -2242,6 +2242,7 @@ final class InnerClassTest {
         def err = shouldFail """
             class Upper {
                 $returnType propertyMissing(String name, Object value) {
+                    throw new MissingPropertyException(name, getClass())
                 }
             }
             class Outer {
@@ -2253,6 +2254,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