Repository: incubator-groovy Updated Branches: refs/heads/master 3844f2835 -> 14a3a6700
Fixes and tests for getting AST transforms relating to bean properties to validate their includes/excludes lists. Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/1a320e92 Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/1a320e92 Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/1a320e92 Branch: refs/heads/master Commit: 1a320e92682a779e06bffcb77b24a0dbc3e24771 Parents: 3844f28 Author: John Hurst <john.b.hu...@gmail.com> Authored: Sun Dec 28 15:17:50 2014 +1300 Committer: Paul King <pa...@asert.com.au> Committed: Fri May 22 20:17:30 2015 +1000 ---------------------------------------------------------------------- .../groovy/transform/ExternalizeMethods.java | 2 +- .../transform/AbstractASTTransformation.java | 27 ++++++++++++ .../transform/AutoCloneASTTransformation.java | 1 + .../EqualsAndHashCodeASTTransformation.java | 2 + .../transform/ImmutableASTTransformation.java | 1 + .../transform/CanonicalTransformTest.groovy | 46 ++++++++++++++++++++ .../transform/ImmutableTransformTest.groovy | 15 +++++++ 7 files changed, 93 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/1a320e92/src/main/groovy/transform/ExternalizeMethods.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/transform/ExternalizeMethods.java b/src/main/groovy/transform/ExternalizeMethods.java index 01d5073..5ef37a0 100644 --- a/src/main/groovy/transform/ExternalizeMethods.java +++ b/src/main/groovy/transform/ExternalizeMethods.java @@ -33,7 +33,7 @@ import java.lang.annotation.Target; * The {@code writeExternal()} method writes each property (and optionally field) of the class * while the {@code readExternal()} method will read each one back in the same order. * Properties or fields marked as {@code transient} are ignored. - * This annotation is typically used in conjunction with the {@code @ExternalizeMethods} annotation but + * This annotation is typically used in conjunction with the {@code @ExternalizeVerifier} annotation but * most usually not directly but rather via {@code @AutoExternalizable} which is a shortcut for both annotations. * <p> * Example usage: http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/1a320e92/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java b/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java index 36e1e06..c1a97ec 100644 --- a/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java +++ b/src/main/org/codehaus/groovy/transform/AbstractASTTransformation.java @@ -47,6 +47,8 @@ import java.util.List; import java.util.Map; import static groovy.transform.Undefined.isUndefined; +import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstanceNonPropertyFieldNames; +import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstancePropertyNames; public abstract class AbstractASTTransformation implements Opcodes, ASTTransformation { public static final ClassNode RETENTION_CLASSNODE = ClassHelper.makeWithoutCaching(Retention.class); @@ -269,6 +271,31 @@ public abstract class AbstractASTTransformation implements Opcodes, ASTTransform } } + protected boolean checkPropertyList(ClassNode cNode, List<String> propertyNameList, String listName, AnnotationNode anno, String typeName, boolean includeFields) { + if (propertyNameList == null || propertyNameList.isEmpty()) { + return true; + } + final List<String> pNames = getInstancePropertyNames(cNode); + boolean result = true; + if (includeFields) { + final List<String> fNames = getInstanceNonPropertyFieldNames(cNode); + for (String pName : propertyNameList) { + if (!pNames.contains(pName) && !fNames.contains(pName)) { + addError("Error during " + typeName + " processing: '" + listName + "' property or field '" + pName + "' does not exist.", anno); + result = false; + } + } + } else { + for (String pName : propertyNameList) { + if (!pNames.contains(pName)) { + addError("Error during " + typeName + " processing: '" + listName + "' property '" + pName + "' does not exist.", anno); + result = false; + } + } + } + return result; + } + /** * @deprecated use GenericsUtils#nonGeneric */ http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/1a320e92/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java b/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java index f32a1ee..ba9d877 100644 --- a/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java +++ b/src/main/org/codehaus/groovy/transform/AutoCloneASTTransformation.java @@ -84,6 +84,7 @@ public class AutoCloneASTTransformation extends AbstractASTTransformation { boolean includeFields = memberHasValue(anno, "includeFields", true); AutoCloneStyle style = getStyle(anno, "style"); List<String> excludes = getMemberList(anno, "excludes"); + if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return; List<FieldNode> list = getInstancePropertyFields(cNode); if (includeFields) { list.addAll(getInstanceNonPropertyFields(cNode)); http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/1a320e92/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java b/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java index d1a46b5..4857ebe 100644 --- a/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java +++ b/src/main/org/codehaus/groovy/transform/EqualsAndHashCodeASTTransformation.java @@ -74,6 +74,8 @@ public class EqualsAndHashCodeASTTransformation extends AbstractASTTransformatio List<String> excludes = getMemberList(anno, "excludes"); List<String> includes = getMemberList(anno, "includes"); if (!checkIncludeExclude(anno, excludes, includes, MY_TYPE_NAME)) return; + if (!checkPropertyList(cNode, includes, "includes", anno, MY_TYPE_NAME, includeFields)) return; + if (!checkPropertyList(cNode, excludes, "excludes", anno, MY_TYPE_NAME, includeFields)) return; createHashCode(cNode, cacheHashCode, includeFields, callSuper, excludes, includes); createEquals(cNode, includeFields, callSuper, useCanEqual, excludes, includes); } http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/1a320e92/src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java b/src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java index 6257192..d06e8a5 100644 --- a/src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java +++ b/src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java @@ -146,6 +146,7 @@ public class ImmutableASTTransformation extends AbstractASTTransformation { ClassNode cNode = (ClassNode) parent; String cName = cNode.getName(); if (!checkNotInterface(cNode, MY_TYPE_NAME)) return; + if (!checkPropertyList(cNode, knownImmutables, "knownImmutables", node, MY_TYPE_NAME, false)) return; makeClassFinal(cNode); final List<PropertyNode> pList = getInstanceProperties(cNode); http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/1a320e92/src/test/org/codehaus/groovy/transform/CanonicalTransformTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/org/codehaus/groovy/transform/CanonicalTransformTest.groovy b/src/test/org/codehaus/groovy/transform/CanonicalTransformTest.groovy index 944b103..04710c5 100644 --- a/src/test/org/codehaus/groovy/transform/CanonicalTransformTest.groovy +++ b/src/test/org/codehaus/groovy/transform/CanonicalTransformTest.groovy @@ -594,4 +594,50 @@ class CanonicalTransformTest extends GroovyShellTestCase { """ } + // GROOVY-7227 + void testIncludesAndExcludesTogetherResultsInError() { + def message = shouldFail { + evaluate(""" + import groovy.transform.Canonical + @Canonical(includes='surName', excludes='surName') + class Person { + String surName + } + new Person(surName: "Doe").toString() + """) + } + assert message.contains("Error during @Canonical processing: Only one of 'includes' and 'excludes' should be supplied not both.") + } + + // GROOVY-7227 + void testIncludesWithInvalidPropertyNameResultsInError() { + def message = shouldFail { + evaluate(""" + import groovy.transform.Canonical + @Canonical(includes='sirName') + class Person { + String surName + } + new Person(surName: "Doe").toString() + """) + } + assert message.contains("Error during @Canonical processing: 'includes' property 'sirName' does not exist.") + } + + // GROOVY-7227 + void testExcludesWithInvalidPropertyNameResultsInError() { + def message = shouldFail { + evaluate(""" + import groovy.transform.Canonical + @Canonical(excludes='sirName') + class Person { + String firstName + String surName + } + new Person(firstName: "John", surName: "Doe").toString() + """) + } + assert message.contains("Error during @Canonical processing: 'excludes' property 'sirName' does not exist.") + } + } http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/1a320e92/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy ---------------------------------------------------------------------- diff --git a/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy b/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy index 1d03e2b..3c19842 100644 --- a/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy +++ b/src/test/org/codehaus/groovy/transform/ImmutableTransformTest.groovy @@ -903,6 +903,21 @@ class ImmutableTransformTest extends GroovyShellTestCase { assert result.first == [ 'tim', 'tim' ] } + // GROOVY-7227 + void testKnownImmutablesWithInvalidPropertyNameResultsInError() { + def message = shouldFail { + evaluate """ + import groovy.transform.Immutable + @Immutable(knownImmutables=['sirName']) + class Person { + String surName + } + new Person(surName: "Doe") + """ + } + assert message.contains("Error during @Immutable processing: 'knownImmutables' property 'sirName' does not exist.") + } + // GROOVY-7162 void testImmutableWithSuperClass() { assertScript '''