From 9d566465e665df93baeccf56e0b426a5a464b06d Mon Sep 17 00:00:00 2001 From: Nikita Bobko Date: Mon, 4 Mar 2024 14:51:21 +0100 Subject: [PATCH] [test] Run checkers in metadata-like style to see what diagnostics metadata compilation omits Review: https://jetbrains.team/p/kt/reviews/14807 Technically, *.ll.kt should have been covering that. But I see that there slight differences --- .../based/LowLevelFirAnalyzerFacade.kt | 9 +- .../expectActualInTheSameModule.fir.kt | 4 +- ...ActualInTheSameModuleDifferentFiles.fir.kt | 4 +- ...alMainInTheSameModuleDifferentFiles.fir.kt | 4 +- .../tests/multiplatform/expectExternal.fir.kt | 16 +-- .../expectOptInAnnotation.fir.kt | 2 +- .../multiplatform/expectOptInAnnotation.ll.kt | 24 ++++ .../tests/multiplatform/expectTailrec.fir.kt | 4 +- .../tests/multiplatform/expectTailrec.ll.kt | 24 ++++ .../actualMethodInExpectClass.fir.kt | 2 +- .../actualMethodInExpectClass.ll.kt | 13 +++ ...tendExpectedClassWithAbstractMember.fir.kt | 6 +- ...xtendExpectedClassWithAbstractMember.ll.kt | 71 ++++++++++++ .../actualizeSubtypeBeforeSupertype.fir.kt | 2 +- .../hmpp/actualizedInPlatformSession.fir.kt | 2 +- .../hmpp/hmppModalityClash.fir.kt | 2 +- ...ppRedeclarationWithExpectActualPair.fir.kt | 2 +- .../hmpp/hmppSignatureClash.fir.kt | 4 +- .../expectAndActualInTheSameModule.fir.kt | 40 +++---- ...ualInTheSameModuleIncompatibilities.fir.kt | 6 +- .../tests/multiplatform/incompatibles.fir.kt | 10 +- .../scopes/multipleDefaultsInhereted.fir.kt | 4 +- .../scopes/multipleDefaultsInhereted.ll.kt | 38 +++++++ .../conflictingHeaderDeclarations.fir.kt | 4 +- .../headerDeclarationWithBody.fir.kt | 4 +- .../topLevelFun/implWithoutHeader.fir.kt | 4 + .../topLevelFun/implWithoutHeader.kt | 1 - .../topLevelFun/implWithoutHeader.ll.kt | 4 + .../handlers/NoFirCompilationErrorsHandler.kt | 2 +- .../fir/handlers/FirDiagnosticsHandler.kt | 105 ++++++++++++++---- 30 files changed, 330 insertions(+), 87 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.ll.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/expectTailrec.ll.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.ll.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.ll.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.ll.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.fir.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.ll.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/LowLevelFirAnalyzerFacade.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/LowLevelFirAnalyzerFacade.kt index e6148ad0d0c..6c6faae44a4 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/LowLevelFirAnalyzerFacade.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/LowLevelFirAnalyzerFacade.kt @@ -10,7 +10,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.collectDiagnosticsForFile import org.jetbrains.kotlin.diagnostics.KtDiagnostic import org.jetbrains.kotlin.fir.AbstractFirAnalyzerFacade -import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.pipeline.FirResult import org.jetbrains.kotlin.fir.pipeline.ModuleCompilerAnalyzedOutput @@ -20,8 +19,10 @@ import org.jetbrains.kotlin.fir.util.listMultimapOf import org.jetbrains.kotlin.fir.util.plusAssign import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact +import org.jetbrains.kotlin.test.frontend.fir.handlers.DiagnosticWithKmpCompilationMode import org.jetbrains.kotlin.test.frontend.fir.handlers.DiagnosticsMap import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDiagnosticCollectorService +import org.jetbrains.kotlin.test.frontend.fir.handlers.KmpCompilationMode import org.jetbrains.kotlin.test.model.TestFile import org.jetbrains.kotlin.test.services.TestServices @@ -60,11 +61,13 @@ open class LowLevelFirAnalyzerFacade( class AnalysisApiFirDiagnosticCollectorService(testServices: TestServices) : FirDiagnosticCollectorService(testServices) { override fun getFrontendDiagnosticsForModule(info: FirOutputArtifact): DiagnosticsMap { - val result = listMultimapOf() + val result = listMultimapOf() for (part in info.partsForDependsOnModules) { val facade = part.firAnalyzerFacade require(facade is LowLevelFirAnalyzerFacade) - result += facade.runCheckers() + result += facade.runCheckers().mapValues { entry -> + entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.LOW_LEVEL_API) } + } } return result } diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModule.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModule.fir.kt index d0f8ee31068..aa833ea9a14 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModule.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModule.fir.kt @@ -1,8 +1,8 @@ // MODULE: m1-common // FILE: common.kt -expect class A -actual class A +expect class A +actual class A // MODULE: m1-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModuleDifferentFiles.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModuleDifferentFiles.fir.kt index a5482c47db8..67f76754f0b 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModuleDifferentFiles.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectActualInTheSameModuleDifferentFiles.fir.kt @@ -1,10 +1,10 @@ // MODULE: m1-common // FILE: common.kt -expect class A +expect class A // FILE: common2.kt -actual class A +actual class A // MODULE: m1-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt index b6b2bedd4e1..b81ede4f0ed 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectActualMainInTheSameModuleDifferentFiles.fir.kt @@ -1,9 +1,9 @@ // MODULE: m1-common // FILE: common.kt -expect fun main() +expect fun main() // FILE: common2.kt -actual fun main() {} +actual fun main() {} // MODULE: m1-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectExternal.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectExternal.fir.kt index d94c059c7c6..e51147ddd3c 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectExternal.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectExternal.fir.kt @@ -1,20 +1,20 @@ // MODULE: m1-common // FILE: common.kt -expect external fun foo() +expect external fun foo() expect fun bar() -expect external var prop: String +expect external var prop: String expect var getAndSet: String - external get - external set + external get + external set -external expect val explicitGetter: String - external get +external expect val explicitGetter: String + external get -expect external class A { - external fun foo() +expect external class A { + external fun foo() fun bar() } diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.fir.kt index 134586ad3db..b7a9274e82f 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.fir.kt @@ -6,7 +6,7 @@ expect annotation class ActualOnly @RequiresOptIn -expect annotation class Both +expect annotation class Both @RequiresOptIn @OptionalExpectation diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.ll.kt new file mode 100644 index 00000000000..134586ad3db --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/expectOptInAnnotation.ll.kt @@ -0,0 +1,24 @@ +// WITH_STDLIB +// MODULE: m1-common +// FILE: common.kt +@file:OptIn(ExperimentalMultiplatform::class) + +expect annotation class ActualOnly + +@RequiresOptIn +expect annotation class Both + +@RequiresOptIn +@OptionalExpectation +expect annotation class MyOptIn + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +@RequiresOptIn +actual annotation class ActualOnly + +@RequiresOptIn +actual annotation class Both + +@RequiresOptIn +actual annotation class MyOptIn diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.fir.kt index 303e1acae44..dc157c67da2 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.fir.kt @@ -1,13 +1,13 @@ // MODULE: m1-common // FILE: common.kt -expect tailrec fun foo(p: Int): Int +expect tailrec fun foo(p: Int): Int expect fun bar(p: Int): Int expect tailrec val notReport: String expect class A { - tailrec fun foo(p: Int): Int + tailrec fun foo(p: Int): Int fun bar(p: Int): Int } diff --git a/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.ll.kt new file mode 100644 index 00000000000..303e1acae44 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/expectTailrec.ll.kt @@ -0,0 +1,24 @@ +// MODULE: m1-common +// FILE: common.kt + +expect tailrec fun foo(p: Int): Int +expect fun bar(p: Int): Int + +expect tailrec val notReport: String + +expect class A { + tailrec fun foo(p: Int): Int + fun bar(p: Int): Int +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt +actual tailrec fun foo(p: Int): Int = foo(p) +actual tailrec fun bar(p: Int): Int = bar(p) + +actual val notReport: String = "123" + +actual class A { + actual tailrec fun foo(p: Int): Int = foo(p) + actual tailrec fun bar(p: Int): Int = bar(p) +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.fir.kt index 7b39694ab86..72b3f59cf6c 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.fir.kt @@ -2,7 +2,7 @@ // FILE: common.kt expect class Foo { - actual fun bar() + actual fun bar() } // MODULE: m1-jvm()()(m1-common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.ll.kt new file mode 100644 index 00000000000..7b39694ab86 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMethodInExpectClass.ll.kt @@ -0,0 +1,13 @@ +// MODULE: m1-common +// FILE: common.kt + +expect class Foo { + actual fun bar() +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: jvm.kt + +actual class Foo { + actual fun bar() {} +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt index 807affa7d9f..225bcd57efe 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt @@ -6,7 +6,7 @@ expect abstract class BaseA() { } expect open class BaseAImpl() : BaseA -class DerivedA1 : BaseAImpl() +class DerivedA1 : BaseAImpl() class DerivedA2 : BaseAImpl() { override fun foo() = super.foo() } @@ -18,7 +18,7 @@ expect interface BaseB { } expect open class BaseBImpl() : BaseB -class DerivedB1 : BaseBImpl() +class DerivedB1 : BaseBImpl() class DerivedB2 : BaseBImpl() { override fun foo() = super.foo() } @@ -30,7 +30,7 @@ expect interface BaseC { } expect abstract class BaseCImpl() : BaseC -class DerivedC1 : BaseCImpl() +class DerivedC1 : BaseCImpl() class DerivedC2 : BaseCImpl() { override fun foo() = super.foo() } diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.ll.kt new file mode 100644 index 00000000000..807affa7d9f --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.ll.kt @@ -0,0 +1,71 @@ +// MODULE: m1-common +// FILE: common.kt + +expect abstract class BaseA() { + abstract fun foo() +} +expect open class BaseAImpl() : BaseA + +class DerivedA1 : BaseAImpl() +class DerivedA2 : BaseAImpl() { + override fun foo() = super.foo() +} + + + +expect interface BaseB { + fun foo() +} +expect open class BaseBImpl() : BaseB + +class DerivedB1 : BaseBImpl() +class DerivedB2 : BaseBImpl() { + override fun foo() = super.foo() +} + + + +expect interface BaseC { + fun foo() +} +expect abstract class BaseCImpl() : BaseC + +class DerivedC1 : BaseCImpl() +class DerivedC2 : BaseCImpl() { + override fun foo() = super.foo() +} + + + +expect interface BaseD { + fun foo() +} +abstract class BaseDImpl() : BaseD { + fun bar() = super.foo() +} + + + +expect interface BaseE { + fun foo() +} +sealed class BaseEImpl() : BaseE { + fun bar() = super.foo() +} + + + +expect interface BaseF { + fun foo() +} +expect class BaseFImpl() : BaseF + + + +expect abstract class BaseG() { + abstract fun foo() +} +expect open class BaseGImpl() : BaseG { + override fun foo() +} +class DerivedG1 : BaseGImpl() diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizeSubtypeBeforeSupertype.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizeSubtypeBeforeSupertype.fir.kt index 6cffeca52dd..f1273963480 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizeSubtypeBeforeSupertype.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizeSubtypeBeforeSupertype.fir.kt @@ -8,7 +8,7 @@ expect open class B() // TARGET_PLATFORM: Common actual class A : B() { // "Nothing to override" in metadata compilation. Unfortunately we don't check metadata compilation in diagnostic tests - override fun foo() {} + override fun foo() {} } actual class C : B() { // Nothing to override in platform compilation. diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizedInPlatformSession.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizedInPlatformSession.fir.kt index 4b34037a882..ad97557983b 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizedInPlatformSession.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/actualizedInPlatformSession.fir.kt @@ -8,7 +8,7 @@ expect class Foo() { // TARGET_PLATFORM: Common expect open class Base() {} -actual class Foo : Base() { +actual class Foo : Base() { } // MODULE: main()()(intermediate) diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppModalityClash.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppModalityClash.fir.kt index 9ee1f3b371a..e5996c646d0 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppModalityClash.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppModalityClash.fir.kt @@ -4,7 +4,7 @@ expect abstract class Foo() { abstract fun foo() } -class Impl : Foo() {} +class Impl : Foo() {} fun common() { Impl().foo() diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppRedeclarationWithExpectActualPair.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppRedeclarationWithExpectActualPair.fir.kt index 1aa8d96f5ba..a646d9df558 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppRedeclarationWithExpectActualPair.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppRedeclarationWithExpectActualPair.fir.kt @@ -12,7 +12,7 @@ class C actual class A -class B +class B expect class C diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppSignatureClash.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppSignatureClash.fir.kt index e2dcf39a96a..c0db1e2aa2f 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppSignatureClash.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/hmppSignatureClash.fir.kt @@ -9,8 +9,8 @@ expect class A { expect class B // K1 EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE false positive -actual class A { - actual fun foo(x: B) = "a" +actual class A { + actual fun foo(x: B) = "a" } // MODULE: main()()(intermediate) diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt index 8bf487f8e46..36c7da015a0 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModule.fir.kt @@ -1,47 +1,47 @@ // MODULE: common // TARGET_PLATFORM: Common -expect class CommonClass { +expect class CommonClass { fun memberFun() val memberProp: Int class Nested inner class Inner } -actual class CommonClass { - actual fun memberFun() {} - actual val memberProp: Int = 42 - actual class Nested - actual inner class Inner +actual class CommonClass { + actual fun memberFun() {} + actual val memberProp: Int = 42 + actual class Nested + actual inner class Inner } -expect fun commonFun() -actual fun commonFun() {} +expect fun commonFun() +actual fun commonFun() {} -expect val commonProperty: String -actual val commonProperty: String +expect val commonProperty: String +actual val commonProperty: String get() = "hello" // MODULE: intermediate()()(common) // TARGET_PLATFORM: Common -expect class IntermediateClass { +expect class IntermediateClass { fun memberFun() val memberProp: Int class Nested inner class Inner } -actual class IntermediateClass { - actual fun memberFun() {} - actual val memberProp: Int = 42 - actual class Nested - actual inner class Inner +actual class IntermediateClass { + actual fun memberFun() {} + actual val memberProp: Int = 42 + actual class Nested + actual inner class Inner } -expect fun intermediateFun() -actual fun intermediateFun() {} +expect fun intermediateFun() +actual fun intermediateFun() {} -expect val intermediateProperty: String -actual val intermediateProperty: String +expect val intermediateProperty: String +actual val intermediateProperty: String get() = "hello" // MODULE: main()()(intermediate) diff --git a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt index 70bf0389124..c67afe0317d 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/expectAndActualInTheSameModuleIncompatibilities.fir.kt @@ -5,7 +5,7 @@ expect fun parameterCount() fun parameterCount(p: String) {} expect fun parameterCount2() -actual fun parameterCount2(p: String) {} +actual fun parameterCount2(p: String) {} expect fun callableKind(): Int val callableKind: Int = 1 @@ -13,10 +13,10 @@ val callableKind: Int = 1 expect fun typeParameterCount() fun typeParameterCount() {} -expect enum class EnumEntries { +expect enum class EnumEntries { ONE, TWO; } -actual enum class EnumEntries { +actual enum class EnumEntries { ONE; } diff --git a/compiler/testData/diagnostics/tests/multiplatform/incompatibles.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/incompatibles.fir.kt index fa8e317cf09..4c7ff0c24d5 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/incompatibles.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/incompatibles.fir.kt @@ -1,12 +1,12 @@ -header impl class First +header impl class First header expect class Second -header actual class Third +header actual class Third -impl expect class Fourth +impl expect class Fourth -impl actual class Fifth +impl actual class Fifth -expect actual class Sixth +expect actual class Sixth diff --git a/compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.fir.kt index b88b91af964..ceab9a166e1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.fir.kt @@ -16,8 +16,8 @@ expect interface I2 { override fun foo(x: Int) } -interface CommonInterface : I1, I2 { - override fun foo(x: Int) +interface CommonInterface : I1, I2 { + override fun foo(x: Int) } // MODULE: m1-jvm()()(m1-common) diff --git a/compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.ll.kt new file mode 100644 index 00000000000..b88b91af964 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/scopes/multipleDefaultsInhereted.ll.kt @@ -0,0 +1,38 @@ +// LANGUAGE: +MultiPlatformProjects + +// MODULE: m1-common +// FILE: common.kt +expect interface I1 { + fun foo(x: Int = 1) + fun bar(x: Int = 1) +} + +expect interface I2 { + fun foo(x: Int = 2) + fun bar(x: Int = 2) +} + +expect interface ExpectInterface : I1, I2 { + override fun foo(x: Int) +} + +interface CommonInterface : I1, I2 { + override fun foo(x: Int) +} + +// MODULE: m1-jvm()()(m1-common) +// FILE: main.kt + +actual interface I1 { + actual fun foo(x: Int) + actual fun bar(x: Int) +} + +actual interface I2 { + actual fun foo(x: Int) + actual fun bar(x: Int) +} + +actual interface ExpectInterface : I1, I2 { + actual override fun foo(x: Int) +} diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/conflictingHeaderDeclarations.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/conflictingHeaderDeclarations.fir.kt index f91c08899de..6fa399753d1 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/conflictingHeaderDeclarations.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/conflictingHeaderDeclarations.fir.kt @@ -1,7 +1,7 @@ // MODULE: m1-common // FILE: common.kt -expect fun foo() -expect fun foo() +expect fun foo() +expect fun foo() expect fun foo(x: Int) diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/headerDeclarationWithBody.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/headerDeclarationWithBody.fir.kt index ff32fd4583d..a6680d0f9ab 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/headerDeclarationWithBody.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/headerDeclarationWithBody.fir.kt @@ -1,8 +1,8 @@ // MODULE: m1-common // FILE: common.kt -expect fun foo() +expect fun foo() -expect fun foo() {} +expect fun foo() {} expect fun bar() {} diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.fir.kt new file mode 100644 index 00000000000..be670e10d22 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.fir.kt @@ -0,0 +1,4 @@ +// MODULE: m1-jvm +// FILE: jvm.kt + +actual fun foo() { } diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.kt index a47bd1be4f0..040b35189fe 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // MODULE: m1-jvm // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.ll.kt b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.ll.kt new file mode 100644 index 00000000000..040b35189fe --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/topLevelFun/implWithoutHeader.ll.kt @@ -0,0 +1,4 @@ +// MODULE: m1-jvm +// FILE: jvm.kt + +actual fun foo() { } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/NoFirCompilationErrorsHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/NoFirCompilationErrorsHandler.kt index ddefb736fe6..9f271fa50f9 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/NoFirCompilationErrorsHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/NoFirCompilationErrorsHandler.kt @@ -36,7 +36,7 @@ class NoFirCompilationErrorsHandler(testServices: TestServices) : FirAnalysisHan val diagnosticsPerFile = testServices.firDiagnosticCollectorService.getFrontendDiagnosticsForModule(info) for ((firFile, diagnostics) in diagnosticsPerFile) { - for (diagnostic in diagnostics) { + for (diagnostic in diagnostics.map { it.diagnostic }) { if (diagnostic.severity == Severity.ERROR) { hasError = true if (!ignoreErrors) { diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt index a8937db06cc..186660836b8 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt @@ -11,6 +11,9 @@ import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFa import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1 import org.jetbrains.kotlin.checkers.utils.TypeOfCall import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.config.AnalysisFlag +import org.jetbrains.kotlin.config.AnalysisFlags +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.diagnostics.* import org.jetbrains.kotlin.diagnostics.rendering.Renderers import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory @@ -135,22 +138,26 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes val firFile = info.mainFirFiles[file] ?: continue var diagnostics = frontendDiagnosticsPerFile[firFile] if (AdditionalFilesDirectives.CHECK_TYPE in currentModule.directives) { - diagnostics = diagnostics.filter { it.factory.name != FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name } + diagnostics = diagnostics.filter { it.diagnostic.factory.name != FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name } } if (LanguageSettingsDirectives.API_VERSION in currentModule.directives) { - diagnostics = diagnostics.filter { it.factory.name != FirErrors.NEWER_VERSION_IN_SINCE_KOTLIN.name } + diagnostics = diagnostics.filter { it.diagnostic.factory.name != FirErrors.NEWER_VERSION_IN_SINCE_KOTLIN.name } } - val diagnosticsMetadataInfos = - diagnostics.diagnosticCodeMetaInfos( - currentModule, file, - diagnosticsService, globalMetadataInfoHandler, - lightTreeEnabled, lightTreeComparingModeEnabled, - forceRenderArguments, - ) + val diagnosticsMetadataInfos = diagnostics + .groupBy({ it.kmpCompilationMode }, { it.diagnostic }) + .flatMap { (kmpCompilation, diagnostics) -> + diagnostics.diagnosticCodeMetaInfos( + currentModule, file, + diagnosticsService, globalMetadataInfoHandler, + lightTreeEnabled, lightTreeComparingModeEnabled, + forceRenderArguments, + kmpCompilation + ) + } globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos) collectSyntaxDiagnostics(currentModule, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments) collectDebugInfoDiagnostics(currentModule, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled) - fullDiagnosticsRenderer.storeFullDiagnosticRender(module, diagnostics, file) + fullDiagnosticsRenderer.storeFullDiagnosticRender(module, diagnostics.map { it.diagnostic }, file) } } } @@ -170,7 +177,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes .toMetaInfos( module, testFile, - globalMetadataInfoHandler1 = globalMetadataInfoHandler, + globalMetadataInfoHandler = globalMetadataInfoHandler, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments, @@ -185,7 +192,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes it.toMetaInfos( module, testFile, - globalMetadataInfoHandler1 = globalMetadataInfoHandler, + globalMetadataInfoHandler = globalMetadataInfoHandler, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments, @@ -389,6 +396,7 @@ fun List.diagnosticCodeMetaInfos( lightTreeEnabled: Boolean, lightTreeComparingModeEnabled: Boolean, forceRenderArguments: Boolean = false, + kmpCompilationMode: KmpCompilationMode? = null ): List = flatMap { diagnostic -> if (!diagnosticsService.shouldRenderDiagnostic( module, @@ -404,6 +412,7 @@ fun List.diagnosticCodeMetaInfos( lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments, + kmpCompilationMode ) } @@ -587,13 +596,14 @@ class PsiLightTreeMetaInfoProcessor(testServices: TestServices) : AbstractTwoAtt fun KtDiagnostic.toMetaInfos( module: TestModule, file: TestFile, - globalMetadataInfoHandler1: GlobalMetadataInfoHandler, + globalMetadataInfoHandler: GlobalMetadataInfoHandler, lightTreeEnabled: Boolean, lightTreeComparingModeEnabled: Boolean, - forceRenderArguments: Boolean = false + forceRenderArguments: Boolean = false, + kmpCompilationMode: KmpCompilationMode? = null ): List = textRanges.map { range -> val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs, range) - val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler1.getExistingMetaInfosForActualMetadata(file, metaInfo) + val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo) .any { it.description != null } if (shouldRenderArguments) { metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs) @@ -611,10 +621,36 @@ fun KtDiagnostic.toMetaInfos( else -> error("Should not be here") } } + if (kmpCompilationMode == KmpCompilationMode.METADATA) { + metaInfo.attributes += "METADATA" + } metaInfo } -typealias DiagnosticsMap = Multimap> +typealias DiagnosticsMap = Multimap> + +data class DiagnosticWithKmpCompilationMode(val diagnostic: KtDiagnostic, val kmpCompilationMode: KmpCompilationMode) + +/** + * There are two types of checkers (represented by [MppCheckerKind]): + * 1. Common checker. When a common checker analyzes a code, the checker doesn't see what are the actualizations for the `expect` declarations. + * 2. Platform checker. When a platform checker analyzes a code, the checker sees what are the actualizations for the `expect` declarations + * instead of the `expect` declarations themselves. + * + * KMP is compiled in two different modes (represented by [KmpCompilationMode]): + * 1. Metadata compilation. Metadata compilation compiles only non-platform fragments, + * and it runs both common and platform checkers on those non-platform fragments. + * But in testData, we only check diagnostics from platform checkers in non-platform fragments. + * Common checkers in non-platform targets are tested by "platform compilation" anyway + * 2. Platform compilation. Platform compilation compiles all the fragments (non-platform and platform), + * and it runs common checkers on non-platform fragments, + * and it runs platform checkers on platform fragments + * + * Please don't confuse "platform checker" and "platform compilation" + */ +enum class KmpCompilationMode { + METADATA, PLATFORM, LOW_LEVEL_API +} open class FirDiagnosticCollectorService(val testServices: TestServices) : TestService { private val cache: MutableMap = mutableMapOf() @@ -624,14 +660,14 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS } fun containsErrors(info: FirOutputArtifact): Boolean { - return getFrontendDiagnosticsForModule(info).values.any { it.severity == Severity.ERROR } + return getFrontendDiagnosticsForModule(info).values.any { it.diagnostic.severity == Severity.ERROR } } - private fun computeDiagnostics(info: FirOutputArtifact): ListMultimap { + private fun computeDiagnostics(info: FirOutputArtifact): ListMultimap { val allFiles = info.partsForDependsOnModules.flatMap { it.firFiles.values } val platformPart = info.partsForDependsOnModules.last() val lazyDeclarationResolver = platformPart.session.lazyDeclarationResolver - val result = listMultimapOf() + val result = listMultimapOf() lazyDeclarationResolver.disableLazyResolveContractChecksInside { result += platformPart.session.runCheckers( @@ -639,7 +675,7 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS allFiles, DiagnosticReporterFactory.createPendingReporter(), mppCheckerKind = MppCheckerKind.Platform - ) + ).mapValues { entry -> entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.PLATFORM) } } for (part in info.partsForDependsOnModules) { result += part.session.runCheckers( @@ -647,7 +683,18 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS part.firFiles.values, DiagnosticReporterFactory.createPendingReporter(), mppCheckerKind = MppCheckerKind.Common - ) + ).mapValues { entry -> entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.PLATFORM) } } + } + + for (part in info.partsForDependsOnModules.dropLast(1)) { + part.session.turnOnMetadataCompilationAnalysisFlag { + result += part.session.runCheckers( + part.firAnalyzerFacade.scopeSession, + part.firFiles.values, + DiagnosticReporterFactory.createPendingReporter(), + mppCheckerKind = MppCheckerKind.Platform + ).mapValues { entry -> entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.METADATA) } } + } } } @@ -655,4 +702,20 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS } } +@OptIn(SessionConfiguration::class) +private fun FirSession.turnOnMetadataCompilationAnalysisFlag(body: () -> Unit) { + val originalLv = languageVersionSettings + val lv = object : LanguageVersionSettings by originalLv { + override fun getFlag(flag: AnalysisFlag): T = + @Suppress("UNCHECKED_CAST") // UNCHECKED_CAST is fine because metadataCompilation is boolean flag + if (flag == AnalysisFlags.metadataCompilation) true as T else originalLv.getFlag(flag) + } + register(FirLanguageSettingsComponent::class, FirLanguageSettingsComponent(lv)) + try { + body() + } finally { + register(FirLanguageSettingsComponent::class, FirLanguageSettingsComponent(originalLv)) + } +} + val TestServices.firDiagnosticCollectorService: FirDiagnosticCollectorService by TestServices.testServiceAccessor()