Index: src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java =================================================================== --- src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java (revision 17485) +++ src/main/org/codehaus/groovy/transform/ImmutableASTTransformation.java (working copy) @@ -91,6 +91,12 @@ if (parent instanceof ClassNode) { ClassNode cNode = (ClassNode) parent; String cName = cNode.getName(); + if(hasDeclaredMethod(cNode, "equals", 1)) { + throw new RuntimeException("Classes marked @Immutable cannot override equals()."); + } + if(hasDeclaredMethod(cNode, "hashCode", 0)) { + throw new RuntimeException("Classes marked @Immutable cannot override hashCode()."); + } if (cNode.isInterface()) { throw new RuntimeException("Error processing interface '" + cName + "'. " + MY_TYPE_NAME + " not allowed for interfaces."); } @@ -113,9 +119,22 @@ createConstructor(cNode); createHashCode(cNode); createEquals(cNode); - createToString(cNode); + if(!hasDeclaredMethod(cNode, "toString", 0)) { + createToString(cNode); + } } } + + private boolean hasDeclaredMethod(ClassNode cNode, String name, int argsCount) { + List ms = cNode.getDeclaredMethods(name); + for(MethodNode m : ms) { + Parameter[] paras = m.getParameters(); + if(paras != null && paras.length == argsCount) { + return true; + } + } + return false; + } private void ensureNotPublic(String cNode, FieldNode fNode) { String fName = fNode.getName(); Index: src/test/groovy/bugs/Groovy3713Bug.groovy =================================================================== --- src/test/groovy/bugs/Groovy3713Bug.groovy (revision 0) +++ src/test/groovy/bugs/Groovy3713Bug.groovy (revision 0) @@ -0,0 +1,50 @@ +package groovy.bugs + +import gls.CompilableTestSupport + +class Groovy3713Bug extends CompilableTestSupport { + void testOverridingObjectMethodsWithImmutable() { + // overriding of standard toString - allowed + assertScript """ + @Immutable class MyClass { + String toString() { 'some return value' } + } + new MyClass() + """ + // overriding hashCode() - not allowed + shouldFail """ + @Immutable class MyClass { + int hashCode() {0} + } + new MyClass() + """ + // overloaded hashCode() - allowed + assertScript """ + @Immutable class MyClass { + int hashCode(tmp) {tmp} + } + new MyClass() + """ + // overriding equals() - not allowed + shouldFail """ + @Immutable class MyClass { + boolean equals(obj) {true} + } + new MyClass() + """ + // overriding equals() - not allowed + shouldFail """ + @Immutable class MyClass { + boolean equals(MyClass obj) {true} + } + new MyClass() + """ + // overloaded equals() - allowed + assertScript """ + @Immutable class MyClass { + boolean equals(MyClass obj, MyClass obj1) {true} + } + new MyClass() + """ + } +} \ No newline at end of file