Now either all or none of equals/hashCode/canEqual is generated. Fixes issue 240.

This commit is contained in:
Roel Spilker committed 2011-07-18 21:05:00 +02:00
1 parent aa1a0e7ac8
commit 83e2fb5e00
16 files changed
+496 -79

No files matched your search

@@ -292,7 +292,7 @@ public class EclipseHandlerUtil {
/** Serves as return value for the methods that check for the existence of fields and methods. */
public enum MemberExistsResult {
NOT_EXISTS, EXISTS_BY_USER, EXISTS_BY_LOMBOK;
NOT_EXISTS, EXISTS_BY_LOMBOK, EXISTS_BY_USER;
}
/**
@@ -203,52 +203,36 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
boolean needsCanEqual = false;
switch (methodExists("equals", typeNode)) {
case NOT_EXISTS:
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
needsCanEqual = !isDirectDescendantOfObject || !isFinal;
MethodDeclaration equals = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual);
injectMethod(typeNode, equals);
break;
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
boolean needsCanEqual = !isDirectDescendantOfObject || !isFinal;
java.util.List<MemberExistsResult> existsResults = new ArrayList<MemberExistsResult>();
existsResults.add(methodExists("equals", typeNode));
existsResults.add(methodExists("hashCode", typeNode));
existsResults.add(methodExists("canEqual", typeNode));
switch (Collections.max(existsResults)) {
case EXISTS_BY_LOMBOK:
break;
default:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
errorNode.addWarning("Not generating equals(Object other): A method with that name already exists");
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
errorNode.addWarning(msg);
}
break;
return;
case NOT_EXISTS:
default:
//fallthrough
}
MethodDeclaration equalsMethod = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual);
injectMethod(typeNode, equalsMethod);
if (needsCanEqual) {
switch (methodExists("canEqual", typeNode)) {
case NOT_EXISTS:
MethodDeclaration equals = createCanEqual(typeNode, errorNode.get());
injectMethod(typeNode, equals);
break;
case EXISTS_BY_LOMBOK:
case EXISTS_BY_USER:
default:
break;
}
MethodDeclaration canEqualMethod = createCanEqual(typeNode, errorNode.get());
injectMethod(typeNode, canEqualMethod);
}
switch (methodExists("hashCode", typeNode)) {
case NOT_EXISTS:
MethodDeclaration hashCode = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
injectMethod(typeNode, hashCode);
break;
case EXISTS_BY_LOMBOK:
break;
default:
case EXISTS_BY_USER:
if (whineIfExists) {
errorNode.addWarning("Not generating hashCode(): A method with that name already exists");
}
break;
}
MethodDeclaration hashCodeMethod = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
injectMethod(typeNode, hashCodeMethod);
}
private MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
@@ -22,6 +22,10 @@
package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
import java.util.Collections;
import lombok.EqualsAndHashCode;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
@@ -170,51 +174,36 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
}
boolean needsCanEqual = false;
switch (methodExists("equals", typeNode)) {
case NOT_EXISTS:
boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
needsCanEqual = !isFinal || !isDirectDescendantOfObject;
JCMethodDecl method = createEquals(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, needsCanEqual, source.get());
injectMethod(typeNode, method);
break;
boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
java.util.List<MemberExistsResult> existsResults = new ArrayList<MemberExistsResult>();
existsResults.add(methodExists("equals", typeNode));
existsResults.add(methodExists("hashCode", typeNode));
existsResults.add(methodExists("canEqual", typeNode));
switch (Collections.max(existsResults)) {
case EXISTS_BY_LOMBOK:
break;
default:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
source.addWarning("Not generating equals(Object other): A method with that name already exists");
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
source.addWarning(msg);
}
break;
return;
case NOT_EXISTS:
default:
//fallthrough
}
JCMethodDecl equalsMethod = createEquals(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, needsCanEqual, source.get());
injectMethod(typeNode, equalsMethod);
if (needsCanEqual) {
switch (methodExists("canEqual", typeNode)) {
case NOT_EXISTS:
JCMethodDecl method = createCanEqual(typeNode, source.get());
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.toList(), callSuper, fieldAccess, source.get());
injectMethod(typeNode, method);
break;
case EXISTS_BY_LOMBOK:
break;
default:
case EXISTS_BY_USER:
if (whineIfExists) {
source.addWarning("Not generating hashCode(): A method with that name already exists");
}
break;
JCMethodDecl canEqualMethod = createCanEqual(typeNode, source.get());
injectMethod(typeNode, canEqualMethod);
}
JCMethodDecl hashCodeMethod = createHashCode(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, source.get());
injectMethod(typeNode, hashCodeMethod);
}
private JCMethodDecl createHashCode(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
@@ -185,7 +185,7 @@ public class JavacHandlerUtil {
/** Serves as return value for the methods that check for the existence of fields and methods. */
public enum MemberExistsResult {
NOT_EXISTS, EXISTS_BY_USER, EXISTS_BY_LOMBOK;
NOT_EXISTS, EXISTS_BY_LOMBOK, EXISTS_BY_USER;
}
/**
@@ -0,0 +1,99 @@
class EqualsAndHashCode {
int x;
boolean[] y;
Object[] z;
String a;
@java.lang.Override
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCode)) return false;
final EqualsAndHashCode other = (EqualsAndHashCode)o;
if (!other.canEqual((java.lang.Object)this)) return false;
if (this.x != other.x) return false;
if (!java.util.Arrays.equals(this.y, other.y)) return false;
if (!java.util.Arrays.deepEquals(this.z, other.z)) return false;
if (this.a == null ? other.a != null : !this.a.equals((java.lang.Object)other.a)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
public boolean canEqual(final java.lang.Object other) {
return other instanceof EqualsAndHashCode;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = result * PRIME + this.x;
result = result * PRIME + java.util.Arrays.hashCode(this.y);
result = result * PRIME + java.util.Arrays.deepHashCode(this.z);
result = result * PRIME + (this.a == null ? 0 : this.a.hashCode());
return result;
}
}
final class EqualsAndHashCode2 {
int x;
@java.lang.Override
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCode2)) return false;
final EqualsAndHashCode2 other = (EqualsAndHashCode2)o;
if (this.x != other.x) 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.x;
return result;
}
}
final class EqualsAndHashCode3 extends EqualsAndHashCode {
@java.lang.Override
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCode3)) return false;
final EqualsAndHashCode3 other = (EqualsAndHashCode3)o;
if (!other.canEqual((java.lang.Object)this)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
public boolean canEqual(final java.lang.Object other) {
return other instanceof EqualsAndHashCode3;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
int result = 1;
return result;
}
}
class EqualsAndHashCode4 extends EqualsAndHashCode {
@java.lang.Override
@java.lang.SuppressWarnings("all")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCode4)) return false;
final EqualsAndHashCode4 other = (EqualsAndHashCode4)o;
if (!other.canEqual((java.lang.Object)this)) return false;
if (!super.equals(o)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
public boolean canEqual(final java.lang.Object other) {
return other instanceof EqualsAndHashCode4;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = result * PRIME + super.hashCode();
return result;
}
}
@@ -0,0 +1,18 @@
class EqualsAndHashCodeWithExistingMethods {
int x;
public int hashCode() {
return 42;
}
}
final class EqualsAndHashCodeWithExistingMethods2 {
int x;
public boolean equals(Object other) {
return false;
}
}
final class EqualsAndHashCodeWithExistingMethods3 extends EqualsAndHashCodeWithExistingMethods {
int x;
public boolean canEqual(Object other) {
return true;
}
}
@@ -0,0 +1,109 @@
@lombok.EqualsAndHashCode class EqualsAndHashCode {
int x;
boolean[] y;
Object[] z;
String a;
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode)))
return false;
final EqualsAndHashCode other = (EqualsAndHashCode) o;
if ((! other.canEqual((java.lang.Object) this)))
return false;
if ((this.x != other.x))
return false;
if ((! java.util.Arrays.equals(this.y, other.y)))
return false;
if ((! java.util.Arrays.deepEquals(this.z, other.z)))
return false;
if (((this.a == null) ? (other.a != null) : (! this.a.equals((java.lang.Object) other.a))))
return false;
return true;
}
public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
return (other instanceof EqualsAndHashCode);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
final int PRIME = 31;
int result = 1;
result = ((result * PRIME) + this.x);
result = ((result * PRIME) + java.util.Arrays.hashCode(this.y));
result = ((result * PRIME) + java.util.Arrays.deepHashCode(this.z));
result = ((result * PRIME) + ((this.a == null) ? 0 : this.a.hashCode()));
return result;
}
EqualsAndHashCode() {
super();
}
}
final @lombok.EqualsAndHashCode class EqualsAndHashCode2 {
int x;
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode2)))
return false;
final EqualsAndHashCode2 other = (EqualsAndHashCode2) o;
if ((this.x != other.x))
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.x);
return result;
}
EqualsAndHashCode2() {
super();
}
}
final @lombok.EqualsAndHashCode(callSuper = false) class EqualsAndHashCode3 extends EqualsAndHashCode {
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode3)))
return false;
final EqualsAndHashCode3 other = (EqualsAndHashCode3) o;
if ((! other.canEqual((java.lang.Object) this)))
return false;
return true;
}
public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
return (other instanceof EqualsAndHashCode3);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
int result = 1;
return result;
}
EqualsAndHashCode3() {
super();
}
}
@lombok.EqualsAndHashCode(callSuper = true) class EqualsAndHashCode4 extends EqualsAndHashCode {
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode4)))
return false;
final EqualsAndHashCode4 other = (EqualsAndHashCode4) o;
if ((! other.canEqual((java.lang.Object) this)))
return false;
if ((! super.equals(o)))
return false;
return true;
}
public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
return (other instanceof EqualsAndHashCode4);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
final int PRIME = 31;
int result = 1;
result = ((result * PRIME) + super.hashCode());
return result;
}
EqualsAndHashCode4() {
super();
}
}
@@ -0,0 +1,27 @@
@lombok.EqualsAndHashCode class EqualsAndHashCodeWithExistingMethods {
int x;
EqualsAndHashCodeWithExistingMethods() {
super();
}
public int hashCode() {
return 42;
}
}
final @lombok.EqualsAndHashCode class EqualsAndHashCodeWithExistingMethods2 {
int x;
EqualsAndHashCodeWithExistingMethods2() {
super();
}
public boolean equals(Object other) {
return false;
}
}
final @lombok.EqualsAndHashCode(callSuper = true) class EqualsAndHashCodeWithExistingMethods3 extends EqualsAndHashCodeWithExistingMethods {
int x;
EqualsAndHashCodeWithExistingMethods3() {
super();
}
public boolean canEqual(Object other) {
return true;
}
}
@@ -0,0 +1,109 @@
@lombok.EqualsAndHashCode class EqualsAndHashCode {
int x;
boolean[] y;
Object[] z;
String a;
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode)))
return false;
final EqualsAndHashCode other = (EqualsAndHashCode) o;
if ((! other.canEqual((java.lang.Object) this)))
return false;
if ((this.x != other.x))
return false;
if ((! java.util.Arrays.equals(this.y, other.y)))
return false;
if ((! java.util.Arrays.deepEquals(this.z, other.z)))
return false;
if (((this.a == null) ? (other.a != null) : (! this.a.equals((java.lang.Object) other.a))))
return false;
return true;
}
public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
return (other instanceof EqualsAndHashCode);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
final int PRIME = 31;
int result = 1;
result = ((result * PRIME) + this.x);
result = ((result * PRIME) + java.util.Arrays.hashCode(this.y));
result = ((result * PRIME) + java.util.Arrays.deepHashCode(this.z));
result = ((result * PRIME) + ((this.a == null) ? 0 : this.a.hashCode()));
return result;
}
EqualsAndHashCode() {
super();
}
}
final @lombok.EqualsAndHashCode class EqualsAndHashCode2 {
int x;
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode2)))
return false;
final EqualsAndHashCode2 other = (EqualsAndHashCode2) o;
if ((this.x != other.x))
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.x);
return result;
}
EqualsAndHashCode2() {
super();
}
}
final @lombok.EqualsAndHashCode(callSuper = false) class EqualsAndHashCode3 extends EqualsAndHashCode {
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode3)))
return false;
final EqualsAndHashCode3 other = (EqualsAndHashCode3) o;
if ((! other.canEqual((java.lang.Object) this)))
return false;
return true;
}
public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
return (other instanceof EqualsAndHashCode3);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
int result = 1;
return result;
}
EqualsAndHashCode3() {
super();
}
}
@lombok.EqualsAndHashCode(callSuper = true) class EqualsAndHashCode4 extends EqualsAndHashCode {
public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
if ((o == this))
return true;
if ((! (o instanceof EqualsAndHashCode4)))
return false;
final EqualsAndHashCode4 other = (EqualsAndHashCode4) o;
if ((! other.canEqual((java.lang.Object) this)))
return false;
if ((! super.equals(o)))
return false;
return true;
}
public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
return (other instanceof EqualsAndHashCode4);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
final int PRIME = 31;
int result = 1;
result = ((result * PRIME) + super.hashCode());
return result;
}
EqualsAndHashCode4() {
super();
}
}
@@ -0,0 +1,27 @@
@lombok.EqualsAndHashCode class EqualsAndHashCodeWithExistingMethods {
int x;
EqualsAndHashCodeWithExistingMethods() {
super();
}
public int hashCode() {
return 42;
}
}
final @lombok.EqualsAndHashCode class EqualsAndHashCodeWithExistingMethods2 {
int x;
EqualsAndHashCodeWithExistingMethods2() {
super();
}
public boolean equals(Object other) {
return false;
}
}
final @lombok.EqualsAndHashCode(callSuper = true) class EqualsAndHashCodeWithExistingMethods3 extends EqualsAndHashCodeWithExistingMethods {
int x;
EqualsAndHashCodeWithExistingMethods3() {
super();
}
public boolean canEqual(Object other) {
return true;
}
}
@@ -0,0 +1,20 @@
@lombok.EqualsAndHashCode
class EqualsAndHashCode {
int x;
boolean[] y;
Object[] z;
String a;
}
@lombok.EqualsAndHashCode
final class EqualsAndHashCode2 {
int x;
}
@lombok.EqualsAndHashCode(callSuper=false)
final class EqualsAndHashCode3 extends EqualsAndHashCode {
}
@lombok.EqualsAndHashCode(callSuper=true)
class EqualsAndHashCode4 extends EqualsAndHashCode {
}
@@ -0,0 +1,26 @@
@lombok.EqualsAndHashCode
class EqualsAndHashCodeWithExistingMethods {
int x;
public int hashCode() {
return 42;
}
}
@lombok.EqualsAndHashCode
final class EqualsAndHashCodeWithExistingMethods2 {
int x;
public boolean equals(Object other) {
return false;
}
}
@lombok.EqualsAndHashCode(callSuper=true)
final class EqualsAndHashCodeWithExistingMethods3 extends EqualsAndHashCodeWithExistingMethods {
int x;
public boolean canEqual(Object other) {
return true;
}
}
@@ -0,0 +1,3 @@
1:1 WARNING Not generating equals, hashCode and canEquals: A method with one of those names already exists. (Either all or none of these methods will be generated).
10:1 WARNING Not generating equals and hashCode: A method with one of those names already exists. (Either all or none of these methods will be generated).
19:1 WARNING Not generating equals, hashCode and canEquals: A method with one of those names already exists. (Either all or none of these methods will be generated).
@@ -0,0 +1,3 @@
1 warning Not generating equals, hashCode and canEquals: A method with one of those names already exists. (Either all or none of these methods will be generated).
10 warning Not generating equals and hashCode: A method with one of those names already exists. (Either all or none of these methods will be generated).
19 warning Not generating equals, hashCode and canEquals: A method with one of those names already exists. (Either all or none of these methods will be generated).
@@ -0,0 +1,3 @@
1 warning Not generating equals, hashCode and canEquals: A method with one of those names already exists. (Either all or none of these methods will be generated).
10 warning Not generating equals and hashCode: A method with one of those names already exists. (Either all or none of these methods will be generated).
19 warning Not generating equals, hashCode and canEquals: A method with one of those names already exists. (Either all or none of these methods will be generated).
+3 -3
View File
@@ -65,9 +65,9 @@
return false. This is analogous to <code>java.lang.Double</code>'s equals method, and is in fact required to ensure that comparing an object
to an exact copy of itself returns <code>true</code> for equality.
</p><p>
If there is <em>any</em> method named either <code>hashCode</code> or <code>equals</code>, regardless of parameters or return type,
that method will not be generated, and a warning is emitted instead. <code>hashCode</code> and <code>equals</code> need to be in sync with
each other, which lombok cannot guarantee if it is only generating one of the two methods, hence you always get a warning if one <em>or</em> both
If there is <em>any</em> method named either <code>hashCode</code>, <code>equals</code> or <code>canEqual</code>, regardless of
parameters or return type, no methods will be generated, and a warning is emitted instead. These 3 methods need to be in sync with
each other, which lombok cannot guarantee unless it generates all the methods, hence you always get a warning if one <em>or</em> more
of the methods already exist.
</p><p>
Attempting to exclude fields that don't exist or would have been excluded anyway (because they are static or transient) results in warnings on the named fields.