[FIR] Properly reports about conflicts in MPP setup

Checker for conflicting declarations will now check for the following
  scenarios too
- two expect declarations in different modules
- actual and non-expect declarations in different modules

^KT-63826 Fixed
This commit is contained in:
Dmitriy Novozhilov
2024-02-27 14:04:42 +02:00
committed by Space Team
parent 2febc807a1
commit 6cfe81de50
11 changed files with 76 additions and 11 deletions
@@ -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() {
@@ -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() {
@@ -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() {
@@ -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() {
@@ -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<FirBasedSymbol<*>>.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)
@@ -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
}
@@ -1,6 +1,6 @@
// MODULE: m1-common
// FILE: common.kt
expect fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>main<!>()
<!CONFLICTING_OVERLOADS!>expect fun main()<!>
// FILE: common2.kt
<!CONFLICTING_OVERLOADS!>actual fun <!ACTUAL_WITHOUT_EXPECT!>main<!>()<!> {}
@@ -0,0 +1,7 @@
// MODULE: common
// FILE: common.kt
expect class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>Foo<!>
// MODULE: main()()(common)
// FILE: test.kt
expect class Foo
@@ -0,0 +1,7 @@
// MODULE: common
// FILE: common.kt
expect class <!NO_ACTUAL_FOR_EXPECT, NO_ACTUAL_FOR_EXPECT{JVM}, PACKAGE_OR_CLASSIFIER_REDECLARATION{JVM}!>Foo<!>
// MODULE: main()()(common)
// FILE: test.kt
expect class <!NO_ACTUAL_FOR_EXPECT, PACKAGE_OR_CLASSIFIER_REDECLARATION!>Foo<!>
@@ -0,0 +1,7 @@
// MODULE: common
// FILE: common.kt
expect class Foo
// MODULE: main()()(common)
// FILE: test.kt
expect class Foo
@@ -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() {