[FIR, IR] ClassScopes incompatibility: split unfulfilled incompatibilities into "mismatchedMembers" and "incompatibleMembers"

KT-62590 is in progress

Unfortunatelly, ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT is now reported
twice in 'classScopeViaTypealiasIncompatible' test.

We had a duplicated diagnostic in other cases before my commits, anyway.
it will be fixed in
https://youtrack.jetbrains.com/issue/KT-62559/KMP-K2-prevent-reporting-ACTUALANNOTATIONSNOTMATCHEXPECT-twice-in-CLI#focus=Comments-27-8293687.0-0

Review: https://jetbrains.team/p/kt/reviews/12750/timeline
This commit is contained in:
Nikita Bobko
2023-10-25 23:13:51 +02:00
committed by teamcity
parent d0b34fe378
commit 664b9cc3ba
11 changed files with 71 additions and 33 deletions
@@ -163,10 +163,13 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember
}
val nonTrivialUnfulfilled = singleIncompatibility.unfulfilled.filterNot(::hasSingleActualSuspect)
val nonTrivialIncompatibleMembers = singleIncompatibility.incompatibleMembers.filterNot(::hasSingleActualSuspect)
val nonTrivialMismatchedMembers = singleIncompatibility.mismatchedMembers.filterNot(::hasSingleActualSuspect)
if (nonTrivialUnfulfilled.isNotEmpty()) {
reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialUnfulfilled, context)
if (nonTrivialIncompatibleMembers.isNotEmpty()) {
reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialIncompatibleMembers, context)
} else if (nonTrivialMismatchedMembers.isNotEmpty()) {
reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialMismatchedMembers, context)
}
}
@@ -102,7 +102,8 @@ private fun StringBuilder.renderIncompatibilityInformation(
append(indent)
append("No actual members are found for expected members listed below:")
mode.newLine(this)
renderIncompatibleClassScopes(compatibility.unfulfilled, indent, mode)
renderIncompatibleClassScopes(compatibility.mismatchedMembers, indent, mode)
renderIncompatibleClassScopes(compatibility.incompatibleMembers, indent, mode)
}
}
}
@@ -469,7 +469,7 @@ class FirExpectActualMatchingContextImpl private constructor(
override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?
) {
@@ -219,7 +219,7 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
actualSymbols: List<IrSymbol>,
context: MatchingContext
) {
AbstractExpectActualChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol,
actualSymbols,
context,
@@ -260,7 +260,7 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {
@@ -45,7 +45,7 @@ internal fun collectActualCallablesMatchingToSpecificExpect(
matchingActuals += actualSymbol
}
}
AbstractExpectActualChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol,
actualSymbols,
context,
@@ -67,20 +67,20 @@ object AbstractExpectActualChecker {
result as ExpectActualCompatibility<T>
}
fun <T : DeclarationSymbolMarker> matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
fun <T : DeclarationSymbolMarker> checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectDeclaration: DeclarationSymbolMarker,
actualDeclarations: List<DeclarationSymbolMarker>,
context: ExpectActualMatchingContext<T>,
checkClassScopesCompatibility: Boolean,
) {
with(context) {
matchSingleExpectAgainstPotentialActuals(
checkSingleExpectAgainstPotentialActuals(
expectDeclaration,
actualDeclarations,
substitutor = null,
expectClassSymbol = null,
actualClassSymbol = null,
unfulfilled = null,
incompatibleMembers = null,
checkClassScopesCompatibility = checkClassScopesCompatibility,
)
}
@@ -216,7 +216,10 @@ object AbstractExpectActualChecker {
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): ExpectActualCheckingCompatibility.Incompatible<*>? {
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>()
val mismatchedMembers =
arrayListOf<Pair<DeclarationSymbolMarker, Map<ExpectActualMatchingCompatibility.Mismatch, List<DeclarationSymbolMarker?>>>>()
val incompatibleMembers =
arrayListOf<Pair<DeclarationSymbolMarker, Map<ExpectActualCheckingCompatibility.Incompatible<*>, List<DeclarationSymbolMarker?>>>>()
val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
@@ -228,15 +231,26 @@ object AbstractExpectActualChecker {
expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker
}.orEmpty()
matchSingleExpectAgainstPotentialActuals(
val matched = AbstractExpectActualMatcher.matchSingleExpectAgainstPotentialActuals(
expectMember,
actualMembers,
substitutor,
expectClassSymbol,
actualClassSymbol,
unfulfilled,
checkClassScopesCompatibility = true,
mismatchedMembers,
)
if (matched != null) {
checkSingleExpectAgainstPotentialActuals(
expectMember,
listOf(matched), // todo convert vector to scalar
substitutor,
expectClassSymbol,
actualClassSymbol,
incompatibleMembers,
checkClassScopesCompatibility = true,
)
}
}
if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
@@ -248,19 +262,20 @@ object AbstractExpectActualChecker {
// TODO: check static scope?
if (unfulfilled.isEmpty()) return null
return ExpectActualCheckingCompatibility.ClassScopes(unfulfilled)
return when (mismatchedMembers.isNotEmpty() || incompatibleMembers.isNotEmpty()) {
true -> ExpectActualCheckingCompatibility.ClassScopes(mismatchedMembers, incompatibleMembers)
false -> null
}
}
context(ExpectActualMatchingContext<*>)
private fun matchSingleExpectAgainstPotentialActuals(
private fun checkSingleExpectAgainstPotentialActuals(
expectMember: DeclarationSymbolMarker,
actualMembers: List<DeclarationSymbolMarker>,
substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>?,
incompatibleMembers: MutableList<Pair<DeclarationSymbolMarker, Map<ExpectActualCheckingCompatibility.Incompatible<*>, List<DeclarationSymbolMarker?>>>>?,
checkClassScopesCompatibility: Boolean,
) {
val mapping = actualMembers.keysToMap { actualMember ->
@@ -286,7 +301,7 @@ object AbstractExpectActualChecker {
}
}
val incompatibilityMap = mutableMapOf<MismatchOrIncompatible<*>, MutableList<DeclarationSymbolMarker>>()
val mismatchedOrIncompatibleMap = mutableMapOf<MismatchOrIncompatible<*>, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) {
when (compatibility) {
is ExpectActualCompatibility.MatchedOrCompatible<*> -> {
@@ -294,12 +309,19 @@ object AbstractExpectActualChecker {
return
}
is MismatchOrIncompatible<*> -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
is MismatchOrIncompatible<*> -> mismatchedOrIncompatibleMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
// todo it's temporary code. KT-62590 in progress
val incompatibilities = mismatchedOrIncompatibleMap
.mapNotNull { (key, value) ->
(key as? ExpectActualCheckingCompatibility.Incompatible)?.let { it to value }
}
.toMap()
incompatibleMembers?.add(expectMember to incompatibilities)
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, mismatchedOrIncompatibleMap, expectClassSymbol, actualClassSymbol)
}
context(ExpectActualMatchingContext<*>)
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
@@ -111,15 +110,22 @@ object AbstractExpectActualMatcher {
// TODO: check static scope?
}
/**
* Besides returning the matched declaration
*
* The function has an additional side effects:
* - It adds mismatched members to `mismatchedMembers`
* - It calls `onMatchedMembers` and `onMismatchedMembersFromClassScope` callbacks
*/
context(ExpectActualMatchingContext<*>)
private fun matchSingleExpectAgainstPotentialActuals(
internal fun matchSingleExpectAgainstPotentialActuals(
expectMember: DeclarationSymbolMarker,
actualMembers: List<DeclarationSymbolMarker>,
substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>?,
) {
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<ExpectActualMatchingCompatibility.Mismatch, List<DeclarationSymbolMarker?>>>>?,
): DeclarationSymbolMarker? {
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is CallableSymbolMarker -> getCallablesCompatibility(
@@ -137,12 +143,12 @@ object AbstractExpectActualMatcher {
}
}
val incompatibilityMap = mutableMapOf<MismatchOrIncompatible<*>, MutableList<DeclarationSymbolMarker>>()
val incompatibilityMap = mutableMapOf<ExpectActualMatchingCompatibility.Mismatch, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) {
when (compatibility) {
ExpectActualMatchingCompatibility.MatchedSuccessfully -> {
onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return
return actualMember
}
is ExpectActualMatchingCompatibility.Mismatch -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
@@ -151,6 +157,7 @@ object AbstractExpectActualMatcher {
unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
return null
}
context(ExpectActualMatchingContext<*>)
@@ -174,7 +174,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {}
@@ -18,7 +18,8 @@ class WeakIncompatibilityImpl {
fun foo(differentName: String) {}
}
actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>WeakIncompatibility<!> = WeakIncompatibilityImpl
// TODO: Duplicated diagnostic will be fixed in KT-62559
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>WeakIncompatibility<!> = WeakIncompatibilityImpl<!>
class StrongIncompatibilityImpl {
fun foo(p: String) {} // Different param type
@@ -18,6 +18,7 @@ class WeakIncompatibilityImpl {
fun foo(differentName: String) {}
}
// TODO: Duplicated diagnostic will be fixed in KT-62559
actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>WeakIncompatibility<!> = WeakIncompatibilityImpl
class StrongIncompatibilityImpl {
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.resolve.multiplatform
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility.Mismatch
private const val TYPE_PARAMETER_COUNT = "number of type parameters is different"
// Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer
@@ -82,7 +84,8 @@ sealed class ExpectActualCheckingCompatibility<out D> : ExpectActualCompatibilit
object FunInterfaceModifier : Incompatible<Nothing>("actual declaration for fun expect interface is not a functional interface")
object Supertypes : Incompatible<Nothing>("some supertypes are missing in the actual declaration")
class ClassScopes<D>(
val unfulfilled: List<Pair<D, Map<out ExpectActualCompatibility.MismatchOrIncompatible<D>, Collection<D>>>>
val mismatchedMembers: List<Pair</* expect */ D, Map<Mismatch, /* actuals */ Collection<D>>>>,
val incompatibleMembers: List<Pair</* expect */ D, Map<Incompatible<D>, /* actuals */ Collection<D>>>>,
) : Incompatible<D>("some expected members have no actual ones")
object EnumEntries : Incompatible<Nothing>("some entries from expected enum are missing in the actual enum")