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 17:04:44 UTC

[groovy] branch master 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 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 a228fae2d1 GROOVY-10741: STC: method pointer/reference to generated property method
a228fae2d1 is described below

commit a228fae2d16c22a59fd29b8d4b205d9be059dadc
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
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 31 +++++++++++++++++++---
 src/test/groovy/transform/stc/BugsSTCTest.groovy   | 26 ++++++++++++++++++
 2 files changed, 53 insertions(+), 4 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 d791fddc1c..b438d7614f 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2450,6 +2450,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 receiverType = wrapTypeIfNecessary(currentReceiver.getType());
 
                 candidates = findMethodsWithGenerated(receiverType, nameText);
+                // GROOVY-10741: check for reference to a property node's method
+                MethodNode generated = findPropertyMethod(receiverType, nameText);
+                if (generated != null && candidates.stream().noneMatch(mn -> mn.getName().equals(generated.getName()))){
+                    candidates.add(generated);
+                }
                 candidates.addAll(findDGMMethodsForClassNode(getSourceUnit().getClassLoader(), receiverType, nameText));
                 candidates = filterMethodsByVisibility(candidates, typeCheckingContext.getEnclosingClassNode());
 
@@ -2487,6 +2492,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return makeClassSafe0(CLOSURE_TYPE, wrapTypeIfNecessary(returnType).asGenericsType());
     }
 
+    private static MethodNode findPropertyMethod(final ClassNode type, final String name) {
+        for (ClassNode cn = type; cn != null; cn = cn.getSuperClass()) {
+            for (PropertyNode pn : cn.getProperties()) {
+                if (name.equals(pn.getGetterNameOrDefault())) {
+                    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(pn.getSetterNameOrDefault()) && !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 expression.getNodeMetaData(DELEGATION_METADATA);
     }
@@ -3680,9 +3702,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private LambdaExpression constructLambdaExpressionForMethodReference(final ClassNode functionalInterfaceType) {
-        Parameter[] parameters = findSAM(functionalInterfaceType).getParameters().clone();
-        for (int i = 0, n = parameters.length; i < n; i += 1) {
-            parameters[i] = new Parameter(dynamicType(), "p" + System.nanoTime());
+        int nParameters = findSAM(functionalInterfaceType).getParameters().length;
+        Parameter[] parameters = new Parameter[nParameters];
+        for (int i = 0; i < nParameters; i += 1) {
+            parameters[i] = new Parameter(dynamicType(), "p" + i);
         }
 
         return new LambdaExpression(parameters, EmptyStatement.INSTANCE);
@@ -3726,8 +3749,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);
diff --git a/src/test/groovy/transform/stc/BugsSTCTest.groovy b/src/test/groovy/transform/stc/BugsSTCTest.groovy
index 99d718cfb4..e3738a6d2c 100644
--- a/src/test/groovy/transform/stc/BugsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/BugsSTCTest.groovy
@@ -945,6 +945,32 @@ Printer
         '''
     }
 
+    // GROOVY-10741
+    void testMethodPointerPropertyReference() {
+        assertScript '''
+            class C { def foo }
+            List<C> list = [new C(foo:'bar'), new C(foo:'baz')]
+            assert list.collect(C.&getFoo) == ['bar', 'baz'] // Cannot find matching method C#getFoo
+        '''
+        assertScript '''
+            record R(def foo) { }
+            List<R> list = [new R('bar'), new R('baz')]
+            assert list.collect(R.&foo) == ['bar', 'baz'] // Cannot find matching method R#foo
+        '''
+        assertScript '''
+            class C { def foo }
+            def proc = C.&setFoo
+            def pogo = new C(foo:'bar')
+            assert pogo.foo == 'bar'
+            proc.call(pogo, 'baz')
+            assert pogo.foo == 'baz'
+        '''
+        shouldFailWithMessages '''
+            class C { final foo }
+            def set = C.&setFoo
+        ''', 'Cannot find matching method C#setFoo'
+    }
+
     // GROOVY-9463
     void testMethodPointerUnknownReference() {
         shouldFailWithMessages '''