KT-2819 Duplicate toString() method generated in data class
#KT-2819 Fixed
This commit is contained in:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user