[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"); runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt");
} }
@Test
@TestMetadata("platformRedeclarationOfExpect.kt")
public void testPlatformRedeclarationOfExpect() {
runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt");
}
@Test @Test
@TestMetadata("privateTopLevelDeclarations.kt") @TestMetadata("privateTopLevelDeclarations.kt")
public void testPrivateTopLevelDeclarations() { public void testPrivateTopLevelDeclarations() {
@@ -25332,6 +25332,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt");
} }
@Test
@TestMetadata("platformRedeclarationOfExpect.kt")
public void testPlatformRedeclarationOfExpect() {
runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt");
}
@Test @Test
@TestMetadata("privateTopLevelDeclarations.kt") @TestMetadata("privateTopLevelDeclarations.kt")
public void testPrivateTopLevelDeclarations() { public void testPrivateTopLevelDeclarations() {
@@ -349,6 +349,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt");
} }
@Test
@TestMetadata("platformRedeclarationOfExpect.kt")
public void testPlatformRedeclarationOfExpect() {
runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt");
}
@Test @Test
@TestMetadata("privateTopLevelDeclarations.kt") @TestMetadata("privateTopLevelDeclarations.kt")
public void testPrivateTopLevelDeclarations() { public void testPrivateTopLevelDeclarations() {
@@ -349,6 +349,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt"); runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt");
} }
@Test
@TestMetadata("platformRedeclarationOfExpect.kt")
public void testPlatformRedeclarationOfExpect() {
runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt");
}
@Test @Test
@TestMetadata("privateTopLevelDeclarations.kt") @TestMetadata("privateTopLevelDeclarations.kt")
public void testPrivateTopLevelDeclarations() { public void testPrivateTopLevelDeclarations() {
@@ -92,10 +92,19 @@ private val FirBasedSymbol<*>.resolvedStatus
else -> null else -> null
} }
internal fun isExpectAndActual(declaration1: FirBasedSymbol<*>, declaration2: FirBasedSymbol<*>): Boolean { internal fun isExpectAndNonExpect(first: FirBasedSymbol<*>, second: FirBasedSymbol<*>): Boolean {
val status1 = declaration1.resolvedStatus ?: return false val firstIsExpect = first.resolvedStatus?.isExpect == true
val status2 = declaration2.resolvedStatus ?: return false val secondIsExpect = second.resolvedStatus?.isExpect == true
return (status1.isExpect && status2.isActual) || (status1.isActual && status2.isExpect) /*
* this `xor` is equivalent to the following check:
* when {
* !firstIsExpect && secondIsExpect -> true
* firstIsExpect && !secondIsExpect -> true
* else -> false
* }
*/
return firstIsExpect xor secondIsExpect
} }
private class DeclarationBuckets { private class DeclarationBuckets {
@@ -438,9 +447,15 @@ private fun FirClassLikeSymbol<*>.expandedClassWithConstructorsScope(context: Ch
} }
} }
private fun shouldCheckForMultiplatformRedeclaration(dependency: FirBasedSymbol<*>, dependent: FirBasedSymbol<*>): Boolean = private fun shouldCheckForMultiplatformRedeclaration(dependency: FirBasedSymbol<*>, dependent: FirBasedSymbol<*>): Boolean {
dependent.moduleData.allDependsOnDependencies.contains(dependency.moduleData) && if (dependency.moduleData !in dependent.moduleData.allDependsOnDependencies) return false
dependency.resolvedStatus?.isExpect != true // ACTUAL_MISSING takes care of this case
/*
* 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( private fun FirDeclarationCollector<FirBasedSymbol<*>>.collectTopLevelConflict(
declaration: FirBasedSymbol<*>, declaration: FirBasedSymbol<*>,
@@ -503,7 +518,7 @@ private fun FirDeclarationCollector<*>.areNonConflictingCallables(
declaration: FirBasedSymbol<*>, declaration: FirBasedSymbol<*>,
conflicting: FirBasedSymbol<*>, conflicting: FirBasedSymbol<*>,
): Boolean { ): 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 declarationIsLowPriority = hasLowPriorityAnnotation(declaration.annotations)
val conflictingIsLowPriority = hasLowPriorityAnnotation(conflicting.annotations) val conflictingIsLowPriority = hasLowPriorityAnnotation(conflicting.annotations)
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1 import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
@@ -123,7 +122,7 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKin
conflictingDeclaration.isPrimaryConstructor && symbols.all { it.isPrimaryConstructor } conflictingDeclaration.isPrimaryConstructor && symbols.all { it.isPrimaryConstructor }
) return@forEach ) 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) reporter.reportOn(source, FirErrors.EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, conflictingDeclaration, context)
return@forEach return@forEach
} }
@@ -1,6 +1,6 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
expect fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>main<!>() <!CONFLICTING_OVERLOADS!>expect fun main()<!>
// FILE: common2.kt // FILE: common2.kt
<!CONFLICTING_OVERLOADS!>actual fun <!ACTUAL_WITHOUT_EXPECT!>main<!>()<!> {} <!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"); runTest("compiler/testData/diagnostics/tests/multiplatform/optionalExpectationDiagnostics.kt");
} }
@Test
@TestMetadata("platformRedeclarationOfExpect.kt")
public void testPlatformRedeclarationOfExpect() {
runTest("compiler/testData/diagnostics/tests/multiplatform/platformRedeclarationOfExpect.kt");
}
@Test @Test
@TestMetadata("privateTopLevelDeclarations.kt") @TestMetadata("privateTopLevelDeclarations.kt")
public void testPrivateTopLevelDeclarations() { public void testPrivateTopLevelDeclarations() {