diff options
author | Roel Spilker <Roels@topdesk.com> | 2019-07-08 22:00:03 +0200 |
---|---|---|
committer | Roel Spilker <Roels@topdesk.com> | 2019-07-08 23:17:38 +0200 |
commit | 2ccd3f5f5d089f5525f6b219df35a1200596f0a8 (patch) | |
tree | 76f11afe3648b22520a65a115058dd7fd4fe61fe | |
parent | 11065b564f3fc1cee2c540a33b7ed1b3774816e2 (diff) | |
download | lombok-2ccd3f5f5d089f5525f6b219df35a1200596f0a8.tar.gz lombok-2ccd3f5f5d089f5525f6b219df35a1200596f0a8.tar.bz2 lombok-2ccd3f5f5d089f5525f6b219df35a1200596f0a8.zip |
Fixes #2165: Generated equals fails on annotated array type
4 files changed, 106 insertions, 6 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown index e9564eb0..f7c62c2c 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -5,6 +5,7 @@ Lombok Changelog * ENHANCEMENT: Thanks to Mark Haynes, the `staticConstructor` will now also be generated if a (private) constructor already exists. [Issue #2100](https://github.com/rzwitserloot/lombok/issues/2100) * ENHANCEMENT: `val` is now capable of decoding the type of convoluted expressions (particularly if the right hand side involves lambdas and conditional (ternary) expressions). [Pull Request #2109](https://github.com/rzwitserloot/lombok/pull/2109) with thanks to Alexander Bulgakov. * BUGFIX: Delombok would turn something like `List<byte[]>...` in a method parameter to `List<byte...>...` [Issue #2140](https://github.com/rzwitserloot/lombok/issues/2140) +* BUGFIX: Javac would generate the wrong equals and hashCode if a type-use annotation was put on an array type field [Issue #2165](https://github.com/rzwitserloot/lombok/issues/2165) ### v1.18.8 (May 7th, 2019) * FEATURE: You can now configure `@FieldNameConstants` to `CONSTANT_CASE` the generated constants, using a `lombok.config` option. See the [FieldNameConstants documentation](https://projectlombok.org/features/experimental/FieldNameConstants). [Issue #2092](https://github.com/rzwitserloot/lombok/issues/2092). diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index cb12bd4e..e4d7fa7f 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -25,6 +25,7 @@ import static lombok.core.handlers.HandlerUtil.handleFlagUsage; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -234,7 +235,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas for (Included<JavacNode, EqualsAndHashCode.Include> member : members) { JavacNode memberNode = member.getNode(); - JCExpression fType = getFieldType(memberNode, fieldAccess); + JCExpression fType = unnotate(getFieldType(memberNode, fieldAccess)); boolean isMethod = memberNode.getKind() == Kind.METHOD; JCExpression fieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess); @@ -279,9 +280,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas break; } } else if (fType instanceof JCArrayTypeTree) { + JCArrayTypeTree array = (JCArrayTypeTree) fType; /* java.util.Arrays.deepHashCode(this.fieldName) //use just hashCode() for primitive arrays. */ - boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree; - boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree; + boolean multiDim = unnotate(array.elemtype) instanceof JCArrayTypeTree; + boolean primitiveArray = unnotate(array.elemtype) instanceof JCPrimitiveTypeTree; boolean useDeepHC = multiDim || !primitiveArray; JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepHC ? "deepHashCode" : "hashCode"); @@ -430,7 +432,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas JavacNode memberNode = member.getNode(); boolean isMethod = memberNode.getKind() == Kind.METHOD; - JCExpression fType = getFieldType(memberNode, fieldAccess); + JCExpression fType = unnotate(getFieldType(memberNode, fieldAccess)); JCExpression thisFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess); JCExpression otherFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode, maker.Ident(otherName)) : createFieldAccessor(maker, memberNode, fieldAccess, maker.Ident(otherName)); if (fType instanceof JCPrimitiveTypeTree) { @@ -450,9 +452,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas break; } } else if (fType instanceof JCArrayTypeTree) { + JCArrayTypeTree array = (JCArrayTypeTree) fType; /* if (!java.util.Arrays.deepEquals(this.fieldName, other.fieldName)) return false; //use equals for primitive arrays. */ - boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree; - boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree; + boolean multiDim = unnotate(array.elemtype) instanceof JCArrayTypeTree; + boolean primitiveArray = unnotate(array.elemtype) instanceof JCPrimitiveTypeTree; boolean useDeepEquals = multiDim || !primitiveArray; JCExpression eqMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals"); @@ -522,4 +525,30 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas public JCStatement returnBool(JavacTreeMaker maker, boolean bool) { return maker.Return(maker.Literal(CTC_BOOLEAN, bool ? 1 : 0)); } + + private boolean jcAnnotatedTypeInit; + private Class<?> jcAnnotatedTypeClass = null; + private Field jcAnnotatedTypeUnderlyingTypeField = null; + + private JCExpression unnotate(JCExpression type) { + if (!isJcAnnotatedType(type)) return type; + if (jcAnnotatedTypeUnderlyingTypeField == null) return type; + try { + return (JCExpression) jcAnnotatedTypeUnderlyingTypeField.get(type); + } catch (Exception ignore) {} + return type; + } + + private boolean isJcAnnotatedType(JCExpression o) { + if (o == null) return false; + if (!jcAnnotatedTypeInit) { + try { + jcAnnotatedTypeClass = Class.forName("com.sun.tools.javac.tree.JCTree$JCAnnotatedType", false, o.getClass().getClassLoader()); + jcAnnotatedTypeUnderlyingTypeField = jcAnnotatedTypeClass.getDeclaredField("underlyingType"); + } + catch (Exception ignore) {} + jcAnnotatedTypeInit = true; + } + return jcAnnotatedTypeClass == o.getClass(); + } } diff --git a/test/transform/resource/after-delombok/EqualsAndHashCodeAnnotated.java b/test/transform/resource/after-delombok/EqualsAndHashCodeAnnotated.java new file mode 100644 index 00000000..64b6f4d3 --- /dev/null +++ b/test/transform/resource/after-delombok/EqualsAndHashCodeAnnotated.java @@ -0,0 +1,51 @@ +import java.lang.annotation.*; + +class EqualsAndHashCodeAnnotated { + @Annotated + int primitive; + @Annotated + Object object; + int @Annotated [] primitiveValues; + int @Annotated [] @Annotated [] morePrimitiveValues; + Integer @Annotated [] objectValues; + Integer @Annotated [] @Annotated [] moreObjectValues; + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.SOURCE) + @interface Annotated { + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public boolean equals(final java.lang.Object o) { + if (o == this) return true; + if (!(o instanceof EqualsAndHashCodeAnnotated)) return false; + final EqualsAndHashCodeAnnotated other = (EqualsAndHashCodeAnnotated) o; + if (!other.canEqual((java.lang.Object) this)) return false; + if (this.primitive != other.primitive) return false; + final java.lang.Object this$object = this.object; + final java.lang.Object other$object = other.object; + if (this$object == null ? other$object != null : !this$object.equals(other$object)) return false; + if (!java.util.Arrays.equals(this.primitiveValues, other.primitiveValues)) return false; + if (!java.util.Arrays.deepEquals(this.morePrimitiveValues, other.morePrimitiveValues)) return false; + if (!java.util.Arrays.deepEquals(this.objectValues, other.objectValues)) return false; + if (!java.util.Arrays.deepEquals(this.moreObjectValues, other.moreObjectValues)) return false; + return true; + } + @java.lang.SuppressWarnings("all") + protected boolean canEqual(final java.lang.Object other) { + return other instanceof EqualsAndHashCodeAnnotated; + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public int hashCode() { + final int PRIME = 59; + int result = 1; + result = result * PRIME + this.primitive; + final java.lang.Object $object = this.object; + result = result * PRIME + ($object == null ? 43 : $object.hashCode()); + result = result * PRIME + java.util.Arrays.hashCode(this.primitiveValues); + result = result * PRIME + java.util.Arrays.deepHashCode(this.morePrimitiveValues); + result = result * PRIME + java.util.Arrays.deepHashCode(this.objectValues); + result = result * PRIME + java.util.Arrays.deepHashCode(this.moreObjectValues); + return result; + } +}
\ No newline at end of file diff --git a/test/transform/resource/before/EqualsAndHashCodeAnnotated.java b/test/transform/resource/before/EqualsAndHashCodeAnnotated.java new file mode 100644 index 00000000..d672b982 --- /dev/null +++ b/test/transform/resource/before/EqualsAndHashCodeAnnotated.java @@ -0,0 +1,19 @@ +//version 8 +import java.lang.annotation.*; + +@lombok.EqualsAndHashCode +class EqualsAndHashCodeAnnotated { + @Annotated int primitive; + @Annotated Object object; + + int @Annotated [] primitiveValues; + int @Annotated [] @Annotated [] morePrimitiveValues; + + Integer @Annotated [] objectValues; + Integer @Annotated [] @Annotated [] moreObjectValues; + + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.SOURCE) + @interface Annotated { + } +} |