diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 0372d21e832..174058686f8 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -25332,6 +25332,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); } + @Test + @TestMetadata("platformRedeclarationOfExpect.kt") + public void testPlatformRedeclarationOfExpect() { + runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt"); + } + @Test @TestMetadata("privateTopLevelDeclarations.kt") public void testPrivateTopLevelDeclarations() { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index d8a28665eca..940e7c99b60 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -25332,6 +25332,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); } + @Test + @TestMetadata("platformRedeclarationOfExpect.kt") + public void testPlatformRedeclarationOfExpect() { + runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt"); + } + @Test @TestMetadata("privateTopLevelDeclarations.kt") public void testPrivateTopLevelDeclarations() { 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 0ae46d2b19f..9fdd2c1d3b7 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 @@ -349,6 +349,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); } + @Test + @TestMetadata("platformRedeclarationOfExpect.kt") + public void testPlatformRedeclarationOfExpect() { + runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt"); + } + @Test @TestMetadata("privateTopLevelDeclarations.kt") public void testPrivateTopLevelDeclarations() { 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 fd8be3c3b7b..abd0a72212d 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 @@ -349,6 +349,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); } + @Test + @TestMetadata("platformRedeclarationOfExpect.kt") + public void testPlatformRedeclarationOfExpect() { + runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt"); + } + @Test @TestMetadata("privateTopLevelDeclarations.kt") public void testPrivateTopLevelDeclarations() { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt index 113446997f8..4d4f15b3ded 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt @@ -92,10 +92,19 @@ private val FirBasedSymbol<*>.resolvedStatus else -> null } -internal fun isExpectAndActual(declaration1: FirBasedSymbol<*>, declaration2: FirBasedSymbol<*>): Boolean { - val status1 = declaration1.resolvedStatus ?: return false - val status2 = declaration2.resolvedStatus ?: return false - return (status1.isExpect && status2.isActual) || (status1.isActual && status2.isExpect) +internal fun isExpectAndNonExpect(first: FirBasedSymbol<*>, second: FirBasedSymbol<*>): Boolean { + val firstIsExpect = first.resolvedStatus?.isExpect == true + val secondIsExpect = second.resolvedStatus?.isExpect == true + /* + * this `xor` is equivalent to the following check: + * when { + * !firstIsExpect && secondIsExpect -> true + * firstIsExpect && !secondIsExpect -> true + * else -> false + * } + */ + + return firstIsExpect xor secondIsExpect } private class DeclarationBuckets { @@ -438,9 +447,15 @@ private fun FirClassLikeSymbol<*>.expandedClassWithConstructorsScope(context: Ch } } -private fun shouldCheckForMultiplatformRedeclaration(dependency: FirBasedSymbol<*>, dependent: FirBasedSymbol<*>): Boolean = - dependent.moduleData.allDependsOnDependencies.contains(dependency.moduleData) && - dependency.resolvedStatus?.isExpect != true // ACTUAL_MISSING takes care of this case +private fun shouldCheckForMultiplatformRedeclaration(dependency: FirBasedSymbol<*>, dependent: FirBasedSymbol<*>): Boolean { + if (dependency.moduleData !in dependent.moduleData.allDependsOnDependencies) return false + + /* + * If one of declarations is expect and the other is not expect, ExpectActualChecker will handle this case + * All other cases (both are expect or both are not expect) should be reported as declarations conflict + */ + return !isExpectAndNonExpect(dependency, dependent) +} private fun FirDeclarationCollector>.collectTopLevelConflict( declaration: FirBasedSymbol<*>, @@ -503,7 +518,7 @@ private fun FirDeclarationCollector<*>.areNonConflictingCallables( declaration: FirBasedSymbol<*>, conflicting: FirBasedSymbol<*>, ): Boolean { - if (isExpectAndActual(declaration, conflicting) && declaration.moduleData != conflicting.moduleData) return true + if (isExpectAndNonExpect(declaration, conflicting) && declaration.moduleData != conflicting.moduleData) return true val declarationIsLowPriority = hasLowPriorityAnnotation(declaration.annotations) val conflictingIsLowPriority = hasLowPriorityAnnotation(conflicting.annotations) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt index b7d2de6ed5c..cb98b1ab304 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtFakeSourceElementKind -import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1 import org.jetbrains.kotlin.diagnostics.reportOn @@ -123,7 +122,7 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKin conflictingDeclaration.isPrimaryConstructor && symbols.all { it.isPrimaryConstructor } ) return@forEach - if (symbols.singleOrNull()?.let { isExpectAndActual(conflictingDeclaration, it) } == true) { + if (symbols.singleOrNull()?.let { isExpectAndNonExpect(conflictingDeclaration, it) } == true) { reporter.reportOn(source, FirErrors.EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, conflictingDeclaration, context) return@forEach } diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt index 8ade4922c0a..b6b2bedd4e1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt @@ -1,6 +1,6 @@ // MODULE: m1-common // FILE: common.kt -expect fun main() +expect fun main() // FILE: common2.kt actual fun main() {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.fir.kt new file mode 100644 index 00000000000..12406b8c306 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.fir.kt @@ -0,0 +1,7 @@ +// MODULE: common +// FILE: common.kt +expect class Foo + +// MODULE: main()()(common) +// FILE: test.kt +expect class Foo diff --git a/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt b/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt new file mode 100644 index 00000000000..1e9d2cfd77c --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt @@ -0,0 +1,7 @@ +// MODULE: common +// FILE: common.kt +expect class Foo + +// MODULE: main()()(common) +// FILE: test.kt +expect class Foo diff --git a/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.ll.kt new file mode 100644 index 00000000000..d3077a7dc73 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.ll.kt @@ -0,0 +1,7 @@ +// MODULE: common +// FILE: common.kt +expect class Foo + +// MODULE: main()()(common) +// FILE: test.kt +expect class 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 dba38915059..01e33134a6a 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 @@ -25332,6 +25332,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); } + @Test + @TestMetadata("platformRedeclarationOfExpect.kt") + public void testPlatformRedeclarationOfExpect() { + runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt"); + } + @Test @TestMetadata("privateTopLevelDeclarations.kt") public void testPrivateTopLevelDeclarations() {