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 2021/12/14 03:44:15 UTC

[groovy] branch master updated: GROOVY-8050: STC: no outer class method or property access from outside

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 e71a44a  GROOVY-8050: STC: no outer class method or property access from outside
e71a44a is described below

commit e71a44a04eda3a0b27de7f54b2176c1e3fdafad1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Dec 13 21:44:02 2021 -0600

    GROOVY-8050: STC: no outer class method or property access from outside
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 68 ++++++++++------------
 .../stc/FieldsAndPropertiesSTCTest.groovy          | 50 +++++++++++++++-
 2 files changed, 77 insertions(+), 41 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index b93193e..04e3ee6 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -4748,13 +4748,14 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     protected List<MethodNode> findMethod(ClassNode receiver, final String name, final ClassNode... args) {
         if (isPrimitiveType(receiver)) receiver = getWrapper(receiver);
+
         List<MethodNode> methods;
-        if (!receiver.isInterface() && "<init>".equals(name)) {
+        if ("<init>".equals(name) && !receiver.isInterface()) {
             methods = addGeneratedMethods(receiver, new ArrayList<>(receiver.getDeclaredConstructors()));
             if (methods.isEmpty()) {
                 MethodNode node = new ConstructorNode(Opcodes.ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, GENERATED_EMPTY_STATEMENT);
                 node.setDeclaringClass(receiver);
-                methods = Collections.singletonList(node);
+                methods.add(node);
                 if (receiver.isArray()) {
                     // No need to check the arguments against an array constructor: it just needs to exist. The array is
                     // created through coercion or by specifying its dimension(s), anyway, and would not match an
@@ -4764,25 +4765,21 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
         } else {
             methods = findMethodsWithGenerated(receiver, name);
-            if (receiver.isInterface()) {
-                if ("call".equals(name) && isFunctionalInterface(receiver)) {
-                    MethodNode sam = findSAM(receiver);
+            if ("call".equals(name) && receiver.isInterface()) {
+                MethodNode sam = findSAM(receiver);
+                if (sam != null) {
                     MethodNode callMethod = new MethodNode("call", sam.getModifiers(), sam.getReturnType(), sam.getParameters(), sam.getExceptions(), sam.getCode());
                     callMethod.setDeclaringClass(sam.getDeclaringClass());
                     callMethod.setSourcePosition(sam);
                     methods.add(callMethod);
                 }
             }
-            // TODO: investigate the trait exclusion a bit further, needed otherwise
-            // CallMethodOfTraitInsideClosureAndClosureParamTypeInference fails saying
-            // not static method can't be called from a static context
-            if (typeCheckingContext.getEnclosingClosure() == null || (receiver.getOuterClass() != null && !receiver.getName().endsWith("$Trait$Helper"))) {
-                // not in a closure or within an inner class
-                ClassNode parent = receiver;
-                while (parent.getOuterClass() != null && !parent.isStaticClass()) {
-                    parent = parent.getOuterClass();
-                    methods.addAll(findMethodsWithGenerated(parent, name));
-                }
+            if (!receiver.isStaticClass() && receiver.getOuterClass() != null
+                    && !receiver.getName().endsWith("$Trait$Helper") // GROOVY-7242
+                    && typeCheckingContext.getEnclosingClassNodes().contains(receiver)) {
+                ClassNode outer = receiver.getOuterClass();
+                do { methods.addAll(findMethodsWithGenerated(outer, name));
+                } while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
             }
             if (methods.isEmpty()) {
                 addArrayMethods(methods, receiver, name, args);
@@ -4795,33 +4792,28 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
                 PropertyNode property = null;
                 if (pname != null) {
-                    // we don't use property exists there because findMethod is called on super clases recursively
-                    ClassNode curNode = receiver;
-                    while (property == null && curNode != null) {
-                        property = curNode.getProperty(pname);
-                        ClassNode svCur = curNode;
-                        while (property == null && svCur.getOuterClass() != null && !svCur.isStaticClass()) {
-                            svCur = svCur.getOuterClass();
-                            property = svCur.getProperty(pname);
-                            if (property != null) {
-                                receiver = svCur;
-                                break;
-                            }
+                    outer_upper: // can't use existsProperty because it calls findMethod
+                    for (ClassNode cn = receiver; cn != null; cn = cn.getSuperClass()) {
+                        property = cn.getProperty(pname);
+                        if (property != null) break outer_upper;
+                        if (!cn.isStaticClass() && cn.getOuterClass() != null
+                                && typeCheckingContext.getEnclosingClassNodes().contains(cn)) {
+                            ClassNode outer = cn.getOuterClass();
+                            do {
+                                property = outer.getProperty(pname);
+                                if (property != null) break outer_upper;
+                            } while (!outer.isStaticClass() && (outer = outer.getOuterClass()) != null);
                         }
-                        curNode = curNode.getSuperClass();
                     }
-                } else {
-                    // look for a property with the getterName set since it may not match above
-                    ClassNode curNode = receiver;
-                    while (property == null && curNode != null && !curNode.isStaticClass()) {
-                        for (PropertyNode p : curNode.getProperties()) {
-                            if (name.equals(p.getGetterName())) {
-                                property = p;
-                                receiver = curNode;
-                                break;
+                } else { // look for property via getGetterName() for non-canonical case
+                    out:
+                    for (ClassNode cn = receiver; cn != null; cn = cn.getSuperClass()) {
+                        for (PropertyNode pn : cn.getProperties()) {
+                            if (name.equals(pn.getGetterName())) {
+                                property = pn;
+                                break out;
                             }
                         }
-                        curNode = curNode.getSuperClass();
                     }
                 }
                 if (property != null) {
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index a7ccf1e..393d67d 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -646,8 +646,52 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    // GROOVY-9598
     void testOuterPropertyAccess2() {
+        assertScript '''
+            class Outer {
+                class Inner {
+                    def m() {
+                        getP()
+                    }
+                }
+                def p = 1
+            }
+            def i = new Outer.Inner(new Outer())
+            def x = i.m()
+            assert x == 1
+        '''
+    }
+
+    // GROOVY-8050
+    void testOuterPropertyAccess3() {
+        shouldFailWithMessages '''
+            class Outer {
+                class Inner {
+                }
+                def p = 1
+            }
+            def i = new Outer.Inner(new Outer())
+            def x = i.p
+        ''',
+        'No such property: p for class: Outer$Inner'
+    }
+
+    // GROOVY-8050
+    void testOuterPropertyAccess4() {
+        shouldFailWithMessages '''
+            class Outer {
+                class Inner {
+                }
+                def p = 1
+            }
+            def i = new Outer.Inner(new Outer())
+            def x = i.getP()
+        ''',
+        'Cannot find matching method Outer$Inner#getP()'
+    }
+
+    // GROOVY-9598
+    void testOuterPropertyAccess5() {
         shouldFailWithMessages '''
             class Outer {
                 static class Inner {
@@ -663,7 +707,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         'The variable [p] is undeclared.'
     }
 
-    void testOuterPropertyAccess3() {
+    void testOuterPropertyAccess6() {
         shouldFailWithMessages '''
             class Outer {
                 static class Inner {
@@ -680,7 +724,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-7024
-    void testOuterPropertyAccess4() {
+    void testOuterPropertyAccess7() {
         assertScript '''
             class Outer {
                 static Map props = [bar: 10, baz: 20]