KT-4107 Data objects
This commit is contained in:
+6
@@ -2438,6 +2438,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE) { firDiagnostic ->
|
||||
DataObjectCustomEqualsOrHashCodeImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE) { firDiagnostic ->
|
||||
FunInterfaceConstructorReferenceImpl(
|
||||
firDiagnostic as KtPsiDiagnostic,
|
||||
|
||||
+4
@@ -1729,6 +1729,10 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = TailRecursionInTryIsNotSupported::class
|
||||
}
|
||||
|
||||
abstract class DataObjectCustomEqualsOrHashCode : KtFirDiagnostic<KtNamedFunction>() {
|
||||
override val diagnosticClass get() = DataObjectCustomEqualsOrHashCode::class
|
||||
}
|
||||
|
||||
abstract class FunInterfaceConstructorReference : KtFirDiagnostic<KtExpression>() {
|
||||
override val diagnosticClass get() = FunInterfaceConstructorReference::class
|
||||
}
|
||||
|
||||
+5
@@ -2073,6 +2073,11 @@ internal class TailRecursionInTryIsNotSupportedImpl(
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.TailRecursionInTryIsNotSupported(), KtAbstractFirDiagnostic<PsiElement>
|
||||
|
||||
internal class DataObjectCustomEqualsOrHashCodeImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
) : KtFirDiagnostic.DataObjectCustomEqualsOrHashCode(), KtAbstractFirDiagnostic<KtNamedFunction>
|
||||
|
||||
internal class FunInterfaceConstructorReferenceImpl(
|
||||
override val firDiagnostic: KtPsiDiagnostic,
|
||||
override val token: KtLifetimeToken,
|
||||
|
||||
+37
-3
@@ -6956,6 +6956,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataClasses"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionDataObject.kt")
|
||||
public void testCompanionDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("componentNamedComponent1.kt")
|
||||
public void testComponentNamedComponent1() throws Exception {
|
||||
@@ -7029,9 +7035,21 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObject.kt")
|
||||
public void testDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
|
||||
@TestMetadata("dataObjectDisabled.kt")
|
||||
public void testDataObjectDisabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectEnabled.kt")
|
||||
public void testDataObjectEnabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectLiteral.kt")
|
||||
public void testDataObjectLiteral() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -7655,6 +7673,22 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overrideEqualsAndHashCode.kt")
|
||||
public void testOverrideEqualsAndHashCode() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+5
-2
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.common
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
@@ -33,8 +34,10 @@ abstract class DataClassMethodGenerator(
|
||||
) : FunctionsFromAnyGenerator(declaration, bindingContext) {
|
||||
|
||||
override fun generate() {
|
||||
generateComponentFunctionsForDataClasses()
|
||||
generateCopyFunctionForDataClasses(primaryConstructorParameters)
|
||||
if (declaration !is KtObjectDeclaration) {
|
||||
generateComponentFunctionsForDataClasses()
|
||||
generateCopyFunctionForDataClasses(primaryConstructorParameters)
|
||||
}
|
||||
|
||||
super.generate()
|
||||
}
|
||||
|
||||
+3
-5
@@ -21,11 +21,9 @@ abstract class FunctionsFromAnyGenerator(protected val declaration: KtClassOrObj
|
||||
|
||||
open fun generate() {
|
||||
val properties = primaryConstructorProperties
|
||||
if (properties.isNotEmpty()) {
|
||||
generateToStringIfNeeded(properties)
|
||||
generateHashCodeIfNeeded(properties)
|
||||
generateEqualsIfNeeded(properties)
|
||||
}
|
||||
generateToStringIfNeeded(properties)
|
||||
generateHashCodeIfNeeded(properties)
|
||||
generateEqualsIfNeeded(properties)
|
||||
}
|
||||
|
||||
protected abstract fun generateToStringMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>)
|
||||
|
||||
+71
-61
@@ -17,8 +17,10 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
@@ -103,42 +105,46 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
|
||||
|
||||
mv.visitCode();
|
||||
|
||||
StringConcatGenerator generator = StringConcatGenerator.Companion.create(generationState, iv);
|
||||
generator.genStringBuilderConstructorIfNeded();
|
||||
boolean first = true;
|
||||
if (properties.isEmpty()) {
|
||||
iv.aconst(classDescriptor.getName().asString());
|
||||
} else {
|
||||
StringConcatGenerator generator = StringConcatGenerator.Companion.create(generationState, iv);
|
||||
generator.genStringBuilderConstructorIfNeded();
|
||||
boolean first = true;
|
||||
|
||||
for (PropertyDescriptor propertyDescriptor : properties) {
|
||||
if (first) {
|
||||
generator.addStringConstant(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "=");
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
generator.addStringConstant(", " + propertyDescriptor.getName().asString() + "=");
|
||||
}
|
||||
|
||||
JvmKotlinType type = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
|
||||
Type asmType = type.getType();
|
||||
KotlinType kotlinType = type.getKotlinType();
|
||||
|
||||
if (asmType.getSort() == Type.ARRAY) {
|
||||
Type elementType = correctElementType(asmType);
|
||||
if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
|
||||
iv.invokestatic("java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
asmType = JAVA_STRING_TYPE;
|
||||
kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
|
||||
for (PropertyDescriptor propertyDescriptor : properties) {
|
||||
if (first) {
|
||||
generator.addStringConstant(classDescriptor.getName() + "(" + propertyDescriptor.getName().asString() + "=");
|
||||
first = false;
|
||||
}
|
||||
else if (elementType.getSort() != Type.CHAR) {
|
||||
iv.invokestatic("java/util/Arrays", "toString", "(" + asmType.getDescriptor() + ")Ljava/lang/String;", false);
|
||||
asmType = JAVA_STRING_TYPE;
|
||||
kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
|
||||
else {
|
||||
generator.addStringConstant(", " + propertyDescriptor.getName().asString() + "=");
|
||||
}
|
||||
|
||||
JvmKotlinType type = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
|
||||
Type asmType = type.getType();
|
||||
KotlinType kotlinType = type.getKotlinType();
|
||||
|
||||
if (asmType.getSort() == Type.ARRAY) {
|
||||
Type elementType = correctElementType(asmType);
|
||||
if (elementType.getSort() == Type.OBJECT || elementType.getSort() == Type.ARRAY) {
|
||||
iv.invokestatic("java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||
asmType = JAVA_STRING_TYPE;
|
||||
kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
|
||||
}
|
||||
else if (elementType.getSort() != Type.CHAR) {
|
||||
iv.invokestatic("java/util/Arrays", "toString", "(" + asmType.getDescriptor() + ")Ljava/lang/String;", false);
|
||||
asmType = JAVA_STRING_TYPE;
|
||||
kotlinType = DescriptorUtilsKt.getBuiltIns(function).getStringType();
|
||||
}
|
||||
}
|
||||
genInvokeAppendMethod(generator, asmType, kotlinType, typeMapper, StackValue.onStack(asmType));
|
||||
}
|
||||
genInvokeAppendMethod(generator, asmType, kotlinType, typeMapper, StackValue.onStack(asmType));
|
||||
|
||||
generator.addStringConstant(")");
|
||||
|
||||
generator.genToString();
|
||||
}
|
||||
|
||||
generator.addStringConstant(")");
|
||||
|
||||
generator.genToString();
|
||||
iv.areturn(JAVA_STRING_TYPE);
|
||||
|
||||
FunctionCodegen.endVisit(mv, toStringMethodName, getDeclaration());
|
||||
@@ -175,41 +181,45 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
|
||||
mv.visitCode();
|
||||
boolean first = true;
|
||||
for (PropertyDescriptor propertyDescriptor : properties) {
|
||||
if (!first) {
|
||||
iv.iconst(31);
|
||||
iv.mul(Type.INT_TYPE);
|
||||
}
|
||||
if (properties.isEmpty()) {
|
||||
iv.iconst(DescriptorUtils.getFqNameSafe(classDescriptor).asString().hashCode());
|
||||
} else {
|
||||
boolean first = true;
|
||||
for (PropertyDescriptor propertyDescriptor : properties) {
|
||||
if (!first) {
|
||||
iv.iconst(31);
|
||||
iv.mul(Type.INT_TYPE);
|
||||
}
|
||||
|
||||
JvmKotlinType propertyType = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
|
||||
KotlinType kotlinType = propertyDescriptor.getReturnType();
|
||||
Type asmType = typeMapper.mapType(kotlinType);
|
||||
StackValue.coerce(propertyType.getType(), propertyType.getKotlinType(), asmType, kotlinType, iv);
|
||||
JvmKotlinType propertyType = genOrLoadOnStack(iv, context, propertyDescriptor, 0);
|
||||
KotlinType kotlinType = propertyDescriptor.getReturnType();
|
||||
Type asmType = typeMapper.mapType(kotlinType);
|
||||
StackValue.coerce(propertyType.getType(), propertyType.getKotlinType(), asmType, kotlinType, iv);
|
||||
|
||||
Label ifNull = null;
|
||||
if (!isPrimitive(asmType)) {
|
||||
ifNull = new Label();
|
||||
iv.dup();
|
||||
iv.ifnull(ifNull);
|
||||
}
|
||||
Label ifNull = null;
|
||||
if (!isPrimitive(asmType)) {
|
||||
ifNull = new Label();
|
||||
iv.dup();
|
||||
iv.ifnull(ifNull);
|
||||
}
|
||||
|
||||
genHashCode(mv, iv, asmType, generationState.getTarget());
|
||||
genHashCode(mv, iv, asmType, generationState.getTarget());
|
||||
|
||||
if (ifNull != null) {
|
||||
Label end = new Label();
|
||||
iv.goTo(end);
|
||||
iv.mark(ifNull);
|
||||
iv.pop();
|
||||
iv.iconst(0);
|
||||
iv.mark(end);
|
||||
}
|
||||
if (ifNull != null) {
|
||||
Label end = new Label();
|
||||
iv.goTo(end);
|
||||
iv.mark(ifNull);
|
||||
iv.pop();
|
||||
iv.iconst(0);
|
||||
iv.mark(end);
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
iv.add(Type.INT_TYPE);
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
iv.add(Type.INT_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-3
@@ -6956,6 +6956,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataClasses"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionDataObject.kt")
|
||||
public void testCompanionDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("componentNamedComponent1.kt")
|
||||
public void testComponentNamedComponent1() throws Exception {
|
||||
@@ -7029,9 +7035,21 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObject.kt")
|
||||
public void testDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
|
||||
@TestMetadata("dataObjectDisabled.kt")
|
||||
public void testDataObjectDisabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectEnabled.kt")
|
||||
public void testDataObjectEnabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectLiteral.kt")
|
||||
public void testDataObjectLiteral() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -7655,6 +7673,22 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overrideEqualsAndHashCode.kt")
|
||||
public void testOverrideEqualsAndHashCode() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+37
-3
@@ -6956,6 +6956,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataClasses"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionDataObject.kt")
|
||||
public void testCompanionDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("componentNamedComponent1.kt")
|
||||
public void testComponentNamedComponent1() throws Exception {
|
||||
@@ -7029,9 +7035,21 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObject.kt")
|
||||
public void testDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
|
||||
@TestMetadata("dataObjectDisabled.kt")
|
||||
public void testDataObjectDisabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectEnabled.kt")
|
||||
public void testDataObjectEnabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectLiteral.kt")
|
||||
public void testDataObjectLiteral() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -7655,6 +7673,22 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overrideEqualsAndHashCode.kt")
|
||||
public void testOverrideEqualsAndHashCode() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+2
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
|
||||
import org.jetbrains.kotlin.diagnostics.deprecationError2
|
||||
import org.jetbrains.kotlin.diagnostics.error0
|
||||
import org.jetbrains.kotlin.fir.FirModuleData
|
||||
import org.jetbrains.kotlin.fir.PrivateForInline
|
||||
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
|
||||
@@ -901,6 +902,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val TAILREC_ON_VIRTUAL_MEMBER_ERROR by error<KtNamedFunction>(PositioningStrategy.TAILREC_MODIFIER)
|
||||
val NON_TAIL_RECURSIVE_CALL by warning<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED by warning<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE by error<KtNamedFunction>(PositioningStrategy.OVERRIDE_MODIFIER)
|
||||
}
|
||||
|
||||
val FUN_INTERFACES by object : DiagnosticGroup("Fun interfaces") {
|
||||
|
||||
@@ -483,6 +483,7 @@ object FirErrors {
|
||||
val TAILREC_ON_VIRTUAL_MEMBER_ERROR by error0<KtNamedFunction>(SourceElementPositioningStrategies.TAILREC_MODIFIER)
|
||||
val NON_TAIL_RECURSIVE_CALL by warning0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED by warning0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||
val DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE by error0<KtNamedFunction>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
|
||||
|
||||
// Fun interfaces
|
||||
val FUN_INTERFACE_CONSTRUCTOR_REFERENCE by error0<KtExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirTailrecFunctionChecker,
|
||||
FirTopLevelFunctionsChecker,
|
||||
FirMemberFunctionsChecker,
|
||||
FirDataObjectContentChecker,
|
||||
)
|
||||
|
||||
override val propertyCheckers: Set<FirPropertyChecker>
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.types.isNullableAny
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object FirDataObjectContentChecker : FirSimpleFunctionChecker() {
|
||||
override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (!declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
|
||||
val source = declaration.source
|
||||
if (source == null || source.kind is KtFakeSourceElementKind) return
|
||||
|
||||
val containingClass = context.containingDeclarations.lastOrNull() as? FirClass ?: return
|
||||
if (containingClass.classKind != ClassKind.OBJECT || !containingClass.hasModifier(KtTokens.DATA_KEYWORD)) return
|
||||
|
||||
if (
|
||||
(declaration.name == OperatorNameConventions.HASH_CODE && declaration.valueParameters.isEmpty()) ||
|
||||
(declaration.name == OperatorNameConventions.EQUALS &&
|
||||
declaration.valueParameters.singleOrNull()?.returnTypeRef?.isNullableAny == true)
|
||||
) {
|
||||
reporter.reportOn(source, FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.Companion.classActualTargets
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.FirModifier
|
||||
@@ -27,6 +28,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.DATA_KEYWORD
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
|
||||
object FirModifierChecker : FirBasicDeclarationChecker() {
|
||||
@@ -182,7 +184,11 @@ object FirModifierChecker : FirBasicDeclarationChecker() {
|
||||
}
|
||||
val set = map[modifierToken] ?: emptySet()
|
||||
val checkResult = if (factory == FirErrors.WRONG_MODIFIER_TARGET) {
|
||||
actualTargets.none { it in set }
|
||||
actualTargets.none { it in set } ||
|
||||
// TODO: Implement some generic feature-checking mechanism
|
||||
(modifierToken == DATA_KEYWORD
|
||||
&& actualTargets.contains(KotlinTarget.STANDALONE_OBJECT)
|
||||
&& !context.languageVersionSettings.supportsFeature(LanguageFeature.DataObjects))
|
||||
} else {
|
||||
actualTargets.any { it in set }
|
||||
}
|
||||
|
||||
+2
-1
@@ -136,6 +136,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_NOT_PR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_OVERRIDE_CONFLICT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_VARARG_PARAMETER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_WITHOUT_PARAMETERS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DECLARATION_CANT_BE_INLINED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATED_PROPERTY_INSIDE_VALUE_CLASS
|
||||
@@ -579,7 +580,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_MODIFIER_TA
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_PARAMETER_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_SETTER_RETURN_TYPE
|
||||
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||
|
||||
@Suppress("unused")
|
||||
object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
@@ -1456,6 +1456,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
map.put(TAILREC_ON_VIRTUAL_MEMBER_ERROR, "Tailrec is not allowed on open members")
|
||||
map.put(NON_TAIL_RECURSIVE_CALL, "Recursive call is not a tail call")
|
||||
map.put(TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED, "Tail recursion optimization inside try/catch/finally is not supported")
|
||||
map.put(DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE, "Data object cannot have a custom implementation of 'equals' or 'hashCode'")
|
||||
|
||||
// Fun interfaces
|
||||
map.put(FUN_INTERFACE_CONSTRUCTOR_REFERENCE, "Functional/SAM interface constructor references are prohibited")
|
||||
|
||||
+1
-3
@@ -87,6 +87,7 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
|
||||
IrGeneratorContextBase(components.irBuiltIns),
|
||||
components.symbolTable,
|
||||
irClass,
|
||||
irClass.kotlinFqName,
|
||||
origin
|
||||
) {
|
||||
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction {
|
||||
@@ -183,9 +184,6 @@ class DataClassMembersGenerator(val components: Fir2IrComponents) : Fir2IrCompon
|
||||
fun generate(klass: FirClass): List<FirDeclaration> {
|
||||
val propertyParametersCount = irClass.primaryConstructor?.explicitParameters?.size ?: 0
|
||||
val properties = irClass.properties.filter { it.backingField != null }.take(propertyParametersCount).toList()
|
||||
if (properties.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val result = mutableListOf<FirDeclaration>()
|
||||
|
||||
|
||||
+40
@@ -13648,6 +13648,46 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("multipleInstances.kt")
|
||||
public void testMultipleInstances() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/multipleInstances.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("serialization.kt")
|
||||
public void testSerialization() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/serialization.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
-2
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.builder
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.*
|
||||
import org.jetbrains.kotlin.KtNodeTypes.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
@@ -38,6 +39,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.parsing.*
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
@@ -1078,8 +1080,10 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
private val createParameterTypeRefWithSourceKind: (FirProperty, KtFakeSourceElementKind) -> FirTypeRef,
|
||||
) {
|
||||
fun generate() {
|
||||
generateComponentFunctions()
|
||||
generateCopyFunction()
|
||||
if (classBuilder.classKind != ClassKind.OBJECT) {
|
||||
generateComponentFunctions()
|
||||
generateCopyFunction()
|
||||
}
|
||||
// Refer to (IR utils or FIR backend) DataClassMembersGenerator for generating equals, hashCode, and toString
|
||||
}
|
||||
|
||||
|
||||
@@ -737,6 +737,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<PsiElement> DATA_CLASS_WITHOUT_PARAMETERS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtParameter> DATA_CLASS_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtParameter> DATA_CLASS_NOT_PROPERTY_PARAMETER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtParameter> CATCH_PARAMETER_WITH_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
|
||||
+1
@@ -1058,6 +1058,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(DATA_CLASS_WITHOUT_PARAMETERS, "Data class must have at least one primary constructor parameter");
|
||||
MAP.put(DATA_CLASS_VARARG_PARAMETER, "Primary constructor vararg parameters are forbidden for data classes");
|
||||
MAP.put(DATA_CLASS_NOT_PROPERTY_PARAMETER, "Data class primary constructor must only have property (val / var) parameters");
|
||||
MAP.put(DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE, "Data object cannot have a custom implementation of 'equals' or 'hashCode'");
|
||||
|
||||
MAP.put(CATCH_PARAMETER_WITH_DEFAULT_VALUE, "Catch clause parameter may not have a default value");
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
ValueParameterUsageInDefaultArgumentChecker,
|
||||
CyclicAnnotationsChecker,
|
||||
UnsupportedUntilRangeDeclarationChecker,
|
||||
DataObjectContentChecker,
|
||||
)
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
@@ -27,7 +28,7 @@ class DataClassDeclarationChecker : DeclarationChecker {
|
||||
if (descriptor !is ClassDescriptor) return
|
||||
if (declaration !is KtClassOrObject) return
|
||||
|
||||
if (descriptor.isData) {
|
||||
if (descriptor.isData && descriptor.kind == ClassKind.CLASS) {
|
||||
if (descriptor.unsubstitutedPrimaryConstructor == null && descriptor.constructors.isNotEmpty()) {
|
||||
declaration.nameIdentifier?.let { context.trace.report(Errors.PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS.on(it)) }
|
||||
}
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object DataObjectContentChecker : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (descriptor !is FunctionDescriptor) return
|
||||
if (descriptor.name != OperatorNameConventions.EQUALS && descriptor.name != OperatorNameConventions.HASH_CODE) return
|
||||
|
||||
val container = descriptor.containingDeclaration
|
||||
if (container !is ClassDescriptor) return
|
||||
if (!container.isData || container.kind != ClassKind.OBJECT) return
|
||||
|
||||
if (DescriptorUtils.getAllOverriddenDescriptors(descriptor).any(::isDeclaredInAny)) {
|
||||
val target = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) ?: declaration
|
||||
context.trace.report(Errors.DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE.on(target))
|
||||
}
|
||||
}
|
||||
|
||||
private fun isDeclaredInAny(descriptor: FunctionDescriptor): Boolean {
|
||||
val container = descriptor.containingDeclaration
|
||||
return container is ClassDescriptor && KotlinBuiltIns.isAny(container)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -482,7 +482,7 @@ open class LazyClassMemberScope(
|
||||
}
|
||||
|
||||
private fun addDataClassMethods(result: MutableCollection<DeclarationDescriptor>, location: LookupLocation) {
|
||||
if (!thisDescriptor.isData) return
|
||||
if (!thisDescriptor.isData || thisDescriptor.kind != ClassKind.CLASS) return
|
||||
|
||||
if (getPrimaryConstructor() == null) return
|
||||
|
||||
|
||||
+2
-5
@@ -14,17 +14,13 @@ import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.isArray
|
||||
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
|
||||
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irClass: IrClass, val origin: IrDeclarationOrigin) {
|
||||
private fun IrClass.addSyntheticFunction(name: String, returnType: IrType) =
|
||||
@@ -74,6 +70,7 @@ open class LoweringDataClassMemberGenerator(
|
||||
IrGeneratorContextBase(backendContext.irBuiltIns),
|
||||
backendContext.ir.symbols.externalSymbolTable,
|
||||
irClass,
|
||||
irClass.kotlinFqName,
|
||||
origin,
|
||||
forbidDirectFieldAccess
|
||||
) {
|
||||
|
||||
@@ -353,6 +353,7 @@ private val jvmFilePhases = listOf(
|
||||
|
||||
enumClassPhase,
|
||||
objectClassPhase,
|
||||
readResolveForDataObjectsPhase,
|
||||
staticInitializersPhase,
|
||||
initializersPhase,
|
||||
initializersCleanupPhase,
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.ir.addDispatchReceiver
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.builders.irExprBody
|
||||
import org.jetbrains.kotlin.ir.builders.irGetField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.fields
|
||||
import org.jetbrains.kotlin.ir.util.getAllSuperclasses
|
||||
import org.jetbrains.kotlin.ir.util.hasEqualFqName
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
internal val readResolveForDataObjectsPhase = makeIrFilePhase(
|
||||
::ReadResolveForDataObjectsLowering,
|
||||
name = "ReadResolveForDataObjectsLowering",
|
||||
description = "Generate readResolve for serializable data objects"
|
||||
)
|
||||
|
||||
private class ReadResolveForDataObjectsLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
override fun lower(irClass: IrClass) {
|
||||
if (!context.state.languageVersionSettings.supportsFeature(LanguageFeature.DataObjects)) return
|
||||
|
||||
if (!irClass.isData || irClass.kind != ClassKind.OBJECT || !irClass.isSerializable()) return
|
||||
|
||||
context.irFactory.buildFun {
|
||||
name = Name.identifier("readResolve")
|
||||
modality = Modality.FINAL
|
||||
origin = IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER
|
||||
returnType = context.irBuiltIns.anyType
|
||||
visibility = DescriptorVisibilities.PRIVATE
|
||||
}.apply {
|
||||
addDispatchReceiver { type = irClass.defaultType }
|
||||
parent = irClass
|
||||
body = context.createJvmIrBuilder(symbol).run {
|
||||
val instanceField = irClass.fields.single { it.name.asString() == JvmAbi.INSTANCE_FIELD }
|
||||
irExprBody(irGetField(null, instanceField))
|
||||
}
|
||||
irClass.declarations.add(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val SERIALIZABLE_FQ_NAME = FqName("java.io.Serializable")
|
||||
|
||||
private fun IrClass.isSerializable(): Boolean =
|
||||
getAllSuperclasses().any { it.hasEqualFqName(SERIALIZABLE_FQ_NAME) }
|
||||
+3
-2
@@ -74,8 +74,9 @@ class DataClassMembersGenerator(
|
||||
val origin: IrDeclarationOrigin
|
||||
) : DataClassMethodGenerator(ktClassOrObject, declarationGenerator.context.bindingContext) {
|
||||
|
||||
private val irDataClassMembersGenerator =
|
||||
object : DataClassMembersGenerator(context, context.symbolTable, irClass, origin, generateBodies = generateBodies) {
|
||||
private val irDataClassMembersGenerator = object : DataClassMembersGenerator(
|
||||
context, context.symbolTable, irClass, ktClassOrObject.fqName, origin, generateBodies = generateBodies
|
||||
) {
|
||||
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction =
|
||||
declareSimpleFunction(startOffset, endOffset, origin, functionDescriptor)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
/**
|
||||
@@ -32,6 +33,7 @@ abstract class DataClassMembersGenerator(
|
||||
val context: IrGeneratorContext,
|
||||
val symbolTable: ReferenceSymbolTable,
|
||||
val irClass: IrClass,
|
||||
val fqName: FqName?,
|
||||
val origin: IrDeclarationOrigin,
|
||||
val forbidDirectFieldAccess: Boolean = false,
|
||||
val generateBodies: Boolean = false
|
||||
@@ -139,9 +141,9 @@ abstract class DataClassMembersGenerator(
|
||||
+irReturnTrue()
|
||||
}
|
||||
|
||||
fun generateHashCodeMethodBody(properties: List<IrProperty>) {
|
||||
fun generateHashCodeMethodBody(properties: List<IrProperty>, constHashCode: Int) {
|
||||
if (properties.isEmpty()) {
|
||||
+irReturn(irInt(0))
|
||||
+irReturn(irInt(constHashCode))
|
||||
return
|
||||
} else if (properties.size == 1) {
|
||||
+irReturn(getHashCodeOfProperty(properties[0]))
|
||||
@@ -186,6 +188,10 @@ abstract class DataClassMembersGenerator(
|
||||
}
|
||||
|
||||
fun generateToStringMethodBody(properties: List<IrProperty>) {
|
||||
if (properties.isEmpty() && irClass.kind == ClassKind.OBJECT) {
|
||||
+irReturn(irString(irClass.name.asString()))
|
||||
return
|
||||
}
|
||||
val irConcat = irConcat()
|
||||
irConcat.addArgument(irString(irClass.classNameForToString() + "("))
|
||||
var first = true
|
||||
@@ -259,6 +265,7 @@ abstract class DataClassMembersGenerator(
|
||||
) {
|
||||
MemberFunctionBuilder(startOffset, endOffset, declareSimpleFunction(startOffset, endOffset, function)).addToClass { irFunction ->
|
||||
irFunction.buildWithScope {
|
||||
irFunction.parent = irClass
|
||||
generateSyntheticFunctionParameterDeclarations(irFunction)
|
||||
body(irFunction)
|
||||
}
|
||||
@@ -344,14 +351,20 @@ abstract class DataClassMembersGenerator(
|
||||
// Entry for psi2ir
|
||||
fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
|
||||
buildMember(function) {
|
||||
generateHashCodeMethodBody(properties.map { getIrProperty(it) })
|
||||
generateHashCodeMethodBody(
|
||||
properties.map { getIrProperty(it) },
|
||||
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Entry for fir2ir
|
||||
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<IrProperty>) {
|
||||
buildMember(irFunction) {
|
||||
generateHashCodeMethodBody(properties)
|
||||
generateHashCodeMethodBody(
|
||||
properties,
|
||||
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ open class KtClass : KtClassOrObject {
|
||||
_stub?.isInterface() ?: (findChildByType<PsiElement>(KtTokens.INTERFACE_KEYWORD) != null)
|
||||
|
||||
fun isEnum(): Boolean = hasModifier(KtTokens.ENUM_KEYWORD)
|
||||
fun isData(): Boolean = hasModifier(KtTokens.DATA_KEYWORD)
|
||||
fun isSealed(): Boolean = hasModifier(KtTokens.SEALED_KEYWORD)
|
||||
fun isInner(): Boolean = hasModifier(KtTokens.INNER_KEYWORD)
|
||||
fun isInline(): Boolean = hasModifier(KtTokens.INLINE_KEYWORD)
|
||||
|
||||
@@ -100,6 +100,8 @@ abstract class KtClassOrObject :
|
||||
|
||||
override fun isLocal(): Boolean = stub?.isLocal() ?: KtPsiUtil.isLocal(this)
|
||||
|
||||
fun isData(): Boolean = hasModifier(KtTokens.DATA_KEYWORD)
|
||||
|
||||
override fun getDeclarations(): List<KtDeclaration> = getBody()?.declarations.orEmpty()
|
||||
|
||||
override fun getPresentation(): ItemPresentation? = ItemPresentationProviders.getItemPresentation(this)
|
||||
|
||||
@@ -132,7 +132,8 @@ val featureDependencies = mapOf(
|
||||
EXPECT_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
|
||||
ACTUAL_KEYWORD to listOf(LanguageFeature.MultiPlatformProjects),
|
||||
LATEINIT_KEYWORD to listOf(LanguageFeature.LateinitTopLevelProperties, LanguageFeature.LateinitLocalVariables),
|
||||
FUN_KEYWORD to listOf(LanguageFeature.FunctionalInterfaceConversion)
|
||||
FUN_KEYWORD to listOf(LanguageFeature.FunctionalInterfaceConversion),
|
||||
DATA_KEYWORD to listOf(LanguageFeature.DataObjects)
|
||||
)
|
||||
|
||||
val featureDependenciesTargets = mapOf(
|
||||
@@ -141,7 +142,8 @@ val featureDependenciesTargets = mapOf(
|
||||
LanguageFeature.LateinitTopLevelProperties to setOf(KotlinTarget.TOP_LEVEL_PROPERTY),
|
||||
LanguageFeature.InlineClasses to setOf(KotlinTarget.CLASS_ONLY),
|
||||
LanguageFeature.JvmInlineValueClasses to setOf(KotlinTarget.CLASS_ONLY),
|
||||
LanguageFeature.FunctionalInterfaceConversion to setOf(KotlinTarget.INTERFACE)
|
||||
LanguageFeature.FunctionalInterfaceConversion to setOf(KotlinTarget.INTERFACE),
|
||||
LanguageFeature.DataObjects to setOf(KotlinTarget.STANDALONE_OBJECT)
|
||||
)
|
||||
|
||||
val defaultVisibilityTargets: EnumSet<KotlinTarget> = EnumSet.of(
|
||||
@@ -204,7 +206,7 @@ val possibleTargetMap = mapOf(
|
||||
KotlinTarget.LOCAL_VARIABLE,
|
||||
KotlinTarget.BACKING_FIELD
|
||||
),
|
||||
DATA_KEYWORD to EnumSet.of(KotlinTarget.CLASS_ONLY, KotlinTarget.LOCAL_CLASS),
|
||||
DATA_KEYWORD to EnumSet.of(KotlinTarget.CLASS_ONLY, KotlinTarget.LOCAL_CLASS, KotlinTarget.STANDALONE_OBJECT),
|
||||
INLINE_KEYWORD to EnumSet.of(
|
||||
KotlinTarget.FUNCTION,
|
||||
KotlinTarget.PROPERTY,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// LANGUAGE: +DataObjects
|
||||
// WITH_STDLIB
|
||||
|
||||
package com.example
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
data object DataObject
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(DataObject, DataObject)
|
||||
assertFalse(DataObject == null)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// LANGUAGE: +DataObjects
|
||||
// WITH_STDLIB
|
||||
|
||||
package com.example
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
data object DataObject {
|
||||
data object Inner
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(DataObject.hashCode(), DataObject.hashCode())
|
||||
assertNotEquals(DataObject.hashCode(), DataObject.Inner.hashCode())
|
||||
assertNotEquals(0, DataObject.hashCode())
|
||||
assertNotEquals(0, DataObject.Inner.hashCode())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// LANGUAGE: +DataObjects
|
||||
// WITH_STDLIB
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
data object DataObject
|
||||
|
||||
val doppelganger = DataObject::class.java.declaredConstructors[0].apply { isAccessible = true }.newInstance()
|
||||
|
||||
fun box(): String {
|
||||
assertFalse(DataObject === doppelganger)
|
||||
assertEquals(DataObject, doppelganger)
|
||||
assertEquals(DataObject.hashCode(), DataObject::class.java.cast(doppelganger).hashCode())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// LANGUAGE: +DataObjects
|
||||
// WITH_STDLIB
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// FULL_JDK
|
||||
// CHECK_BYTECODE_LISTING
|
||||
|
||||
import java.io.*
|
||||
|
||||
data object NonSerializableDataObject
|
||||
|
||||
data object SerializableDataObject: Serializable
|
||||
|
||||
fun box(): String {
|
||||
ByteArrayOutputStream().use { baos ->
|
||||
ObjectOutputStream(baos).use { oos -> oos.writeObject(SerializableDataObject) }
|
||||
ByteArrayInputStream(baos.toByteArray()).use { bais -> assert(java.io.ObjectInputStream(bais).readObject() === SerializableDataObject) }
|
||||
}
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
@kotlin.Metadata
|
||||
public final class NonSerializableDataObject {
|
||||
// source: 'serialization.kt'
|
||||
public final static @org.jetbrains.annotations.NotNull field INSTANCE: NonSerializableDataObject
|
||||
static method <clinit>(): void
|
||||
private method <init>(): void
|
||||
public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
|
||||
public method hashCode(): int
|
||||
public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class SerializableDataObject {
|
||||
// source: 'serialization.kt'
|
||||
public final static @org.jetbrains.annotations.NotNull field INSTANCE: SerializableDataObject
|
||||
static method <clinit>(): void
|
||||
private method <init>(): void
|
||||
public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean
|
||||
public method hashCode(): int
|
||||
private final method readResolve(): java.lang.Object
|
||||
public @org.jetbrains.annotations.NotNull method toString(): java.lang.String
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class SerializationKt {
|
||||
// source: 'serialization.kt'
|
||||
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// LANGUAGE: +DataObjects
|
||||
// WITH_STDLIB
|
||||
|
||||
package com.example
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
data object DataObject {
|
||||
data object Nested
|
||||
}
|
||||
|
||||
class Foo {
|
||||
data object Inner
|
||||
}
|
||||
|
||||
data object Declared {
|
||||
override fun toString() = "Overriden"
|
||||
}
|
||||
|
||||
open class WithFinalToString {
|
||||
final override fun toString() = "FinalToString"
|
||||
}
|
||||
|
||||
data object InheritedFromClassWithFinalToString: WithFinalToString()
|
||||
|
||||
open class WithOpenToString {
|
||||
override fun toString() = "OpenToString"
|
||||
}
|
||||
|
||||
data object InheritedFromClassWithOpenToString: WithOpenToString()
|
||||
|
||||
abstract class WithAbstractToString {
|
||||
abstract override fun toString(): String
|
||||
}
|
||||
|
||||
data object InheritedFromClassWithAbstractToString : WithAbstractToString()
|
||||
|
||||
class C {
|
||||
companion object CC
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
assertEquals("DataObject", DataObject.toString())
|
||||
assertEquals("Nested", DataObject.Nested.toString())
|
||||
assertEquals("Inner", Foo.Inner.toString())
|
||||
assertEquals("Overriden", Declared.toString())
|
||||
assertEquals("FinalToString", InheritedFromClassWithFinalToString.toString())
|
||||
assertEquals("InheritedFromClassWithOpenToString", InheritedFromClassWithOpenToString.toString())
|
||||
assertEquals("InheritedFromClassWithAbstractToString", InheritedFromClassWithAbstractToString.toString())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// FIR_IDENTICAL
|
||||
// LANGUAGE: +DataObjects
|
||||
|
||||
class C {
|
||||
companion <!WRONG_MODIFIER_TARGET!>data<!> object Object
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package
|
||||
|
||||
public final class C {
|
||||
public constructor C()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public data companion object Object {
|
||||
private constructor Object()
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// FIR_IDENTICAL
|
||||
<!WRONG_MODIFIER_TARGET!>data<!> object Object<!CONSTRUCTOR_IN_OBJECT!>(val x: Int, val y: Int)<!>
|
||||
@@ -1,13 +0,0 @@
|
||||
package
|
||||
|
||||
public data object Object {
|
||||
private constructor Object(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final val y: kotlin.Int
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.Int
|
||||
public final operator /*synthesized*/ fun component2(): kotlin.Int
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ..., /*1*/ y: kotlin.Int = ...): Object
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// LANGUAGE: -DataObjects
|
||||
<!WRONG_MODIFIER_TARGET!>data<!> object Object
|
||||
@@ -0,0 +1,2 @@
|
||||
// LANGUAGE: -DataObjects
|
||||
<!UNSUPPORTED_FEATURE!>data<!> object Object
|
||||
@@ -0,0 +1,9 @@
|
||||
package
|
||||
|
||||
public data object Object {
|
||||
private constructor Object()
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// LANGUAGE: +DataObjects
|
||||
// FIR_IDENTICAL
|
||||
data object Object
|
||||
@@ -0,0 +1,9 @@
|
||||
package
|
||||
|
||||
public data object Object {
|
||||
private constructor Object()
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// FIR_IDENTICAL
|
||||
// LANGUAGE: +DataObjects
|
||||
|
||||
interface I {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
val o = <!UNRESOLVED_REFERENCE!>data<!><!SYNTAX!><!> object<!SYNTAX!><!>: I {
|
||||
override fun foo() {}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package
|
||||
|
||||
public val o: [Error type: Not found recorded type for data]
|
||||
|
||||
public object `<no name provided>` : I {
|
||||
private constructor `<no name provided>`()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ fun foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public interface I {
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public abstract fun foo(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
B(2)
|
||||
}
|
||||
|
||||
<!WRONG_MODIFIER_TARGET!>data<!> object <!DATA_CLASS_WITHOUT_PARAMETERS!>Second<!>
|
||||
<!UNSUPPORTED_FEATURE!>data<!> object Second
|
||||
|
||||
<!WRONG_MODIFIER_TARGET!>data<!> interface <!DATA_CLASS_WITHOUT_PARAMETERS!>Third<!>
|
||||
<!WRONG_MODIFIER_TARGET!>data<!> interface Third
|
||||
|
||||
<!WRONG_MODIFIER_TARGET!>data<!> annotation class Fourth(val x: Int)
|
||||
<!WRONG_MODIFIER_TARGET!>data<!> annotation class Fourth(val x: Int)
|
||||
|
||||
@@ -11,8 +11,6 @@ public final data enum class First : kotlin.Enum<First> {
|
||||
public final val x: kotlin.Int
|
||||
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: First): kotlin.Int
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.Int
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ...): First
|
||||
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
protected/*protected and package*/ final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun finalize(): kotlin.Unit
|
||||
public final override /*1*/ /*fake_override*/ /*isHiddenForResolutionEverywhereBesideSupercalls*/ fun getDeclaringClass(): java.lang.Class<First!>!
|
||||
@@ -27,8 +25,6 @@ public final data enum class First : kotlin.Enum<First> {
|
||||
public final data annotation class Fourth : kotlin.Annotation {
|
||||
public constructor Fourth(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.Int
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.Int = ...): Fourth
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
@@ -36,7 +32,6 @@ public final data annotation class Fourth : kotlin.Annotation {
|
||||
|
||||
public data object Second {
|
||||
private constructor Second()
|
||||
public final /*synthesized*/ fun copy(): Second
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
@@ -47,3 +42,4 @@ public data interface Third {
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// FIR_IDENTICAL
|
||||
// LANGUAGE: +DataObjects
|
||||
|
||||
data object Override {
|
||||
<!DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE!>override<!> fun equals(other: Any?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
<!DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE!>override<!> fun hashCode(): Int {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
open class Base {
|
||||
open fun hashCode(x: Int) = x
|
||||
}
|
||||
|
||||
data object NoOverride: Base() {
|
||||
fun equals(other: Any?, tag: Int): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
fun hashCode(param: String): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun hashCode(x: Int) = x + 1
|
||||
}
|
||||
|
||||
open class Super {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return super.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
data object OverridenInSuper: Super() {
|
||||
<!DATA_OBJECT_CUSTOM_EQUALS_OR_HASH_CODE!>override<!> fun equals(other: Any?): Boolean {
|
||||
return super.equals(other)
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package
|
||||
|
||||
public open class Base {
|
||||
public constructor Base()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open fun hashCode(/*0*/ x: kotlin.Int): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public data object NoOverride : Base {
|
||||
private constructor NoOverride()
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun equals(/*0*/ other: kotlin.Any?, /*1*/ tag: kotlin.Int): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ fun hashCode(/*0*/ x: kotlin.Int): kotlin.Int
|
||||
public final fun hashCode(/*0*/ param: kotlin.String): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public data object Override {
|
||||
private constructor Override()
|
||||
public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public data object OverridenInSuper : Super {
|
||||
private constructor OverridenInSuper()
|
||||
public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public open class Super {
|
||||
public constructor Super()
|
||||
public open override /*1*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// !LANGUAGE: +DataObjects
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data <!CONFLICTING_JVM_DECLARATIONS!>object A<!> : Serializable {
|
||||
<!CONFLICTING_JVM_DECLARATIONS!>private fun readResolve(): Any<!> = this
|
||||
}
|
||||
|
||||
data object B : Serializable {
|
||||
private fun readResolve(): B = this
|
||||
}
|
||||
|
||||
data object C {
|
||||
private fun readResolve(): Any = this
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package
|
||||
|
||||
public data object A : java.io.Serializable {
|
||||
private constructor A()
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
private final fun readResolve(): kotlin.Any
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public data object B : java.io.Serializable {
|
||||
private constructor B()
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
private final fun readResolve(): B
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public data object C {
|
||||
private constructor C()
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
private final fun readResolve(): kotlin.Any
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
+1
@@ -56,3 +56,4 @@ data class Test<T : Any?> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Generated
+37
-3
@@ -6962,6 +6962,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataClasses"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("companionDataObject.kt")
|
||||
public void testCompanionDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/companionDataObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("componentNamedComponent1.kt")
|
||||
public void testComponentNamedComponent1() throws Exception {
|
||||
@@ -7035,9 +7041,21 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObject.kt")
|
||||
public void testDataObject() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObject.kt");
|
||||
@TestMetadata("dataObjectDisabled.kt")
|
||||
public void testDataObjectDisabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectDisabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectEnabled.kt")
|
||||
public void testDataObjectEnabled() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectEnabled.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("dataObjectLiteral.kt")
|
||||
public void testDataObjectLiteral() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataClasses/dataObjectLiteral.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -7661,6 +7679,22 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/dataObjects"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overrideEqualsAndHashCode.kt")
|
||||
public void testOverrideEqualsAndHashCode() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/dataObjects/overrideEqualsAndHashCode.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/declarationChecks")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+16
@@ -61,6 +61,22 @@ public class DiagnosticsTestWithJvmIrBackendGenerated extends AbstractDiagnostic
|
||||
runTest("compiler/testData/diagnostics/testsWithJvmBackend/suspendInlineCycle_ir.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects"), Pattern.compile("^(.+)\\.kts?$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("customReadResolve.kt")
|
||||
public void testCustomReadResolve() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects/customReadResolve.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/duplicateJvmSignature")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+10
@@ -61,6 +61,16 @@ public class DiagnosticsTestWithOldJvmBackendGenerated extends AbstractDiagnosti
|
||||
runTest("compiler/testData/diagnostics/testsWithJvmBackend/suspendInlineCycle.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_OLD, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/duplicateJvmSignature")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+28
@@ -13528,6 +13528,34 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+40
@@ -13648,6 +13648,46 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("multipleInstances.kt")
|
||||
public void testMultipleInstances() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/multipleInstances.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("serialization.kt")
|
||||
public void testSerialization() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/serialization.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+28
@@ -10981,6 +10981,34 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DataObjects extends AbstractLightAnalysisModeTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -263,6 +263,7 @@ enum class LanguageFeature(
|
||||
RefineTypeCheckingOnAssignmentsToJavaFields(KOTLIN_1_8, kind = BUG_FIX),
|
||||
RangeUntilOperator(KOTLIN_1_8), // KT-15613
|
||||
GenericInlineClassParameter(sinceVersion = KOTLIN_1_8, kind = UNSTABLE_FEATURE), // KT-32162
|
||||
DataObjects(KOTLIN_1_8), // KT-4107
|
||||
|
||||
// 1.9
|
||||
|
||||
|
||||
+3
-2
@@ -34,8 +34,9 @@ enum class KotlinTarget(val description: String, val isDefault: Boolean = true)
|
||||
// includes only top level classes and nested/inner classes (but not enums, objects, interfaces and local classes)
|
||||
CLASS_ONLY("class", false),
|
||||
|
||||
// does not include OBJECT_LITERAL but DOES include COMPANION_OBJECT
|
||||
// does not include OBJECT_LITERAL but DOES include both STANDALONE_OBJECT and COMPANION_OBJECT
|
||||
OBJECT("object", false),
|
||||
STANDALONE_OBJECT("standalone object", false),
|
||||
COMPANION_OBJECT("companion object", false),
|
||||
INTERFACE("interface", false),
|
||||
ENUM_CLASS("enum class", false),
|
||||
@@ -84,7 +85,7 @@ enum class KotlinTarget(val description: String, val isDefault: Boolean = true)
|
||||
val LOCAL_CLASS_LIST = listOf(LOCAL_CLASS, CLASS)
|
||||
val CLASS_LIST = listOf(CLASS_ONLY, CLASS)
|
||||
val COMPANION_OBJECT_LIST = listOf(COMPANION_OBJECT, OBJECT, CLASS)
|
||||
val OBJECT_LIST = listOf(OBJECT, CLASS)
|
||||
val OBJECT_LIST = listOf(STANDALONE_OBJECT, OBJECT, CLASS)
|
||||
val INTERFACE_LIST = listOf(INTERFACE, CLASS)
|
||||
val ENUM_LIST = listOf(ENUM_CLASS, CLASS)
|
||||
val ENUM_ENTRY_LIST = listOf(ENUM_ENTRY, PROPERTY, FIELD)
|
||||
|
||||
+28
@@ -10116,6 +10116,34 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+28
@@ -10158,6 +10158,34 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+28
@@ -8996,6 +8996,34 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DataObjects extends AbstractIrCodegenBoxWasmTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+17
-3
@@ -16,7 +16,9 @@ import org.jetbrains.kotlin.js.backend.ast.*;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration;
|
||||
import org.jetbrains.kotlin.psi.KtParameter;
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElementKt;
|
||||
|
||||
@@ -35,6 +37,11 @@ abstract class JsEqualsHashcodeToStringGenerator extends DataClassMethodGenerato
|
||||
|
||||
@Override
|
||||
public void generateToStringMethod(@NotNull FunctionDescriptor function, @NotNull List<? extends PropertyDescriptor> classProperties) {
|
||||
if (getDeclaration() instanceof KtObjectDeclaration) {
|
||||
generateJsMethod(function).getBody().getStatements().add(new JsReturn(new JsStringLiteral(getDeclaration().getName())));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: relax this limitation, with the data generation logic fixed.
|
||||
assert !classProperties.isEmpty();
|
||||
JsFunction functionObj = generateJsMethod(function);
|
||||
@@ -65,6 +72,14 @@ abstract class JsEqualsHashcodeToStringGenerator extends DataClassMethodGenerato
|
||||
@Override
|
||||
public void generateHashCodeMethod(@NotNull FunctionDescriptor function, @NotNull List<? extends PropertyDescriptor> classProperties) {
|
||||
JsFunction functionObj = generateJsMethod(function);
|
||||
if (classProperties.isEmpty()) {
|
||||
FqName fqName = getDeclaration().getFqName();
|
||||
JsExpression returnExpression = new JsIntLiteral(fqName != null ? fqName.hashCode() : 0);
|
||||
JsReturn returnStatement = new JsReturn(returnExpression);
|
||||
returnStatement.setSource(getDeclaration());
|
||||
functionObj.getBody().getStatements().add(returnStatement);
|
||||
return;
|
||||
}
|
||||
|
||||
List<JsStatement> statements = functionObj.getBody().getStatements();
|
||||
|
||||
@@ -92,8 +107,8 @@ abstract class JsEqualsHashcodeToStringGenerator extends DataClassMethodGenerato
|
||||
|
||||
@Override
|
||||
public void generateEqualsMethod(@NotNull FunctionDescriptor function, @NotNull List<? extends PropertyDescriptor> classProperties) {
|
||||
assert !classProperties.isEmpty();
|
||||
JsFunction functionObj = generateJsMethod(function);
|
||||
|
||||
JsFunctionScope funScope = functionObj.getScope();
|
||||
|
||||
JsName paramName = funScope.declareName("other");
|
||||
@@ -120,9 +135,8 @@ abstract class JsEqualsHashcodeToStringGenerator extends DataClassMethodGenerato
|
||||
fieldChain = and(fieldChain, next);
|
||||
}
|
||||
}
|
||||
assert fieldChain != null;
|
||||
|
||||
JsExpression returnExpression = or(referenceEqual, and(isNotNull, and(otherIsObject, and(prototypeEqual, fieldChain))));
|
||||
JsExpression returnExpression = or(referenceEqual, and(isNotNull, and(otherIsObject, fieldChain != null ? and(prototypeEqual, fieldChain) : prototypeEqual)));
|
||||
JsReturn returnStatement = new JsReturn(returnExpression);
|
||||
returnStatement.setSource(getDeclaration());
|
||||
functionObj.getBody().getStatements().add(returnStatement);
|
||||
|
||||
+30
@@ -11102,6 +11102,36 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/dataObjects")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@Tag("codegen")
|
||||
@UseExtTestCaseGroupProvider()
|
||||
public class DataObjects {
|
||||
@Test
|
||||
public void testAllFilesPresentInDataObjects() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/dataObjects"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/equals.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("hashCode.kt")
|
||||
public void testHashCode() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/hashCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("toString.kt")
|
||||
public void testToString() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/dataObjects/toString.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/box/deadCodeElimination")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
@@ -32,7 +32,7 @@ class Experiment {
|
||||
annotation class Type
|
||||
|
||||
@Type
|
||||
data class Group(s: String)
|
||||
data class Group(var s: String)
|
||||
}
|
||||
|
||||
class Foo {
|
||||
|
||||
@@ -100,6 +100,8 @@ public final class Experiment {
|
||||
@kotlin.Metadata()
|
||||
@Experiment.Type()
|
||||
public static final class Group {
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
private java.lang.String s;
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final Experiment.Group copy(@org.jetbrains.annotations.NotNull()
|
||||
@@ -107,10 +109,41 @@ public final class Experiment {
|
||||
return null;
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public boolean equals(@org.jetbrains.annotations.Nullable()
|
||||
java.lang.Object other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
@java.lang.Override()
|
||||
public java.lang.String toString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Group(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String s) {
|
||||
super();
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String component1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String getS() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setS(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String p0) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,12 +100,28 @@ public final class Experiment {
|
||||
@kotlin.Metadata()
|
||||
@Experiment.Type()
|
||||
public static final class Group {
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
private java.lang.String s;
|
||||
|
||||
public Group(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String s) {
|
||||
super();
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String getS() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setS(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String p0) {
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String component1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final Experiment.Group copy(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String s) {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ class Experiment {
|
||||
annotation class Type
|
||||
|
||||
@Type
|
||||
data class Group(s: String)
|
||||
data class Group(val s: String)
|
||||
}
|
||||
|
||||
class Foo {
|
||||
|
||||
+29
@@ -103,6 +103,8 @@ public final class Experiment {
|
||||
@kotlin.Metadata()
|
||||
@test.Experiment.Type()
|
||||
public static final class Group {
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
private final java.lang.String s = null;
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final test.Experiment.Group copy(@org.jetbrains.annotations.NotNull()
|
||||
@@ -110,10 +112,37 @@ public final class Experiment {
|
||||
return null;
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public boolean equals(@org.jetbrains.annotations.Nullable()
|
||||
java.lang.Object other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@java.lang.Override()
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
@java.lang.Override()
|
||||
public java.lang.String toString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Group(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String s) {
|
||||
super();
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String component1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String getS() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -103,12 +103,24 @@ public final class Experiment {
|
||||
@kotlin.Metadata()
|
||||
@test.Experiment.Type()
|
||||
public static final class Group {
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
private final java.lang.String s = null;
|
||||
|
||||
public Group(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String s) {
|
||||
super();
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String getS() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final java.lang.String component1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@org.jetbrains.annotations.NotNull()
|
||||
public final test.Experiment.Group copy(@org.jetbrains.annotations.NotNull()
|
||||
java.lang.String s) {
|
||||
|
||||
Reference in New Issue
Block a user