aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java85
-rw-r--r--test/transform/resource/after-delombok/DataExtended.java8
-rw-r--r--test/transform/resource/after-delombok/DataIgnore.java10
-rw-r--r--test/transform/resource/after-delombok/DataOnLocalClass.java24
-rw-r--r--test/transform/resource/after-delombok/DataPlain.java100
-rw-r--r--test/transform/resource/after-delombok/DataWithGetter.java8
-rw-r--r--test/transform/resource/after-delombok/DataWithGetterNone.java8
-rw-r--r--test/transform/resource/after-ecj/DataPlain.java74
-rw-r--r--test/transform/resource/before/DataPlain.java14
9 files changed, 287 insertions, 44 deletions
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index ee049f1f..b2d64f05 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -167,9 +167,13 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
}
}
+ boolean needsCanEqual = false;
switch (methodExists("equals", typeNode)) {
case NOT_EXISTS:
- JCMethodDecl method = createEquals(typeNode, nodesForEquality, callSuper, useFieldsDirectly);
+ boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
+ needsCanEqual = !isFinal || !isDirectDescendantOfObject;
+
+ JCMethodDecl method = createEquals(typeNode, nodesForEquality, callSuper, useFieldsDirectly, needsCanEqual);
injectMethod(typeNode, method);
break;
case EXISTS_BY_LOMBOK:
@@ -182,6 +186,18 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
break;
}
+ if (needsCanEqual) {
+ switch (methodExists("canEqual", typeNode)) {
+ case NOT_EXISTS:
+ JCMethodDecl method = createCanEqual(typeNode);
+ injectMethod(typeNode, method);
+ break;
+ case EXISTS_BY_LOMBOK:
+ case EXISTS_BY_USER:
+ default:
+ break;
+ }
+ }
switch (methodExists("hashCode", typeNode)) {
case NOT_EXISTS:
JCMethodDecl method = createHashCode(typeNode, nodesForEquality, callSuper, useFieldsDirectly);
@@ -196,7 +212,6 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
}
break;
}
-
return true;
}
@@ -314,7 +329,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
return maker.TypeCast(maker.TypeIdent(TypeTags.INT), xorBits);
}
- private JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, boolean useFieldsDirectly) {
+ private JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, boolean useFieldsDirectly, boolean needsCanEqual) {
TreeMaker maker = typeNode.getTreeMaker();
JCClassDecl type = (JCClassDecl) typeNode.get();
@@ -335,27 +350,9 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
maker.Ident(thisName)), returnBool(maker, true), null));
}
- /* if (o == null) return false; */ {
- statements = statements.append(maker.If(maker.Binary(JCTree.EQ, maker.Ident(oName),
- maker.Literal(TypeTags.BOT, null)), returnBool(maker, false), null));
- }
-
- /* if (o.getClass() != this.getClass()) return false; */ {
- Name getClass = typeNode.toName("getClass");
- List<JCExpression> exprNil = List.nil();
- JCExpression oGetClass = maker.Apply(exprNil, maker.Select(maker.Ident(oName), getClass), exprNil);
- JCExpression thisGetClass = maker.Apply(exprNil, maker.Select(maker.Ident(thisName), getClass), exprNil);
- statements = statements.append(
- maker.If(maker.Binary(JCTree.NE, oGetClass, thisGetClass), returnBool(maker, false), null));
- }
-
- /* if (!super.equals(o)) return false; */
- if (callSuper) {
- JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
- List.<JCExpression>of(maker.Ident(oName)));
- JCUnary superNotEqual = maker.Unary(JCTree.NOT, callToSuper);
- statements = statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
+ /* if (!(o instanceof MyType) return false; */ {
+ JCUnary notInstanceOf = maker.Unary(JCTree.NOT, maker.TypeTest(maker.Ident(oName), maker.Ident(type.name)));
+ statements = statements.append(maker.If(notInstanceOf, returnBool(maker, false), null));
}
/* MyType<?> other = (MyType<?>) o; */ {
@@ -379,6 +376,25 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
maker.VarDef(maker.Modifiers(Flags.FINAL), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
}
+ /* if (!other.canEqual(this)) return false; */ {
+ if (needsCanEqual) {
+ List<JCExpression> exprNil = List.nil();
+ JCExpression equalityCheck = maker.Apply(exprNil,
+ maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
+ List.<JCExpression>of(maker.Ident(thisName)));
+ statements = statements.append(maker.If(maker.Unary(JCTree.NOT, equalityCheck), returnBool(maker, false), null));
+ }
+ }
+
+ /* if (!super.equals(o)) return false; */
+ if (callSuper) {
+ JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
+ List.<JCExpression>of(maker.Ident(oName)));
+ JCUnary superNotEqual = maker.Unary(JCTree.NOT, callToSuper);
+ statements = statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
+ }
+
for (JavacNode fieldNode : fields) {
JCExpression fType = getFieldType(fieldNode, useFieldsDirectly);
JCExpression thisFieldAccessor = createFieldAccessor(maker, fieldNode, useFieldsDirectly);
@@ -428,6 +444,27 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
JCBlock body = maker.Block(0, statements);
return maker.MethodDef(mods, typeNode.toName("equals"), returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null);
}
+
+ private JCMethodDecl createCanEqual(JavacNode typeNode) {
+ /* public boolean canEquals(final java.lang.Object other) {
+ * return other instanceof MyType;
+ * }
+ */
+ TreeMaker maker = typeNode.getTreeMaker();
+ JCClassDecl type = (JCClassDecl) typeNode.get();
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.<JCAnnotation>nil());
+ JCExpression returnType = maker.TypeIdent(TypeTags.BOOLEAN);
+ Name canEqualName = typeNode.toName("canEqual");
+ JCExpression objectType = chainDots(maker, typeNode, "java", "lang", "Object");
+ Name otherName = typeNode.toName("other");
+ List<JCVariableDecl> params = List.of(maker.VarDef(maker.Modifiers(Flags.FINAL), otherName, objectType, null));
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(
+ maker.Return(maker.TypeTest(maker.Ident(otherName), maker.Ident(type.name)))));
+
+ return maker.MethodDef(mods, canEqualName, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null);
+ }
private JCStatement generateCompareFloatOrDouble(JCExpression thisDotField, JCExpression otherDotField,
TreeMaker maker, JavacNode node, boolean isDouble) {
diff --git a/test/transform/resource/after-delombok/DataExtended.java b/test/transform/resource/after-delombok/DataExtended.java
index 6f55bc2c..be1c3b74 100644
--- a/test/transform/resource/after-delombok/DataExtended.java
+++ b/test/transform/resource/after-delombok/DataExtended.java
@@ -15,12 +15,16 @@ class DataExtended {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof DataExtended)) return false;
final DataExtended other = (DataExtended)o;
+ if (!other.canEqual(this)) return false;
if (this.getX() != other.getX()) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof DataExtended;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
diff --git a/test/transform/resource/after-delombok/DataIgnore.java b/test/transform/resource/after-delombok/DataIgnore.java
index 7e81432d..9f2a7d79 100644
--- a/test/transform/resource/after-delombok/DataIgnore.java
+++ b/test/transform/resource/after-delombok/DataIgnore.java
@@ -14,12 +14,16 @@ class DataIgnore {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof DataIgnore)) return false;
final DataIgnore other = (DataIgnore)o;
+ if (!other.canEqual(this)) return false;
if (this.getX() != other.getX()) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof DataIgnore;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
@@ -33,4 +37,4 @@ class DataIgnore {
public java.lang.String toString() {
return "DataIgnore(x=" + this.getX() + ")";
}
-}
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/DataOnLocalClass.java b/test/transform/resource/after-delombok/DataOnLocalClass.java
index bb3f564d..02101a81 100644
--- a/test/transform/resource/after-delombok/DataOnLocalClass.java
+++ b/test/transform/resource/after-delombok/DataOnLocalClass.java
@@ -23,13 +23,17 @@ class DataOnLocalClass1 {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof Local)) return false;
final Local other = (Local)o;
+ if (!other.canEqual(this)) return false;
if (this.getX() != other.getX()) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof Local;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
@@ -73,12 +77,16 @@ class DataOnLocalClass2 {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof InnerLocal)) return false;
final InnerLocal other = (InnerLocal)o;
+ if (!other.canEqual(this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof InnerLocal;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
@@ -105,12 +113,16 @@ class DataOnLocalClass2 {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof Local)) return false;
final Local other = (Local)o;
+ if (!other.canEqual(this)) return false;
if (this.getX() != other.getX()) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof Local;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
diff --git a/test/transform/resource/after-delombok/DataPlain.java b/test/transform/resource/after-delombok/DataPlain.java
index 1e11a33d..ef86f9d7 100644
--- a/test/transform/resource/after-delombok/DataPlain.java
+++ b/test/transform/resource/after-delombok/DataPlain.java
@@ -22,13 +22,17 @@ class Data1 {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof Data1)) return false;
final Data1 other = (Data1)o;
+ if (!other.canEqual(this)) return false;
if (this.getX() != other.getX()) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof Data1;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
@@ -68,13 +72,17 @@ class Data2 {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof Data2)) return false;
final Data2 other = (Data2)o;
+ if (!other.canEqual(this)) return false;
if (this.getX() != other.getX()) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof Data2;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
@@ -90,3 +98,87 @@ class Data2 {
return "Data2(x=" + this.getX() + ", name=" + this.getName() + ")";
}
}
+final class Data3 {
+ final int x;
+ String name;
+ @java.beans.ConstructorProperties({"x"})
+ @java.lang.SuppressWarnings("all")
+ public Data3(final int x) {
+ this.x = x;
+ }
+ @java.lang.SuppressWarnings("all")
+ public int getX() {
+ return this.x;
+ }
+ @java.lang.SuppressWarnings("all")
+ public String getName() {
+ return this.name;
+ }
+ @java.lang.SuppressWarnings("all")
+ public void setName(final String name) {
+ this.name = name;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof Data3)) return false;
+ final Data3 other = (Data3)o;
+ if (this.getX() != other.getX()) return false;
+ if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
+ return true;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * PRIME + this.getX();
+ result = result * PRIME + (this.getName() == null ? 0 : this.getName().hashCode());
+ return result;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "Data3(x=" + this.getX() + ", name=" + this.getName() + ")";
+ }
+}
+final class Data4 extends java.util.Timer {
+ final int x;
+ Data4() {
+ super();
+ }
+ @java.lang.SuppressWarnings("all")
+ public int getX() {
+ return this.x;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "Data4(x=" + this.getX() + ")";
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof Data4)) return false;
+ final Data4 other = (Data4)o;
+ if (!other.canEqual(this)) return false;
+ if (!super.equals(o)) return false;
+ if (this.getX() != other.getX()) return false;
+ return true;
+ }
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof Data4;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * PRIME + super.hashCode();
+ result = result * PRIME + this.getX();
+ return result;
+ }
+}
diff --git a/test/transform/resource/after-delombok/DataWithGetter.java b/test/transform/resource/after-delombok/DataWithGetter.java
index 28b8dee0..1e181370 100644
--- a/test/transform/resource/after-delombok/DataWithGetter.java
+++ b/test/transform/resource/after-delombok/DataWithGetter.java
@@ -19,14 +19,18 @@ class DataWithGetter {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof DataWithGetter)) return false;
final DataWithGetter other = (DataWithGetter)o;
+ if (!other.canEqual(this)) return false;
if (this.getX() != other.getX()) return false;
if (this.getY() != other.getY()) return false;
if (this.getZ() == null ? other.getZ() != null : !this.getZ().equals(other.getZ())) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof DataWithGetter;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
diff --git a/test/transform/resource/after-delombok/DataWithGetterNone.java b/test/transform/resource/after-delombok/DataWithGetterNone.java
index 161c70f9..b1cab7cf 100644
--- a/test/transform/resource/after-delombok/DataWithGetterNone.java
+++ b/test/transform/resource/after-delombok/DataWithGetterNone.java
@@ -19,14 +19,18 @@ class DataWithGetterNone {
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
- if (o == null) return false;
- if (o.getClass() != this.getClass()) return false;
+ if (!(o instanceof DataWithGetterNone)) return false;
final DataWithGetterNone other = (DataWithGetterNone)o;
+ if (!other.canEqual(this)) return false;
if (this.x != other.x) return false;
if (this.y != other.y) return false;
if (this.z == null ? other.z != null : !this.z.equals(other.z)) return false;
return true;
}
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof DataWithGetterNone;
+ }
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
diff --git a/test/transform/resource/after-ecj/DataPlain.java b/test/transform/resource/after-ecj/DataPlain.java
index b6e385f3..b0af873f 100644
--- a/test/transform/resource/after-ecj/DataPlain.java
+++ b/test/transform/resource/after-ecj/DataPlain.java
@@ -81,3 +81,77 @@ import lombok.Data;
return (((("Data2(x=" + this.getX()) + ", name=") + this.getName()) + ")");
}
}
+final @Data class Data3 {
+ final int x;
+ String name;
+ public @java.beans.ConstructorProperties({"x"}) @java.lang.SuppressWarnings("all") Data3(final int x) {
+ super();
+ this.x = x;
+ }
+ public @java.lang.SuppressWarnings("all") int getX() {
+ return this.x;
+ }
+ public @java.lang.SuppressWarnings("all") String getName() {
+ return this.name;
+ }
+ public @java.lang.SuppressWarnings("all") void setName(final String name) {
+ this.name = name;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((o == null))
+ return false;
+ if ((o.getClass() != this.getClass()))
+ return false;
+ final Data3 other = (Data3) o;
+ if ((this.getX() != other.getX()))
+ return false;
+ if (((this.getName() == null) ? (other.getName() != null) : (! this.getName().equals(other.getName()))))
+ return false;
+ return true;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = ((result * PRIME) + this.getX());
+ result = ((result * PRIME) + ((this.getName() == null) ? 0 : this.getName().hashCode()));
+ return result;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("Data3(x=" + this.getX()) + ", name=") + this.getName()) + ")");
+ }
+}
+final @Data @lombok.EqualsAndHashCode(callSuper = true) class Data4 extends java.util.Timer {
+ final int x;
+ public @java.lang.SuppressWarnings("all") int getX() {
+ return this.x;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("Data4(x=" + this.getX()) + ")");
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((o == null))
+ return false;
+ if ((o.getClass() != this.getClass()))
+ return false;
+ if ((! super.equals(o)))
+ return false;
+ final Data4 other = (Data4) o;
+ if ((this.getX() != other.getX()))
+ return false;
+ return true;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = ((result * PRIME) + super.hashCode());
+ result = ((result * PRIME) + this.getX());
+ return result;
+ }
+ Data4() {
+ super();
+ }
+}
diff --git a/test/transform/resource/before/DataPlain.java b/test/transform/resource/before/DataPlain.java
index 680ae46f..4151dd20 100644
--- a/test/transform/resource/before/DataPlain.java
+++ b/test/transform/resource/before/DataPlain.java
@@ -6,4 +6,16 @@ import lombok.Data;
@Data class Data2 {
final int x;
String name;
-} \ No newline at end of file
+}
+final @Data class Data3 {
+ final int x;
+ String name;
+}
+@Data
+@lombok.EqualsAndHashCode(callSuper=true)
+final class Data4 extends java.util.Timer {
+ final int x;
+ Data4() {
+ super();
+ }
+}