diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index dfb965008f7..7c27585044a 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -3225,6 +3225,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @Test + @TestMetadata("expectConstructor.kt") + public void testExpectConstructor() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt"); + } + @Test @TestMetadata("fakeTypeMismatchOnExtensionReference.kt") public void testFakeTypeMismatchOnExtensionReference() throws Exception { diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index 6adbad7232e..9d2cc48c458 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -2844,6 +2844,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @TestMetadata("expectConstructor.kt") + public void testExpectConstructor() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt"); + } + @TestMetadata("fakeTypeMismatchOnExtensionReference.kt") public void testFakeTypeMismatchOnExtensionReference() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/problems/fakeTypeMismatchOnExtensionReference.kt"); diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.fir.txt b/compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.fir.txt new file mode 100644 index 00000000000..0c851f6e80c --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.fir.txt @@ -0,0 +1,31 @@ +FILE: expectConstructor.kt + public open class Base : R|kotlin/Any| { + public constructor(v: R|kotlin/String|): R|Base| { + super() + } + + } + public final expect class Derived : R|Base| { + public expect constructor(v: R|kotlin/String|): R|Derived| + + } + public open expect class ExpectBase : R|kotlin/Any| { + public expect constructor(v: R|kotlin/String|): R|ExpectBase| + + } + public final expect class ExpectDerived : R|ExpectBase| { + public expect constructor(v: R|kotlin/String|): R|ExpectDerived| + + } + public open expect class IOException : R|kotlin/Any| { + public expect constructor(message: R|kotlin/String|, cause: R|kotlin/Throwable?|): R|IOException| + + public expect constructor(message: R|kotlin/String|): R|IOException| { + super() + } + + } + public final expect class EOFException : R|IOException| { + public expect constructor(message: R|kotlin/String|): R|EOFException| + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt b/compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt new file mode 100644 index 00000000000..b2aecf63c06 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt @@ -0,0 +1,13 @@ +open class Base(v: String) + +expect class Derived(v: String) : Base + +expect open class ExpectBase(v: String) + +expect class ExpectDerived(v: String) : ExpectBase + +expect open class IOException(message: String, cause: Throwable?) { + constructor(message: String) +} + +expect class EOFException(message: String) : IOException diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/FakeOverrides.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/FakeOverrides.fir.txt index 0584958a586..bc13efddc07 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/FakeOverrides.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/FakeOverrides.fir.txt @@ -1,9 +1,7 @@ Module: m1-common FILE: common.kt public open expect class A : R|kotlin/Any| { - public expect constructor(): R|A| { - super() - } + public expect constructor(): R|A| public open expect fun foo(arg: R|T|): R|kotlin/Unit| diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/Members.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/Members.fir.txt index 6e9033a691a..55a7148d9c7 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/Members.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/Members.fir.txt @@ -1,9 +1,7 @@ Module: m1-common FILE: common.kt public open expect class A : R|kotlin/Any| { - public expect constructor(): R|A| { - super() - } + public expect constructor(): R|A| public final expect fun foo(): R|kotlin/Unit| diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/SuperTypes.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/SuperTypes.fir.txt index bfdacc544f1..85a21f2b187 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/SuperTypes.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/multiModule/SuperTypes.fir.txt @@ -1,9 +1,7 @@ Module: m1-common FILE: common.kt public open expect class A : R|kotlin/Any| { - public expect constructor(): R|A| { - super() - } + public expect constructor(): R|A| public final expect fun foo(): R|kotlin/Unit| diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index 7e1bbf9e1c6..f71353f126d 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -3225,6 +3225,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @Test + @TestMetadata("expectConstructor.kt") + public void testExpectConstructor() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt"); + } + @Test @TestMetadata("fakeTypeMismatchOnExtensionReference.kt") public void testFakeTypeMismatchOnExtensionReference() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 6e6799f1a0a..fc511ab1f9e 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -3225,6 +3225,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/problems/doubleGenericDiamond.kt"); } + @Test + @TestMetadata("expectConstructor.kt") + public void testExpectConstructor() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt"); + } + @Test @TestMetadata("fakeTypeMismatchOnExtensionReference.kt") public void testFakeTypeMismatchOnExtensionReference() throws Exception { diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt index 94a5b9e7952..dafeaec01f0 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/DeclarationsConverter.kt @@ -56,6 +56,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.* import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.utils.addToStdlib.runIf +import org.jetbrains.kotlin.utils.addToStdlib.runUnless class DeclarationsConverter( session: FirSession, @@ -711,6 +712,7 @@ class DeclarationsConverter( } val enumEntryName = identifier.nameAsSafeName() + val containingClassIsExpectClass = classWrapper.hasExpect() || context.containerIsExpect return buildEnumEntry { source = enumEntry.toFirSourceElement() moduleData = baseModuleData @@ -720,7 +722,7 @@ class DeclarationsConverter( symbol = FirEnumEntrySymbol(CallableId(context.currentClassId, enumEntryName)) status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL).apply { isStatic = true - isExpect = classWrapper.hasExpect() || context.containerIsExpect + isExpect = containingClassIsExpectClass } if (classWrapper.hasDefaultConstructor && enumEntry.getChildNodeByType(INITIALIZER_LIST) == null && modifiers.annotations.isEmpty() && classBodyNode == null @@ -762,7 +764,7 @@ class DeclarationsConverter( enumClassWrapper, superTypeCallEntry?.toFirSourceElement(), isEnumEntry = true, - containingClassIsExpectClass = false + containingClassIsExpectClass = containingClassIsExpectClass )?.let { declarations += it.firConstructor } classBodyNode?.also { // Use ANONYMOUS_OBJECT_NAME for the owner class id of enum entry declarations @@ -828,7 +830,9 @@ class DeclarationsConverter( ): PrimaryConstructor? { if (primaryConstructor == null && !classWrapper.isEnumEntry() && classWrapper.hasSecondaryConstructor) return null val classKind = classWrapper.classBuilder.classKind - if (primaryConstructor == null && (containingClassIsExpectClass && classKind != ClassKind.ENUM_CLASS)) return null + if (primaryConstructor == null && + (containingClassIsExpectClass && classKind != ClassKind.ENUM_CLASS && classKind != ClassKind.ENUM_ENTRY) + ) return null if (classWrapper.isInterface()) return null var modifiers = Modifier() @@ -841,26 +845,28 @@ class DeclarationsConverter( } val defaultVisibility = classWrapper.defaultConstructorVisibility() - val firDelegatedCall = buildDelegatedConstructorCall { - source = delegatedConstructorSource ?: selfTypeSource?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall) - constructedTypeRef = classWrapper.delegatedSuperTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef) - isThis = false - calleeReference = buildExplicitSuperReference { - //[dirty] in case of enum classWrapper.delegatedSuperTypeRef.source is whole enum source - source = if (!isEnumEntry) { - classWrapper.delegatedSuperTypeRef.source?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall) - ?: this@buildDelegatedConstructorCall.source?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall) - } else { - delegatedConstructorSource - ?.lighterASTNode - ?.getChildNodeByType(CONSTRUCTOR_CALLEE) - ?.toFirSourceElement(KtFakeSourceElementKind.DelegatingConstructorCall) - ?: this@buildDelegatedConstructorCall.source - } + val firDelegatedCall = runUnless(containingClassIsExpectClass) { + buildDelegatedConstructorCall { + source = delegatedConstructorSource ?: selfTypeSource?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall) + constructedTypeRef = classWrapper.delegatedSuperTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef) + isThis = false + calleeReference = buildExplicitSuperReference { + //[dirty] in case of enum classWrapper.delegatedSuperTypeRef.source is whole enum source + source = if (!isEnumEntry) { + classWrapper.delegatedSuperTypeRef.source?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall) + ?: this@buildDelegatedConstructorCall.source?.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall) + } else { + delegatedConstructorSource + ?.lighterASTNode + ?.getChildNodeByType(CONSTRUCTOR_CALLEE) + ?.toFirSourceElement(KtFakeSourceElementKind.DelegatingConstructorCall) + ?: this@buildDelegatedConstructorCall.source + } - superTypeRef = this@buildDelegatedConstructorCall.constructedTypeRef + superTypeRef = this@buildDelegatedConstructorCall.constructedTypeRef + } + extractArgumentsFrom(classWrapper.superTypeCallEntry) } - extractArgumentsFrom(classWrapper.superTypeCallEntry) } val explicitVisibility = runIf(primaryConstructor != null) { diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index e72441e527e..0b7c9443474 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -837,6 +837,7 @@ open class RawFirBuilder( delegatedSelfTypeRef ?: delegatedSuperTypeRef!!, owner = this, containerTypeParameters, + containingClassIsExpectClass, body = null ) container.declarations += firPrimaryConstructor @@ -851,12 +852,13 @@ open class RawFirBuilder( delegatedSelfTypeRef: FirTypeRef, owner: KtClassOrObject, ownerTypeParameters: List, + containingClassIsExpectClass: Boolean, body: FirBlock? = null ): FirConstructor { val constructorCall = superTypeCallEntry?.toFirSourceElement() val constructorSource = this?.toFirSourceElement() ?: owner.toKtPsiSourceElement(KtFakeSourceElementKind.ImplicitConstructor) - val firDelegatedCall = buildDelegatedConstructorCall { + val firDelegatedCall = if (containingClassIsExpectClass) null else buildDelegatedConstructorCall { source = constructorCall ?: constructorSource.fakeElement(KtFakeSourceElementKind.DelegatingConstructorCall) constructedTypeRef = delegatedSuperTypeRef.copyWithNewSourceKind(KtFakeSourceElementKind.ImplicitTypeRef) isThis = false @@ -945,6 +947,7 @@ open class RawFirBuilder( ownerClassHasDefaultConstructor: Boolean ): FirDeclaration { val ktEnumEntry = this@toFirEnumEntry + val containingClassIsExpectClass = hasExpectModifier() || this@RawFirBuilder.context.containerIsExpect return buildEnumEntry { source = toFirSourceElement() moduleData = baseModuleData @@ -953,7 +956,7 @@ open class RawFirBuilder( name = nameAsSafeName status = FirDeclarationStatusImpl(Visibilities.Public, Modality.FINAL).apply { isStatic = true - isExpect = hasExpectModifier() || this@RawFirBuilder.context.containerIsExpect + isExpect = containingClassIsExpectClass } symbol = FirEnumEntrySymbol(callableIdForName(nameAsSafeName)) if (ownerClassHasDefaultConstructor && ktEnumEntry.initializerList == null && @@ -992,7 +995,8 @@ open class RawFirBuilder( correctedEnumSelfTypeRef, delegatedEntrySelfType, owner = ktEnumEntry, - typeParameters + typeParameters, + containingClassIsExpectClass ) // Use ANONYMOUS_OBJECT_NAME for the owner class id for enum entry declarations (see KT-42351) withChildClassName(SpecialNames.ANONYMOUS, forceLocalContext = true, isExpect = false) { diff --git a/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt index 5029835c698..d9fd8902af9 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/enum/constructorInHeaderEnum.fir.kt @@ -2,7 +2,7 @@ // FILE: common.kt expect enum class En(x: Int) { - E1, + E1, E2(42), ;