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 2022/09/01 18:48:15 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-10741: STC: method pointer/reference to generated property method

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

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new e25af85727 GROOVY-10741: STC: method pointer/reference to generated property method
e25af85727 is described below

commit e25af85727b1bde560a7c97980460569ed6d99db
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Sep 1 11:31:09 2022 -0500

    GROOVY-10741: STC: method pointer/reference to generated property method
    
    2_5_X backport
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 46 ++++++++++++++++++----
 src/test/groovy/transform/stc/BugsSTCTest.groovy   | 19 +++++++++
 2 files changed, 58 insertions(+), 7 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 ec271d106b..c9a2929376 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1709,7 +1709,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private MethodNode findGetter(ClassNode current, String name, boolean searchOuterClasses) {
+    private static MethodNode findGetter(ClassNode current, String name, boolean searchOuterClasses) {
         MethodNode getterMethod = current.getGetterMethod(name);
         if (getterMethod == null && searchOuterClasses && current instanceof InnerClassNode) {
             return findGetter(current.getOuterClass(), name, true);
@@ -2541,7 +2541,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 ClassNode receiverType = wrapTypeIfNecessary(currentReceiver.getType());
 
                 candidates = findMethodsWithGenerated(receiverType, nameText);
-                if (isBeingCompiled(receiverType)) candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
+                // GROOVY-10741: check for reference to a property node's method
+                MethodNode generated = findPropertyMethod(receiverType, nameText);
+                if (generated != null) {
+                    boolean contains = false;
+                    for (MethodNode candidate : candidates) {
+                        if (candidate.getName().equals(generated.getName())) {
+                            contains = true;
+                            break;
+                        }
+                    }
+                    if (!contains) candidates.add(generated);
+                }
+                if (!receiverType.isInterface() && isBeingCompiled(receiverType))
+                       candidates.addAll(GROOVY_OBJECT_TYPE.getMethods(nameText));
                 candidates.addAll(findDGMMethodsForClassNode(getSourceUnit().getClassLoader(), receiverType, nameText));
                 candidates = filterMethodsByVisibility(candidates);
                 if (!candidates.isEmpty()) {
@@ -2585,6 +2598,26 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return inferredType;
     }
 
+    private static MethodNode findPropertyMethod(final ClassNode type, final String name) {
+        for (ClassNode cn = type; cn != null; cn = cn.getSuperClass()) {
+            for (PropertyNode pn : cn.getProperties()) {
+                String properName = MetaClassHelper.capitalize(pn.getName());
+                String getterName = "get" + properName; // Groovy 4+ moves getter name to PropertyNode#getGetterNameOrDefault
+                if (boolean_TYPE.equals(pn.getType()) && findGetter(cn, getterName, false) == null) getterName = "is" + properName;
+                if (name.equals(getterName)) {
+                    MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC | (pn.isStatic() ? Opcodes.ACC_STATIC : 0), pn.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
+                    node.setDeclaringClass(pn.getDeclaringClass());
+                    return node;
+                } else if (name.equals("set" + properName) && !Modifier.isFinal(pn.getModifiers())) {
+                    MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC | (pn.isStatic() ? Opcodes.ACC_STATIC : 0), VOID_TYPE, new Parameter[]{new Parameter(pn.getType(), pn.getName())}, ClassNode.EMPTY_ARRAY, null);
+                    node.setDeclaringClass(pn.getDeclaringClass());
+                    return node;
+                }
+            }
+        }
+        return null;
+    }
+
     protected DelegationMetadata getDelegationMetadata(final ClosureExpression expression) {
         return (DelegationMetadata) expression.getNodeMetaData(StaticTypesMarker.DELEGATION_METADATA);
     }
@@ -3703,8 +3736,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      * @param args     the argument classes
      */
     private static void addArrayMethods(final List<MethodNode> methods, final ClassNode receiver, final String name, final ClassNode[] args) {
-        if (args.length != 1) return;
         if (!receiver.isArray()) return;
+        if (args == null || args.length != 1) return;
         if (!isIntCategory(getUnwrapper(args[0]))) return;
         if ("getAt".equals(name)) {
             MethodNode node = new MethodNode(name, Opcodes.ACC_PUBLIC, receiver.getComponentType(), new Parameter[]{new Parameter(args[0], "arg")}, null, null);
@@ -4597,11 +4630,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         if (receiver.isInterface()) {
             methods.addAll(OBJECT_TYPE.getMethods(name));
         }
-
-        if (methods.isEmpty() || receiver.isResolved()) {
-            return methods;
+        if (!receiver.isResolved() && !methods.isEmpty()) {
+            methods = addGeneratedMethods(receiver, methods);
         }
-        return addGeneratedMethods(receiver, methods);
+        return methods;
     }
 
     private static List<MethodNode> addGeneratedMethods(final ClassNode receiver, final List<MethodNode> methods) {
diff --git a/src/test/groovy/transform/stc/BugsSTCTest.groovy b/src/test/groovy/transform/stc/BugsSTCTest.groovy
index d4357d24bb..395894d97d 100644
--- a/src/test/groovy/transform/stc/BugsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/BugsSTCTest.groovy
@@ -918,6 +918,25 @@ Printer
         '''
     }
 
+    // GROOVY-10741
+    void testMethodPointerPropertyReference() {
+        assertScript '''
+            class C { def foo }
+            def pogo = new C(foo:'bar')
+            assert pogo.foo == 'bar'
+
+            def proc = pogo.&setFoo
+            proc.call('baz')
+            assert pogo.foo == 'baz'
+        '''
+        shouldFailWithMessages '''
+            class C { final foo }
+            def pogo = new C(foo:'bar')
+
+            def proc = pogo.&setFoo
+        ''', 'Cannot find matching method C#setFoo'
+    }
+
     // GROOVY-9463
     void testMethodPointerUnknownReference() {
         shouldFailWithMessages '''