KT-2819 Duplicate toString() method generated in data class

#KT-2819 Fixed
This commit is contained in:
Andrey Breslav
2012-09-26 12:32:16 +04:00
parent b5145f1a87
commit 92ac865c72
9 changed files with 188 additions and 6 deletions
@@ -37,10 +37,7 @@ import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
@@ -200,4 +197,41 @@ public class CodegenUtil {
throw new IllegalStateException("code generation for synthesized members should be handled separately");
}
}
@Nullable
public static FunctionDescriptor getDeclaredFunctionByRawSignature(
@NotNull ClassDescriptor owner,
@NotNull Name name,
@NotNull ClassDescriptor returnedClass,
@NotNull ClassDescriptor... valueParameterClasses
) {
Collection<FunctionDescriptor> functions = owner.getDefaultType().getMemberScope().getFunctions(name);
for (FunctionDescriptor function : functions) {
if (function.getKind() == CallableMemberDescriptor.Kind.DECLARATION
&& function.getTypeParameters().isEmpty()
&& valueParameterClassesMatch(function.getValueParameters(), Arrays.asList(valueParameterClasses))
&& rawTypeMatches(function.getReturnType(), returnedClass)) {
return function;
}
}
return null;
}
private static boolean valueParameterClassesMatch(
@NotNull List<ValueParameterDescriptor> parameters,
@NotNull List<ClassDescriptor> classes) {
if (parameters.size() != classes.size()) return false;
for (int i = 0; i < parameters.size(); i++) {
ValueParameterDescriptor parameterDescriptor = parameters.get(i);
ClassDescriptor classDescriptor = classes.get(i);
if (!rawTypeMatches(parameterDescriptor.getType(), classDescriptor)) {
return false;
}
}
return true;
}
private static boolean rawTypeMatches(JetType type, ClassDescriptor classDescriptor) {
return type.getConstructor().getDeclarationDescriptor().getOriginal() == classDescriptor.getOriginal();
}
}
@@ -50,7 +50,9 @@ import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames;
import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
import org.jetbrains.jet.lexer.JetTokens;
@@ -411,10 +413,33 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
generateComponentFunctionsForDataClasses();
final List<PropertyDescriptor> properties = getDataProperties();
List<PropertyDescriptor> properties = getDataProperties();
if (!properties.isEmpty()) {
generateDataClassToStringIfNeeded(properties);
generateDataClassHashCodeIfNeeded(properties);
generateDataClassEqualsIfNeeded(properties);
}
}
private void generateDataClassToStringIfNeeded(List<PropertyDescriptor> properties) {
ClassDescriptor stringClass = JetStandardLibrary.getInstance().getString();
if (getDeclaredFunctionByRawSignature(descriptor, Name.identifier("toString"), stringClass) == null) {
generateDataClassToStringMethod(properties);
}
}
private void generateDataClassHashCodeIfNeeded(List<PropertyDescriptor> properties) {
ClassDescriptor intClass = JetStandardLibrary.getInstance().getInt();
if (getDeclaredFunctionByRawSignature(descriptor, Name.identifier("hashCode"), intClass) == null) {
generateDataClassHashCodeMethod(properties);
}
}
private void generateDataClassEqualsIfNeeded(List<PropertyDescriptor> properties) {
ClassDescriptor booleanClass = JetStandardLibrary.getInstance().getBoolean();
ClassDescriptor anyClass = JetStandardClasses.getAny();
FunctionDescriptor equalsFunction = getDeclaredFunctionByRawSignature(descriptor, Name.identifier("equals"), booleanClass, anyClass);
if (equalsFunction == null) {
generateDataClassEqualsMethod(properties);
}
}
@@ -0,0 +1,8 @@
data class A(val x: Int) {
fun equals(other: Any?): Boolean = false
}
fun box(): String {
val a = A(0)
return if (a equals a) "fail" else "OK"
}
@@ -0,0 +1,38 @@
data class A(val x: Int) {
fun equals(other: Any): Boolean = false
}
data class B(val x: Int) {
fun equals(other: B): Boolean = false
}
data class C(val x: Int) {
fun equals(): Boolean = false
}
data class D(val x: Int) {
fun equals(other: Any?, another: String): Boolean = false
}
data class E(val x: Int) {
fun equals(x: E): Boolean = false
fun equals(x: Any?): Boolean = false
}
fun box(): String {
javaClass<A>().getDeclaredMethod("equals", javaClass<Any>())
javaClass<B>().getDeclaredMethod("equals", javaClass<Any>())
javaClass<B>().getDeclaredMethod("equals", javaClass<B>())
javaClass<C>().getDeclaredMethod("equals", javaClass<Any>())
javaClass<C>().getDeclaredMethod("equals")
javaClass<D>().getDeclaredMethod("equals", javaClass<Any>())
javaClass<D>().getDeclaredMethod("equals", javaClass<Any>(), javaClass<String>())
javaClass<E>().getDeclaredMethod("equals", javaClass<Any>())
javaClass<E>().getDeclaredMethod("equals", javaClass<E>())
return "OK"
}
@@ -0,0 +1,7 @@
data class A(val x: Int) {
fun hashCode(): Int = -3
}
fun box(): String {
return if (A(0).hashCode() == -3) "OK" else "fail"
}
@@ -0,0 +1,17 @@
data class A(val x: Int) {
fun hashCode(other: Any): Int = 0
}
data class B(val x: Int) {
fun hashCode(other: B, another: Any): Int = 0
}
fun box(): String {
javaClass<A>().getDeclaredMethod("hashCode")
javaClass<A>().getDeclaredMethod("hashCode", javaClass<Any>())
javaClass<B>().getDeclaredMethod("hashCode")
javaClass<B>().getDeclaredMethod("hashCode", javaClass<B>(), javaClass<Any>())
return "OK"
}
@@ -0,0 +1,7 @@
data class A(val x: Int) {
fun toString(): String = "!"
}
fun box(): String {
return if (A(0).toString() == "!") "OK" else "fail"
}
@@ -0,0 +1,17 @@
data class A(val x: Int) {
fun toString(other: Any): String = ""
}
data class B(val x: Int) {
fun toString(other: B, another: Any): String = ""
}
fun box(): String {
javaClass<A>().getDeclaredMethod("toString")
javaClass<A>().getDeclaredMethod("toString", javaClass<Any>())
javaClass<B>().getDeclaredMethod("toString")
javaClass<B>().getDeclaredMethod("toString", javaClass<B>(), javaClass<Any>())
return "OK"
}
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.codegen;
import junit.framework.Test;
@@ -88,6 +87,16 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", new File("compiler/testData/codegen/dataClasses/equals"), "kt", true);
}
@TestMetadata("alreadyDeclared.kt")
public void testAlreadyDeclared() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/equals/alreadyDeclared.kt");
}
@TestMetadata("alreadyDeclaredWrongSignature.kt")
public void testAlreadyDeclaredWrongSignature() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/equals/alreadyDeclaredWrongSignature.kt");
}
@TestMetadata("genericarray.kt")
public void testGenericarray() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/equals/genericarray.kt");
@@ -121,6 +130,16 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", new File("compiler/testData/codegen/dataClasses/hashcode"), "kt", true);
}
@TestMetadata("alreadyDeclared.kt")
public void testAlreadyDeclared() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/hashcode/alreadyDeclared.kt");
}
@TestMetadata("alreadyDeclaredWrongSignature.kt")
public void testAlreadyDeclaredWrongSignature() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/hashcode/alreadyDeclaredWrongSignature.kt");
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/hashcode/array.kt");
@@ -179,6 +198,16 @@ public class DataClassCodegenTestGenerated extends AbstractDataClassCodegenTest
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.codegen.AbstractDataClassCodegenTest", new File("compiler/testData/codegen/dataClasses/tostring"), "kt", true);
}
@TestMetadata("alreadyDeclared.kt")
public void testAlreadyDeclared() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/alreadyDeclared.kt");
}
@TestMetadata("alreadyDeclaredWrongSignature.kt")
public void testAlreadyDeclaredWrongSignature() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/alreadyDeclaredWrongSignature.kt");
}
@TestMetadata("arrayParams.kt")
public void testArrayParams() throws Exception {
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/arrayParams.kt");