[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:
+6
-3
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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?
|
||||
) {
|
||||
|
||||
+2
-2
@@ -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?,
|
||||
) {
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ internal fun collectActualCallablesMatchingToSpecificExpect(
|
||||
matchingActuals += actualSymbol
|
||||
}
|
||||
}
|
||||
AbstractExpectActualChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
|
||||
AbstractExpectActualChecker.checkSingleExpectTopLevelDeclarationAgainstPotentialActuals(
|
||||
expectSymbol,
|
||||
actualSymbols,
|
||||
context,
|
||||
|
||||
+38
-16
@@ -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<*>)
|
||||
|
||||
+13
-6
@@ -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<*>)
|
||||
|
||||
+1
-1
@@ -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?,
|
||||
) {}
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
+1
@@ -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 {
|
||||
|
||||
+4
-1
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user