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/02/22 15:19:41 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-9289: Delegate: check excludes/includes names against class names

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 861ee93  GROOVY-9289: Delegate: check excludes/includes names against class names
861ee93 is described below

commit 861ee939e491520fcf10f9987862444e443279cb
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Feb 22 09:17:50 2022 -0600

    GROOVY-9289: Delegate: check excludes/includes names against class names
    
    2_5_X backport
---
 .../transform/DelegateASTTransformation.java       | 36 ++++++++++++++++++++++
 .../groovy/transform/DelegateTransformTest.groovy  | 20 ++++++++++++
 2 files changed, 56 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
index ab40e67..461e76f 100644
--- a/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -34,6 +34,7 @@ import org.codehaus.groovy.ast.PropertyNode;
 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.tools.BeanUtils;
 import org.codehaus.groovy.ast.tools.GenericsUtils;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.SourceUnit;
@@ -146,6 +147,9 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
             delegate.includeTypes = getMemberClassList(node, MEMBER_INCLUDE_TYPES);
             checkIncludeExcludeUndefinedAware(node, delegate.excludes, delegate.includes,
                                               delegate.excludeTypes, delegate.includeTypes, MY_TYPE_NAME);
+            // GROOVY-9289: check excludes/includes names against the delegate's property and method names
+            if (!checkPropertyOrMethodList(delegate.type, delegate.excludes, "excludes", node, MY_TYPE_NAME)) return;
+            if (!checkPropertyOrMethodList(delegate.type, delegate.includes, "includes", node, MY_TYPE_NAME)) return;
 
             final List<MethodNode> ownerMethods = getAllMethods(delegate.owner);
             final List<MethodNode> delegateMethods = filterMethods(collectMethods(delegate.type), delegate, allNames, includeDeprecated);
@@ -270,6 +274,38 @@ next_signature: for (int i = 1; i <= n; i += 1) { // from Verifier#addDefaultPar
         return methods;
     }
 
+    private boolean checkPropertyOrMethodList(final ClassNode cNode, final List<String> propertyNameList, final String listName, final AnnotationNode anno, final String typeName) {
+        if (propertyNameList == null || propertyNameList.isEmpty()) {
+            return true;
+        }
+        final Set<String> pNames = new HashSet<>();
+        final Set<String> mNames = new HashSet<>();
+        for (PropertyNode pNode : BeanUtils.getAllProperties(cNode, false, false, false)) {
+            String name = pNode.getField().getName();
+            String suffix = capitalize(name);
+            pNames.add(name);
+            // add getter/setters since Groovy compiler hasn't added property accessors yet
+            if ((pNode.getModifiers() & ACC_FINAL) == 0) {
+                mNames.add("set" + suffix);
+            }
+            mNames.add("get" + suffix);
+            if (pNode.getOriginType().equals(ClassHelper.boolean_TYPE)) {
+                mNames.add("is" + suffix);
+            }
+        }
+        for (MethodNode mNode : cNode.getAllDeclaredMethods()) {
+            mNames.add(mNode.getName());
+        }
+        boolean result = true;
+        for (String name : propertyNameList) {
+            if (!pNames.contains(name) && !mNames.contains(name)) {
+                addError("Error during " + typeName + " processing: '" + listName + "' property or method '" + name + "' does not exist.", anno);
+                result = false;
+            }
+        }
+        return result;
+    }
+
     private static void addSetterIfNeeded(final DelegateDescription delegate, final PropertyNode prop, final String name, final boolean allNames) {
         String setterName = "set" + capitalize(name);
         if ((prop.getModifiers() & ACC_FINAL) == 0
diff --git a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
index 3b7f3ab..82ffea5 100644
--- a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
@@ -859,6 +859,26 @@ final class DelegateTransformTest {
         '''
     }
 
+    @Test // GROOVY-9289
+    void testExcludesWithInvalidPropertyNameResultsInError() {
+        def err = shouldFail '''
+            class WMap {
+                String name
+                @Delegate(excludes = "name")
+                Map<String, String> data
+
+                WMap(String name, Map<String, String> data) {
+                    this.name = name
+                    this.data = data
+                }
+            }
+
+            new WMap('example', [name: 'weird'])
+        '''
+
+        assert err.message.contains("Error during @Delegate processing: 'excludes' property or method 'name' does not exist.")
+    }
+
     @Test // GROOVY-8825
     void testDelegateToPrecompiledGroovyGeneratedMethod() {
         assertScript '''