[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
This commit is contained in:
Nikita Bobko
2024-03-04 14:51:21 +01:00
committed by Space Team
parent ab069cb7f4
commit 9d566465e6
30 changed files with 330 additions and 87 deletions
@@ -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.analysis.low.level.api.fir.api.collectDiagnosticsForFile
import org.jetbrains.kotlin.diagnostics.KtDiagnostic import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.fir.AbstractFirAnalyzerFacade 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.declarations.FirFile
import org.jetbrains.kotlin.fir.pipeline.FirResult import org.jetbrains.kotlin.fir.pipeline.FirResult
import org.jetbrains.kotlin.fir.pipeline.ModuleCompilerAnalyzedOutput 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.fir.util.plusAssign
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact 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.DiagnosticsMap
import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDiagnosticCollectorService 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.model.TestFile
import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.TestServices
@@ -60,11 +61,13 @@ open class LowLevelFirAnalyzerFacade(
class AnalysisApiFirDiagnosticCollectorService(testServices: TestServices) : FirDiagnosticCollectorService(testServices) { class AnalysisApiFirDiagnosticCollectorService(testServices: TestServices) : FirDiagnosticCollectorService(testServices) {
override fun getFrontendDiagnosticsForModule(info: FirOutputArtifact): DiagnosticsMap { override fun getFrontendDiagnosticsForModule(info: FirOutputArtifact): DiagnosticsMap {
val result = listMultimapOf<FirFile, KtDiagnostic>() val result = listMultimapOf<FirFile, DiagnosticWithKmpCompilationMode>()
for (part in info.partsForDependsOnModules) { for (part in info.partsForDependsOnModules) {
val facade = part.firAnalyzerFacade val facade = part.firAnalyzerFacade
require(facade is LowLevelFirAnalyzerFacade) require(facade is LowLevelFirAnalyzerFacade)
result += facade.runCheckers() result += facade.runCheckers().mapValues { entry ->
entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.LOW_LEVEL_API) }
}
} }
return result return result
} }
@@ -1,8 +1,8 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>A<!><!> <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>A<!><!>
actual class <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>A<!> actual class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>A<!>
// MODULE: m1-jvm()()(m1-common) // MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt // FILE: jvm.kt
@@ -1,10 +1,10 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class A<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>A<!><!>
// FILE: common2.kt // FILE: common2.kt
actual class <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>A<!> actual class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>A<!>
// MODULE: m1-jvm()()(m1-common) // MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt // FILE: jvm.kt
@@ -1,9 +1,9 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
<!CONFLICTING_OVERLOADS!>expect fun main()<!> <!CONFLICTING_OVERLOADS!>expect fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>main<!>()<!>
// FILE: common2.kt // FILE: common2.kt
<!CONFLICTING_OVERLOADS!>actual fun <!ACTUAL_WITHOUT_EXPECT!>main<!>()<!> {} <!CONFLICTING_OVERLOADS!>actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>main<!>()<!> {}
// MODULE: m1-jvm()()(m1-common) // MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt // FILE: jvm.kt
@@ -1,20 +1,20 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
expect <!EXPECTED_EXTERNAL_DECLARATION!>external<!> fun foo() expect <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}!>external<!> fun foo()
expect fun bar() expect fun bar()
expect <!EXPECTED_EXTERNAL_DECLARATION, WRONG_MODIFIER_TARGET!>external<!> var prop: String expect <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}, WRONG_MODIFIER_TARGET!>external<!> var prop: String
expect var getAndSet: String expect var getAndSet: String
<!EXPECTED_EXTERNAL_DECLARATION!>external<!> get <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}!>external<!> get
<!EXPECTED_EXTERNAL_DECLARATION!>external<!> set <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}!>external<!> set
<!EXPECTED_EXTERNAL_DECLARATION, WRONG_MODIFIER_TARGET!>external<!> expect val explicitGetter: String <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}, WRONG_MODIFIER_TARGET!>external<!> expect val explicitGetter: String
<!EXPECTED_EXTERNAL_DECLARATION!>external<!> get <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}!>external<!> get
expect <!EXPECTED_EXTERNAL_DECLARATION, WRONG_MODIFIER_TARGET!>external<!> class A { expect <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}, WRONG_MODIFIER_TARGET!>external<!> class A {
<!EXPECTED_EXTERNAL_DECLARATION!>external<!> fun foo() <!EXPECTED_EXTERNAL_DECLARATION, EXPECTED_EXTERNAL_DECLARATION{METADATA}!>external<!> fun foo()
fun bar() fun bar()
} }
@@ -6,7 +6,7 @@
expect annotation class ActualOnly expect annotation class ActualOnly
@RequiresOptIn @RequiresOptIn
<!EXPECT_ACTUAL_OPT_IN_ANNOTATION!>expect<!> annotation class Both <!EXPECT_ACTUAL_OPT_IN_ANNOTATION, EXPECT_ACTUAL_OPT_IN_ANNOTATION{METADATA}!>expect<!> annotation class Both
@RequiresOptIn @RequiresOptIn
@OptionalExpectation @OptionalExpectation
@@ -0,0 +1,24 @@
// WITH_STDLIB
// MODULE: m1-common
// FILE: common.kt
@file:OptIn(ExperimentalMultiplatform::class)
expect annotation class ActualOnly
@RequiresOptIn
<!EXPECT_ACTUAL_OPT_IN_ANNOTATION!>expect<!> annotation class Both
@RequiresOptIn
@OptionalExpectation
expect annotation class MyOptIn
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@RequiresOptIn
<!EXPECT_ACTUAL_OPT_IN_ANNOTATION!>actual<!> annotation class ActualOnly
@RequiresOptIn
<!EXPECT_ACTUAL_OPT_IN_ANNOTATION!>actual<!> annotation class Both
@RequiresOptIn
actual annotation class MyOptIn
@@ -1,13 +1,13 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
expect <!EXPECTED_TAILREC_FUNCTION, NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo(p: Int): Int expect <!EXPECTED_TAILREC_FUNCTION, EXPECTED_TAILREC_FUNCTION{METADATA}, NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo(p: Int): Int
expect fun bar(p: Int): Int expect fun bar(p: Int): Int
expect <!WRONG_MODIFIER_TARGET!>tailrec<!> val notReport: String expect <!WRONG_MODIFIER_TARGET!>tailrec<!> val notReport: String
expect class A { expect class A {
<!EXPECTED_TAILREC_FUNCTION, NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo(p: Int): Int <!EXPECTED_TAILREC_FUNCTION, EXPECTED_TAILREC_FUNCTION{METADATA}, NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo(p: Int): Int
fun bar(p: Int): Int fun bar(p: Int): Int
} }
@@ -0,0 +1,24 @@
// MODULE: m1-common
// FILE: common.kt
expect <!EXPECTED_TAILREC_FUNCTION, NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo(p: Int): Int
expect fun bar(p: Int): Int
expect <!WRONG_MODIFIER_TARGET!>tailrec<!> val notReport: String
expect class A {
<!EXPECTED_TAILREC_FUNCTION, NO_TAIL_CALLS_FOUND!>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)
}
@@ -2,7 +2,7 @@
// FILE: common.kt // FILE: common.kt
expect class Foo { expect class Foo {
actual fun <!ACTUAL_WITHOUT_EXPECT!>bar<!>() actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>bar<!>()
} }
// MODULE: m1-jvm()()(m1-common) // MODULE: m1-jvm()()(m1-common)
@@ -0,0 +1,13 @@
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
actual fun <!ACTUAL_WITHOUT_EXPECT!>bar<!>()
}
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
actual class Foo {
actual fun bar() {}
}
@@ -6,7 +6,7 @@ expect abstract class BaseA() {
} }
expect open <!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class BaseAImpl<!>() : BaseA expect open <!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class BaseAImpl<!>() : BaseA
<!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class DerivedA1<!> : BaseAImpl() <!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED, ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED{METADATA}!>class DerivedA1<!> : BaseAImpl()
class DerivedA2 : BaseAImpl() { class DerivedA2 : BaseAImpl() {
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>() override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
} }
@@ -18,7 +18,7 @@ expect interface BaseB {
} }
expect open <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class BaseBImpl<!>() : BaseB expect open <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class BaseBImpl<!>() : BaseB
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class DerivedB1<!> : BaseBImpl() <!ABSTRACT_MEMBER_NOT_IMPLEMENTED, ABSTRACT_MEMBER_NOT_IMPLEMENTED{METADATA}!>class DerivedB1<!> : BaseBImpl()
class DerivedB2 : BaseBImpl() { class DerivedB2 : BaseBImpl() {
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>() override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
} }
@@ -30,7 +30,7 @@ expect interface BaseC {
} }
expect abstract class BaseCImpl() : BaseC expect abstract class BaseCImpl() : BaseC
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class DerivedC1<!> : BaseCImpl() <!ABSTRACT_MEMBER_NOT_IMPLEMENTED, ABSTRACT_MEMBER_NOT_IMPLEMENTED{METADATA}!>class DerivedC1<!> : BaseCImpl()
class DerivedC2 : BaseCImpl() { class DerivedC2 : BaseCImpl() {
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>() override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
} }
@@ -0,0 +1,71 @@
// MODULE: m1-common
// FILE: common.kt
expect abstract class BaseA() {
abstract fun foo()
}
expect open <!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class BaseAImpl<!>() : BaseA
<!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED!>class DerivedA1<!> : BaseAImpl()
class DerivedA2 : BaseAImpl() {
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
}
expect interface BaseB {
fun foo()
}
expect open <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class BaseBImpl<!>() : BaseB
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class DerivedB1<!> : BaseBImpl()
class DerivedB2 : BaseBImpl() {
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
}
expect interface BaseC {
fun foo()
}
expect abstract class BaseCImpl() : BaseC
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class DerivedC1<!> : BaseCImpl()
class DerivedC2 : BaseCImpl() {
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
}
expect interface BaseD {
fun foo()
}
abstract class BaseDImpl() : BaseD {
fun bar() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
}
expect interface BaseE {
fun foo()
}
sealed class BaseEImpl() : BaseE {
fun bar() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
}
expect interface BaseF {
fun foo()
}
expect <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class BaseFImpl<!>() : BaseF
expect abstract class BaseG() {
abstract fun foo()
}
expect open class BaseGImpl() : BaseG {
override fun foo()
}
class DerivedG1 : BaseGImpl()
@@ -8,7 +8,7 @@ expect open class B()
// TARGET_PLATFORM: Common // TARGET_PLATFORM: Common
actual class A : B() { actual class A : B() {
// "Nothing to override" in metadata compilation. Unfortunately we don't check metadata compilation in diagnostic tests // "Nothing to override" in metadata compilation. Unfortunately we don't check metadata compilation in diagnostic tests
override fun foo() {} <!NOTHING_TO_OVERRIDE{METADATA}!>override<!> fun foo() {}
} }
actual class C : B() { actual class C : B() {
// Nothing to override in platform compilation. // Nothing to override in platform compilation.
@@ -8,7 +8,7 @@ expect class Foo() {
// TARGET_PLATFORM: Common // TARGET_PLATFORM: Common
expect open class Base() {} expect open class Base() {}
actual class Foo : Base() { actual class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS{METADATA}!>Foo<!> : Base() {
} }
// MODULE: main()()(intermediate) // MODULE: main()()(intermediate)
@@ -4,7 +4,7 @@ expect abstract class Foo() {
abstract fun foo() abstract fun foo()
} }
class Impl : Foo() {} <!ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED{METADATA}!>class Impl<!> : Foo() {}
fun common() { fun common() {
Impl().foo() Impl().foo()
@@ -12,7 +12,7 @@ class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>C<!>
actual class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>A<!> actual class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>A<!>
class <!ACTUAL_MISSING, PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!> class <!ACTUAL_MISSING, ACTUAL_MISSING{METADATA}, PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!>
expect class C expect class C
@@ -9,8 +9,8 @@ expect class A {
expect class B expect class B
// K1 EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE false positive // K1 EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE false positive
actual class A { actual class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS{METADATA}!>A<!> {
actual fun <!ACTUAL_WITHOUT_EXPECT!>foo<!>(x: B) = "a" actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>foo<!>(x: B) = "a"
} }
// MODULE: main()()(intermediate) // MODULE: main()()(intermediate)
@@ -1,47 +1,47 @@
// MODULE: common // MODULE: common
// TARGET_PLATFORM: Common // TARGET_PLATFORM: Common
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>CommonClass<!> { <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>CommonClass<!> {
fun memberFun() fun memberFun()
val memberProp: Int val memberProp: Int
class Nested class Nested
inner class Inner inner class Inner
}<!> }<!>
actual class <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>CommonClass<!> { actual class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>CommonClass<!> {
actual fun <!ACTUAL_WITHOUT_EXPECT!>memberFun<!>() {} actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>memberFun<!>() {}
actual val <!ACTUAL_WITHOUT_EXPECT!>memberProp<!>: Int = 42 actual val <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>memberProp<!>: Int = 42
actual class <!ACTUAL_WITHOUT_EXPECT!>Nested<!> actual class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Nested<!>
actual inner class <!ACTUAL_WITHOUT_EXPECT!>Inner<!> actual inner class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Inner<!>
} }
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>commonFun<!>()<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>commonFun<!>()<!>
actual fun <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>commonFun<!>() {} actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>commonFun<!>() {}
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect val <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>commonProperty<!>: String<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect val <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>commonProperty<!>: String<!>
actual val <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>commonProperty<!>: String actual val <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>commonProperty<!>: String
get() = "hello" get() = "hello"
// MODULE: intermediate()()(common) // MODULE: intermediate()()(common)
// TARGET_PLATFORM: Common // TARGET_PLATFORM: Common
expect class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>IntermediateClass<!> { expect class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>IntermediateClass<!> {
fun memberFun() fun memberFun()
val memberProp: Int val memberProp: Int
class Nested class Nested
inner class Inner inner class Inner
} }
actual class <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>IntermediateClass<!> { actual class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>IntermediateClass<!> {
actual fun <!ACTUAL_WITHOUT_EXPECT!>memberFun<!>() {} actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>memberFun<!>() {}
actual val <!ACTUAL_WITHOUT_EXPECT!>memberProp<!>: Int = 42 actual val <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>memberProp<!>: Int = 42
actual class <!ACTUAL_WITHOUT_EXPECT!>Nested<!> actual class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Nested<!>
actual inner class <!ACTUAL_WITHOUT_EXPECT!>Inner<!> actual inner class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Inner<!>
} }
expect fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>intermediateFun<!>() expect fun <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>intermediateFun<!>()
actual fun <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>intermediateFun<!>() {} actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>intermediateFun<!>() {}
expect val <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>intermediateProperty<!>: String expect val <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>intermediateProperty<!>: String
actual val <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>intermediateProperty<!>: String actual val <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>intermediateProperty<!>: String
get() = "hello" get() = "hello"
// MODULE: main()()(intermediate) // MODULE: main()()(intermediate)
@@ -5,7 +5,7 @@ expect fun parameterCount()
fun parameterCount(p: String) {} fun parameterCount(p: String) {}
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun parameterCount2()<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun parameterCount2()<!>
actual fun <!ACTUAL_WITHOUT_EXPECT!>parameterCount2<!>(p: String) {} actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>parameterCount2<!>(p: String) {}
expect fun callableKind(): Int expect fun callableKind(): Int
val callableKind: Int = 1 val callableKind: Int = 1
@@ -13,10 +13,10 @@ val callableKind: Int = 1
expect fun <T> typeParameterCount() expect fun <T> typeParameterCount()
fun typeParameterCount() {} fun typeParameterCount() {}
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect enum class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>EnumEntries<!> { <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect enum class <!EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>EnumEntries<!> {
ONE, TWO; ONE, TWO;
}<!> }<!>
actual enum class <!ACTUAL_WITHOUT_EXPECT, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE!>EnumEntries<!> { actual enum class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE, EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE{METADATA}!>EnumEntries<!> {
ONE; ONE;
} }
@@ -1,12 +1,12 @@
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>header<!> <!INCOMPATIBLE_MODIFIERS!>impl<!> class <!ACTUAL_WITHOUT_EXPECT!>First<!><!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>header<!> <!INCOMPATIBLE_MODIFIERS!>impl<!> class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>First<!><!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>header<!> <!INCOMPATIBLE_MODIFIERS!>expect<!> class Second<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>header<!> <!INCOMPATIBLE_MODIFIERS!>expect<!> class Second<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>header<!> <!INCOMPATIBLE_MODIFIERS!>actual<!> class <!ACTUAL_WITHOUT_EXPECT!>Third<!><!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>header<!> <!INCOMPATIBLE_MODIFIERS!>actual<!> class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Third<!><!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>impl<!> <!INCOMPATIBLE_MODIFIERS!>expect<!> class <!ACTUAL_WITHOUT_EXPECT!>Fourth<!><!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>impl<!> <!INCOMPATIBLE_MODIFIERS!>expect<!> class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Fourth<!><!>
<!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>impl<!> <!INCOMPATIBLE_MODIFIERS!>actual<!> class <!ACTUAL_WITHOUT_EXPECT!>Fifth<!> <!DEPRECATED_MODIFIER, INCOMPATIBLE_MODIFIERS!>impl<!> <!INCOMPATIBLE_MODIFIERS!>actual<!> class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Fifth<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!INCOMPATIBLE_MODIFIERS!>expect<!> <!INCOMPATIBLE_MODIFIERS!>actual<!> class <!ACTUAL_WITHOUT_EXPECT!>Sixth<!><!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!INCOMPATIBLE_MODIFIERS!>expect<!> <!INCOMPATIBLE_MODIFIERS!>actual<!> class <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>Sixth<!><!>
@@ -16,8 +16,8 @@ expect interface I2 {
override fun foo(<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES!>x: Int<!>) override fun foo(<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES!>x: Int<!>)
} }
<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE!>interface CommonInterface<!> : I1, I2 { <!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE, MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE{METADATA}!>interface CommonInterface<!> : I1, I2 {
override fun foo(<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES!>x: Int<!>) override fun foo(<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES, MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES{METADATA}!>x: Int<!>)
} }
// MODULE: m1-jvm()()(m1-common) // MODULE: m1-jvm()()(m1-common)
@@ -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)
}
<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE!>expect interface ExpectInterface<!> : I1, I2 {
override fun foo(<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES!>x: Int<!>)
}
<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE!>interface CommonInterface<!> : I1, I2 {
override fun foo(<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES!>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)
}
<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES_WHEN_NO_EXPLICIT_OVERRIDE!>actual interface ExpectInterface<!> : I1, I2 {
actual override fun foo(<!MULTIPLE_DEFAULTS_INHERITED_FROM_SUPERTYPES!>x: Int<!>)
}
@@ -1,7 +1,7 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
<!CONFLICTING_OVERLOADS, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!> <!CONFLICTING_OVERLOADS, CONFLICTING_OVERLOADS{METADATA}, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!>
<!CONFLICTING_OVERLOADS, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!> <!CONFLICTING_OVERLOADS, CONFLICTING_OVERLOADS{METADATA}, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo(x: Int)<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo(x: Int)<!>
@@ -1,8 +1,8 @@
// MODULE: m1-common // MODULE: m1-common
// FILE: common.kt // FILE: common.kt
<!CONFLICTING_OVERLOADS, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!> <!CONFLICTING_OVERLOADS, CONFLICTING_OVERLOADS{METADATA}, NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo()<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!CONFLICTING_OVERLOADS, EXPECTED_DECLARATION_WITH_BODY!>expect fun foo()<!> {}<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!CONFLICTING_OVERLOADS, CONFLICTING_OVERLOADS{METADATA}, EXPECTED_DECLARATION_WITH_BODY!>expect fun foo()<!> {}<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!><!EXPECTED_DECLARATION_WITH_BODY!>expect fun bar()<!> {}<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!><!EXPECTED_DECLARATION_WITH_BODY!>expect fun bar()<!> {}<!>
@@ -0,0 +1,4 @@
// MODULE: m1-jvm
// FILE: jvm.kt
actual fun <!ACTUAL_WITHOUT_EXPECT, ACTUAL_WITHOUT_EXPECT{METADATA}!>foo<!>() { }
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// MODULE: m1-jvm // MODULE: m1-jvm
// FILE: jvm.kt // FILE: jvm.kt
@@ -0,0 +1,4 @@
// MODULE: m1-jvm
// FILE: jvm.kt
actual fun <!ACTUAL_WITHOUT_EXPECT!>foo<!>() { }
@@ -36,7 +36,7 @@ class NoFirCompilationErrorsHandler(testServices: TestServices) : FirAnalysisHan
val diagnosticsPerFile = testServices.firDiagnosticCollectorService.getFrontendDiagnosticsForModule(info) val diagnosticsPerFile = testServices.firDiagnosticCollectorService.getFrontendDiagnosticsForModule(info)
for ((firFile, diagnostics) in diagnosticsPerFile) { for ((firFile, diagnostics) in diagnosticsPerFile) {
for (diagnostic in diagnostics) { for (diagnostic in diagnostics.map { it.diagnostic }) {
if (diagnostic.severity == Severity.ERROR) { if (diagnostic.severity == Severity.ERROR) {
hasError = true hasError = true
if (!ignoreErrors) { if (!ignoreErrors) {
@@ -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.diagnostics.factories.DebugInfoDiagnosticFactory1
import org.jetbrains.kotlin.checkers.utils.TypeOfCall import org.jetbrains.kotlin.checkers.utils.TypeOfCall
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport 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.*
import org.jetbrains.kotlin.diagnostics.rendering.Renderers import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
@@ -135,22 +138,26 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
val firFile = info.mainFirFiles[file] ?: continue val firFile = info.mainFirFiles[file] ?: continue
var diagnostics = frontendDiagnosticsPerFile[firFile] var diagnostics = frontendDiagnosticsPerFile[firFile]
if (AdditionalFilesDirectives.CHECK_TYPE in currentModule.directives) { 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) { 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 = val diagnosticsMetadataInfos = diagnostics
diagnostics.diagnosticCodeMetaInfos( .groupBy({ it.kmpCompilationMode }, { it.diagnostic })
currentModule, file, .flatMap { (kmpCompilation, diagnostics) ->
diagnosticsService, globalMetadataInfoHandler, diagnostics.diagnosticCodeMetaInfos(
lightTreeEnabled, lightTreeComparingModeEnabled, currentModule, file,
forceRenderArguments, diagnosticsService, globalMetadataInfoHandler,
) lightTreeEnabled, lightTreeComparingModeEnabled,
forceRenderArguments,
kmpCompilation
)
}
globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos) globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos)
collectSyntaxDiagnostics(currentModule, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments) collectSyntaxDiagnostics(currentModule, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments)
collectDebugInfoDiagnostics(currentModule, file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled) 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( .toMetaInfos(
module, module,
testFile, testFile,
globalMetadataInfoHandler1 = globalMetadataInfoHandler, globalMetadataInfoHandler = globalMetadataInfoHandler,
lightTreeEnabled, lightTreeEnabled,
lightTreeComparingModeEnabled, lightTreeComparingModeEnabled,
forceRenderArguments, forceRenderArguments,
@@ -185,7 +192,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
it.toMetaInfos( it.toMetaInfos(
module, module,
testFile, testFile,
globalMetadataInfoHandler1 = globalMetadataInfoHandler, globalMetadataInfoHandler = globalMetadataInfoHandler,
lightTreeEnabled, lightTreeEnabled,
lightTreeComparingModeEnabled, lightTreeComparingModeEnabled,
forceRenderArguments, forceRenderArguments,
@@ -389,6 +396,7 @@ fun List<KtDiagnostic>.diagnosticCodeMetaInfos(
lightTreeEnabled: Boolean, lightTreeEnabled: Boolean,
lightTreeComparingModeEnabled: Boolean, lightTreeComparingModeEnabled: Boolean,
forceRenderArguments: Boolean = false, forceRenderArguments: Boolean = false,
kmpCompilationMode: KmpCompilationMode? = null
): List<FirDiagnosticCodeMetaInfo> = flatMap { diagnostic -> ): List<FirDiagnosticCodeMetaInfo> = flatMap { diagnostic ->
if (!diagnosticsService.shouldRenderDiagnostic( if (!diagnosticsService.shouldRenderDiagnostic(
module, module,
@@ -404,6 +412,7 @@ fun List<KtDiagnostic>.diagnosticCodeMetaInfos(
lightTreeEnabled, lightTreeEnabled,
lightTreeComparingModeEnabled, lightTreeComparingModeEnabled,
forceRenderArguments, forceRenderArguments,
kmpCompilationMode
) )
} }
@@ -587,13 +596,14 @@ class PsiLightTreeMetaInfoProcessor(testServices: TestServices) : AbstractTwoAtt
fun KtDiagnostic.toMetaInfos( fun KtDiagnostic.toMetaInfos(
module: TestModule, module: TestModule,
file: TestFile, file: TestFile,
globalMetadataInfoHandler1: GlobalMetadataInfoHandler, globalMetadataInfoHandler: GlobalMetadataInfoHandler,
lightTreeEnabled: Boolean, lightTreeEnabled: Boolean,
lightTreeComparingModeEnabled: Boolean, lightTreeComparingModeEnabled: Boolean,
forceRenderArguments: Boolean = false forceRenderArguments: Boolean = false,
kmpCompilationMode: KmpCompilationMode? = null
): List<FirDiagnosticCodeMetaInfo> = textRanges.map { range -> ): List<FirDiagnosticCodeMetaInfo> = textRanges.map { range ->
val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs, 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 } .any { it.description != null }
if (shouldRenderArguments) { if (shouldRenderArguments) {
metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs) metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs)
@@ -611,10 +621,36 @@ fun KtDiagnostic.toMetaInfos(
else -> error("Should not be here") else -> error("Should not be here")
} }
} }
if (kmpCompilationMode == KmpCompilationMode.METADATA) {
metaInfo.attributes += "METADATA"
}
metaInfo metaInfo
} }
typealias DiagnosticsMap = Multimap<FirFile, KtDiagnostic, List<KtDiagnostic>> typealias DiagnosticsMap = Multimap<FirFile, DiagnosticWithKmpCompilationMode, List<DiagnosticWithKmpCompilationMode>>
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 { open class FirDiagnosticCollectorService(val testServices: TestServices) : TestService {
private val cache: MutableMap<FirOutputArtifact, DiagnosticsMap> = mutableMapOf() private val cache: MutableMap<FirOutputArtifact, DiagnosticsMap> = mutableMapOf()
@@ -624,14 +660,14 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS
} }
fun containsErrors(info: FirOutputArtifact): Boolean { 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<FirFile, KtDiagnostic> { private fun computeDiagnostics(info: FirOutputArtifact): ListMultimap<FirFile, DiagnosticWithKmpCompilationMode> {
val allFiles = info.partsForDependsOnModules.flatMap { it.firFiles.values } val allFiles = info.partsForDependsOnModules.flatMap { it.firFiles.values }
val platformPart = info.partsForDependsOnModules.last() val platformPart = info.partsForDependsOnModules.last()
val lazyDeclarationResolver = platformPart.session.lazyDeclarationResolver val lazyDeclarationResolver = platformPart.session.lazyDeclarationResolver
val result = listMultimapOf<FirFile, KtDiagnostic>() val result = listMultimapOf<FirFile, DiagnosticWithKmpCompilationMode>()
lazyDeclarationResolver.disableLazyResolveContractChecksInside { lazyDeclarationResolver.disableLazyResolveContractChecksInside {
result += platformPart.session.runCheckers( result += platformPart.session.runCheckers(
@@ -639,7 +675,7 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS
allFiles, allFiles,
DiagnosticReporterFactory.createPendingReporter(), DiagnosticReporterFactory.createPendingReporter(),
mppCheckerKind = MppCheckerKind.Platform mppCheckerKind = MppCheckerKind.Platform
) ).mapValues { entry -> entry.value.map { DiagnosticWithKmpCompilationMode(it, KmpCompilationMode.PLATFORM) } }
for (part in info.partsForDependsOnModules) { for (part in info.partsForDependsOnModules) {
result += part.session.runCheckers( result += part.session.runCheckers(
@@ -647,7 +683,18 @@ open class FirDiagnosticCollectorService(val testServices: TestServices) : TestS
part.firFiles.values, part.firFiles.values,
DiagnosticReporterFactory.createPendingReporter(), DiagnosticReporterFactory.createPendingReporter(),
mppCheckerKind = MppCheckerKind.Common 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 <T> getFlag(flag: AnalysisFlag<T>): 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() val TestServices.firDiagnosticCollectorService: FirDiagnosticCollectorService by TestServices.testServiceAccessor()