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.JetType;
|
||||||
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
|
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
|
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");
|
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.JvmClassName;
|
||||||
import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames;
|
import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames;
|
||||||
import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils;
|
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.JetType;
|
||||||
|
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
|
||||||
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
|
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
|
||||||
import org.jetbrains.jet.lexer.JetTokens;
|
import org.jetbrains.jet.lexer.JetTokens;
|
||||||
|
|
||||||
@@ -411,10 +413,33 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
|
|
||||||
generateComponentFunctionsForDataClasses();
|
generateComponentFunctionsForDataClasses();
|
||||||
|
|
||||||
final List<PropertyDescriptor> properties = getDataProperties();
|
List<PropertyDescriptor> properties = getDataProperties();
|
||||||
if (!properties.isEmpty()) {
|
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);
|
generateDataClassToStringMethod(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateDataClassHashCodeIfNeeded(List<PropertyDescriptor> properties) {
|
||||||
|
ClassDescriptor intClass = JetStandardLibrary.getInstance().getInt();
|
||||||
|
if (getDeclaredFunctionByRawSignature(descriptor, Name.identifier("hashCode"), intClass) == null) {
|
||||||
generateDataClassHashCodeMethod(properties);
|
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);
|
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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.jet.codegen;
|
package org.jetbrains.jet.codegen;
|
||||||
|
|
||||||
import junit.framework.Test;
|
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);
|
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")
|
@TestMetadata("genericarray.kt")
|
||||||
public void testGenericarray() throws Exception {
|
public void testGenericarray() throws Exception {
|
||||||
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/equals/genericarray.kt");
|
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);
|
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")
|
@TestMetadata("array.kt")
|
||||||
public void testArray() throws Exception {
|
public void testArray() throws Exception {
|
||||||
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/hashcode/array.kt");
|
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);
|
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")
|
@TestMetadata("arrayParams.kt")
|
||||||
public void testArrayParams() throws Exception {
|
public void testArrayParams() throws Exception {
|
||||||
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/arrayParams.kt");
|
blackBoxFileByFullPath("compiler/testData/codegen/dataClasses/tostring/arrayParams.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user