[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 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()) { if (nonTrivialIncompatibleMembers.isNotEmpty()) {
reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialUnfulfilled, context) 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(indent)
append("No actual members are found for expected members listed below:") append("No actual members are found for expected members listed below:")
mode.newLine(this) 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( override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker? containingActualClassSymbol: RegularClassSymbolMarker?
) { ) {
@@ -219,7 +219,7 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
actualSymbols: List<IrSymbol>, actualSymbols: List<IrSymbol>,
context: MatchingContext context: MatchingContext
) { ) {
AbstractExpectActualChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals( AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol, expectSymbol,
actualSymbols, actualSymbols,
context, context,
@@ -260,7 +260,7 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
override fun onMismatchedOrIncompatibleMembersFromClassScope( override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?,
) { ) {
@@ -45,7 +45,7 @@ internal fun collectActualCallablesMatchingToSpecificExpect(
matchingActuals += actualSymbol matchingActuals += actualSymbol
} }
} }
AbstractExpectActualChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals( AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol, expectSymbol,
actualSymbols, actualSymbols,
context, context,
@@ -67,20 +67,20 @@ object AbstractExpectActualChecker {
result as ExpectActualCompatibility<T> result as ExpectActualCompatibility<T>
} }
fun <T : DeclarationSymbolMarker> matchSingleExpectTopLevelDeclarationAgainstPotentialActuals( fun <T : DeclarationSymbolMarker> checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectDeclaration: DeclarationSymbolMarker, expectDeclaration: DeclarationSymbolMarker,
actualDeclarations: List<DeclarationSymbolMarker>, actualDeclarations: List<DeclarationSymbolMarker>,
context: ExpectActualMatchingContext<T>, context: ExpectActualMatchingContext<T>,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
) { ) {
with(context) { with(context) {
matchSingleExpectAgainstPotentialActuals( checkSingleExpectAgainstPotentialActuals(
expectDeclaration, expectDeclaration,
actualDeclarations, actualDeclarations,
substitutor = null, substitutor = null,
expectClassSymbol = null, expectClassSymbol = null,
actualClassSymbol = null, actualClassSymbol = null,
unfulfilled = null, incompatibleMembers = null,
checkClassScopesCompatibility = checkClassScopesCompatibility, checkClassScopesCompatibility = checkClassScopesCompatibility,
) )
} }
@@ -216,7 +216,10 @@ object AbstractExpectActualChecker {
actualClassSymbol: RegularClassSymbolMarker, actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker, substitutor: TypeSubstitutorMarker,
): ExpectActualCheckingCompatibility.Incompatible<*>? { ): 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 } val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
@@ -228,15 +231,26 @@ object AbstractExpectActualChecker {
expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker
}.orEmpty() }.orEmpty()
matchSingleExpectAgainstPotentialActuals( val matched = AbstractExpectActualMatcher.matchSingleExpectAgainstPotentialActuals(
expectMember, expectMember,
actualMembers, actualMembers,
substitutor, substitutor,
expectClassSymbol, expectClassSymbol,
actualClassSymbol, actualClassSymbol,
unfulfilled, mismatchedMembers,
checkClassScopesCompatibility = true,
) )
if (matched != null) {
checkSingleExpectAgainstPotentialActuals(
expectMember,
listOf(matched), // todo convert vector to scalar
substitutor,
expectClassSymbol,
actualClassSymbol,
incompatibleMembers,
checkClassScopesCompatibility = true,
)
}
} }
if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) { if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
@@ -248,19 +262,20 @@ object AbstractExpectActualChecker {
// TODO: check static scope? // TODO: check static scope?
if (unfulfilled.isEmpty()) return null return when (mismatchedMembers.isNotEmpty() || incompatibleMembers.isNotEmpty()) {
true -> ExpectActualCheckingCompatibility.ClassScopes(mismatchedMembers, incompatibleMembers)
return ExpectActualCheckingCompatibility.ClassScopes(unfulfilled) false -> null
}
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
private fun matchSingleExpectAgainstPotentialActuals( private fun checkSingleExpectAgainstPotentialActuals(
expectMember: DeclarationSymbolMarker, expectMember: DeclarationSymbolMarker,
actualMembers: List<DeclarationSymbolMarker>, actualMembers: List<DeclarationSymbolMarker>,
substitutor: TypeSubstitutorMarker?, substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?, expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?, actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>?, incompatibleMembers: MutableList<Pair<DeclarationSymbolMarker, Map<ExpectActualCheckingCompatibility.Incompatible<*>, List<DeclarationSymbolMarker?>>>>?,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
) { ) {
val mapping = actualMembers.keysToMap { actualMember -> 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) { for ((actualMember, compatibility) in mapping) {
when (compatibility) { when (compatibility) {
is ExpectActualCompatibility.MatchedOrCompatible<*> -> { is ExpectActualCompatibility.MatchedOrCompatible<*> -> {
@@ -294,12 +309,19 @@ object AbstractExpectActualChecker {
return return
} }
is MismatchOrIncompatible<*> -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) is MismatchOrIncompatible<*> -> mismatchedOrIncompatibleMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
} }
} }
unfulfilled?.add(expectMember to incompatibilityMap) // todo it's temporary code. KT-62590 in progress
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) 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<*>) context(ExpectActualMatchingContext<*>)
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility 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.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
@@ -111,15 +110,22 @@ object AbstractExpectActualMatcher {
// TODO: check static scope? // 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<*>) context(ExpectActualMatchingContext<*>)
private fun matchSingleExpectAgainstPotentialActuals( internal fun matchSingleExpectAgainstPotentialActuals(
expectMember: DeclarationSymbolMarker, expectMember: DeclarationSymbolMarker,
actualMembers: List<DeclarationSymbolMarker>, actualMembers: List<DeclarationSymbolMarker>,
substitutor: TypeSubstitutorMarker?, substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?, expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: 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 -> val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) { when (expectMember) {
is CallableSymbolMarker -> getCallablesCompatibility( 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) { for ((actualMember, compatibility) in mapping) {
when (compatibility) { when (compatibility) {
ExpectActualMatchingCompatibility.MatchedSuccessfully -> { ExpectActualMatchingCompatibility.MatchedSuccessfully -> {
onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return return actualMember
} }
is ExpectActualMatchingCompatibility.Mismatch -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) is ExpectActualMatchingCompatibility.Mismatch -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
@@ -151,6 +157,7 @@ object AbstractExpectActualMatcher {
unfulfilled?.add(expectMember to incompatibilityMap) unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
return null
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -174,7 +174,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
fun onMismatchedOrIncompatibleMembersFromClassScope( fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<out ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?,
) {} ) {}
@@ -18,7 +18,8 @@ class WeakIncompatibilityImpl {
fun foo(differentName: String) {} 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 { class StrongIncompatibilityImpl {
fun foo(p: String) {} // Different param type fun foo(p: String) {} // Different param type
@@ -18,6 +18,7 @@ class WeakIncompatibilityImpl {
fun foo(differentName: String) {} 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 actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>WeakIncompatibility<!> = WeakIncompatibilityImpl
class StrongIncompatibilityImpl { class StrongIncompatibilityImpl {
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.resolve.multiplatform 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" private const val TYPE_PARAMETER_COUNT = "number of type parameters is different"
// Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer // 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 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") object Supertypes : Incompatible<Nothing>("some supertypes are missing in the actual declaration")
class ClassScopes<D>( 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") ) : Incompatible<D>("some expected members have no actual ones")
object EnumEntries : Incompatible<Nothing>("some entries from expected enum are missing in the actual enum") object EnumEntries : Incompatible<Nothing>("some entries from expected enum are missing in the actual enum")