diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java index df0608137fc..654666d009a 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated.java @@ -758,6 +758,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/differentOrder.kt"); } + @Test + @TestMetadata("enumEntries.kt") + public void testEnumEntries() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.kt"); + } + @Test @TestMetadata("fakeOverrides.kt") public void testFakeOverrides() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java index 113dd96bc21..0907f1cfa8b 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendMPPDiagnosticsWithPsiTestGenerated.java @@ -758,6 +758,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/differentOrder.kt"); } + @Test + @TestMetadata("enumEntries.kt") + public void testEnumEntries() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.kt"); + } + @Test @TestMetadata("fakeOverrides.kt") public void testFakeOverrides() throws Exception { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt index f6ca6b5bb0b..728c4d7cc52 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/mpp/FirExpectActualMatchingContextImpl.kt @@ -238,6 +238,10 @@ class FirExpectActualMatchingContextImpl private constructor( return asSymbol().fir.collectEnumEntries().map { it.name } } + override fun RegularClassSymbolMarker.collectEnumEntries(): List { + return asSymbol().fir.collectEnumEntries().map { it.symbol } + } + override val CallableSymbolMarker.dispatchReceiverType: SimpleTypeMarker? get() = asSymbol().dispatchReceiverType override val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker? diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt index 538182bca31..5dccfd2cd45 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrExpectActualMatchingContext.kt @@ -267,6 +267,10 @@ internal abstract class IrExpectActualMatchingContext( return asIr().declarations.filterIsInstance().map { it.name } } + override fun RegularClassSymbolMarker.collectEnumEntries(): List { + return asIr().declarations.filterIsInstance().map { it.symbol } + } + override val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker? get() = (asIr().parent as? IrClass)?.defaultType diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt index 20b55765a0e..e499ab443c3 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt @@ -5,8 +5,10 @@ package org.jetbrains.kotlin.resolve.calls.mpp +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.mpp.* import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility @@ -80,6 +82,9 @@ object AbstractExpectActualAnnotationMatchChecker { if (checkClassScopesForAnnotationCompatibility) { checkAnnotationsInClassMemberScope(expectSymbol, actualSymbol)?.let { return it } } + if (expectSymbol.classKind == ClassKind.ENUM_CLASS && actualSymbol.classKind == ClassKind.ENUM_CLASS) { + checkAnnotationsOnEnumEntries(expectSymbol, actualSymbol)?.let { return it } + } return null } @@ -170,4 +175,27 @@ object AbstractExpectActualAnnotationMatchChecker { } return null } + + context (ExpectActualMatchingContext<*>) + private fun checkAnnotationsOnEnumEntries( + expectClassSymbol: RegularClassSymbolMarker, + actualClassSymbol: RegularClassSymbolMarker, + ): Incompatibility? { + fun DeclarationSymbolMarker.getEnumEntryName(): Name = + when (this) { + is CallableSymbolMarker -> callableId.callableName + is RegularClassSymbolMarker -> classId.shortClassName + else -> error("Unexpected type $this") + } + + val expectEnumEntries = expectClassSymbol.collectEnumEntries() + val actualEnumEntriesByName = actualClassSymbol.collectEnumEntries().associateBy { it.getEnumEntryName() } + + for (expectEnumEntry in expectEnumEntries) { + val actualEnumEntry = actualEnumEntriesByName[expectEnumEntry.getEnumEntryName()] ?: continue + areAnnotationsSetOnDeclarationsCompatible(expectEnumEntry, actualEnumEntry) + ?.let { return it } + } + return null + } } \ No newline at end of file diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt index b790ee5d452..b1d17171544 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt @@ -111,6 +111,7 @@ interface ExpectActualMatchingContext : TypeSystemC fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List fun RegularClassSymbolMarker.collectEnumEntryNames(): List + fun RegularClassSymbolMarker.collectEnumEntries(): List val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker? val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker? diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt index b35ff1a079e..79648708387 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/ClassicExpectActualMatchingContext.kt @@ -176,11 +176,14 @@ class ClassicExpectActualMatchingContext( } override fun RegularClassSymbolMarker.collectEnumEntryNames(): List { + return collectEnumEntries().map { it.name } + } + + override fun RegularClassSymbolMarker.collectEnumEntries(): List { return asDescriptor() .unsubstitutedMemberScope .getDescriptorsFiltered() .filter(DescriptorUtils::isEnumEntry) - .map { it.name } } override val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker? diff --git a/compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.fir.kt new file mode 100644 index 00000000000..9a8b1622a30 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.fir.kt @@ -0,0 +1,15 @@ +// MODULE: m1-common +// FILE: common.kt +annotation class Ann + +expect enum class E { + @Ann + FOO, + MISSING_ON_ACTUAL +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +actual enum class E { + FOO +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.kt b/compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.kt new file mode 100644 index 00000000000..a0a413b35c9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.kt @@ -0,0 +1,15 @@ +// MODULE: m1-common +// FILE: common.kt +annotation class Ann + +expect enum class E { + @Ann + FOO, + MISSING_ON_ACTUAL +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +actual enum class E { + FOO +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 29503239e0a..404b19e8936 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -23527,6 +23527,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/differentOrder.kt"); } + @Test + @TestMetadata("enumEntries.kt") + public void testEnumEntries() throws Exception { + runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/enumEntries.kt"); + } + @Test @TestMetadata("fakeOverrides.kt") public void testFakeOverrides() throws Exception {