[FIR, IR] Refactor: restructure ExpectActualCompatibility class hierarchy

KT-62590 is in progress

This commit is pure refactoring.
- No tests changed their behaviour
- Semantics isn't changed

Review: https://jetbrains.team/p/kt/reviews/12750/timeline
This commit is contained in:
Nikita Bobko
2023-10-16 15:27:57 +02:00
committed by teamcity
parent bbbc775181
commit d0b34fe378
20 changed files with 256 additions and 244 deletions
@@ -373,7 +373,7 @@ internal object FirToKtConversionCreator {
ClassId::class, ClassId::class,
FirModuleData::class, FirModuleData::class,
ExpectActualCompatibility::class, ExpectActualCompatibility::class,
ExpectActualCompatibility.Incompatible::class, ExpectActualCompatibility.MismatchOrIncompatible::class,
ExpectActualAnnotationsIncompatibilityType::class, ExpectActualAnnotationsIncompatibilityType::class,
DeprecationInfo::class, DeprecationInfo::class,
ApiVersion::class, ApiVersion::class,
@@ -3763,8 +3763,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
NoActualClassMemberForExpectedClassImpl( NoActualClassMemberForExpectedClassImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a), firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic.b.map { pair -> firDiagnostic.b.map { pair ->
firSymbolBuilder.buildSymbol(pair.first) to pair.second.mapKeys { (incompatible, _) -> firSymbolBuilder.buildSymbol(pair.first) to pair.second.mapKeys { (mismatchOrIncompatible, _) ->
incompatible mismatchOrIncompatible
}.mapValues { (_, collection) -> }.mapValues { (_, collection) ->
collection.map { firBasedSymbol -> collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol) firSymbolBuilder.buildSymbol(firBasedSymbol)
@@ -84,7 +84,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -2630,7 +2630,7 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
interface NoActualClassMemberForExpectedClass : KtFirDiagnostic<KtNamedDeclaration> { interface NoActualClassMemberForExpectedClass : KtFirDiagnostic<KtNamedDeclaration> {
override val diagnosticClass get() = NoActualClassMemberForExpectedClass::class override val diagnosticClass get() = NoActualClassMemberForExpectedClass::class
val declaration: KtSymbol val declaration: KtSymbol
val members: List<Pair<KtSymbol, Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>>> val members: List<Pair<KtSymbol, Map<MismatchOrIncompatible<FirBasedSymbol<*>>, List<KtSymbol>>>>
} }
interface ActualMissing : KtFirDiagnostic<KtNamedDeclaration> { interface ActualMissing : KtFirDiagnostic<KtNamedDeclaration> {
@@ -85,7 +85,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -3166,7 +3166,7 @@ internal class AmbiguousExpectsImpl(
internal class NoActualClassMemberForExpectedClassImpl( internal class NoActualClassMemberForExpectedClassImpl(
override val declaration: KtSymbol, override val declaration: KtSymbol,
override val members: List<Pair<KtSymbol, Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>>>, override val members: List<Pair<KtSymbol, Map<MismatchOrIncompatible<FirBasedSymbol<*>>, List<KtSymbol>>>>,
firDiagnostic: KtPsiDiagnostic, firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken, token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtNamedDeclaration>(firDiagnostic, token), KtFirDiagnostic.NoActualClassMemberForExpectedClass ) : KtAbstractFirDiagnostic<KtNamedDeclaration>(firDiagnostic, token), KtFirDiagnostic.NoActualClassMemberForExpectedClass
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.PrivateForInline import org.jetbrains.kotlin.util.PrivateForInline
@@ -1270,7 +1269,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ACTUAL_WITHOUT_EXPECT by error<KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME_ONLY) { val ACTUAL_WITHOUT_EXPECT by error<KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME_ONLY) {
parameter<Symbol>("declaration") parameter<Symbol>("declaration")
parameter<Map<ExpectActualCompatibility<Symbol>, Collection<Symbol>>>("compatibility") parameter<Map<out ExpectActualCompatibility<Symbol>, Collection<Symbol>>>("compatibility")
} }
val AMBIGUOUS_ACTUALS by error<KtNamedDeclaration>(PositioningStrategy.INCOMPATIBLE_DECLARATION) { val AMBIGUOUS_ACTUALS by error<KtNamedDeclaration>(PositioningStrategy.INCOMPATIBLE_DECLARATION) {
@@ -1285,7 +1284,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error<KtNamedDeclaration>(PositioningStrategy.ACTUAL_DECLARATION_NAME) { val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error<KtNamedDeclaration>(PositioningStrategy.ACTUAL_DECLARATION_NAME) {
parameter<Symbol>("declaration") parameter<Symbol>("declaration")
parameter<List<Pair<Symbol, Map<Incompatible<Symbol>, Collection<Symbol>>>>>("members") parameter<List<Pair<Symbol, Map<out ExpectActualCompatibility.MismatchOrIncompatible<Symbol>, Collection<Symbol>>>>>("members")
} }
val ACTUAL_MISSING by error<KtNamedDeclaration>(PositioningStrategy.ACTUAL_DECLARATION_NAME) val ACTUAL_MISSING by error<KtNamedDeclaration>(PositioningStrategy.ACTUAL_DECLARATION_NAME)
@@ -110,7 +110,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -663,10 +663,10 @@ object FirErrors {
val DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE by error2<KtClass, FirRegularClassSymbol, Collection<FirNamedFunctionSymbol>>(SourceElementPositioningStrategies.SUPERTYPES_LIST) val DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE by error2<KtClass, FirRegularClassSymbol, Collection<FirNamedFunctionSymbol>>(SourceElementPositioningStrategies.SUPERTYPES_LIST)
val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error0<PsiElement>() val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error0<PsiElement>()
val NO_ACTUAL_FOR_EXPECT by error3<KtNamedDeclaration, FirBasedSymbol<*>, FirModuleData, Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION) val NO_ACTUAL_FOR_EXPECT by error3<KtNamedDeclaration, FirBasedSymbol<*>, FirModuleData, Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val ACTUAL_WITHOUT_EXPECT by error2<KtNamedDeclaration, FirBasedSymbol<*>, Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY) val ACTUAL_WITHOUT_EXPECT by error2<KtNamedDeclaration, FirBasedSymbol<*>, Map<out ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY)
val AMBIGUOUS_ACTUALS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION) val AMBIGUOUS_ACTUALS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val AMBIGUOUS_EXPECTS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirModuleData>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION) val AMBIGUOUS_EXPECTS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirModuleData>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error2<KtNamedDeclaration, FirBasedSymbol<*>, List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME) val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error2<KtNamedDeclaration, FirBasedSymbol<*>, List<Pair<FirBasedSymbol<*>, Map<out MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME)
val ACTUAL_MISSING by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME) val ACTUAL_MISSING by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME)
val EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING by warning0<KtClassLikeDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER) val EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING by warning0<KtClassLikeDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER)
val NOT_A_MULTIPLATFORM_COMPILATION by error0<PsiElement>() val NOT_A_MULTIPLATFORM_COMPILATION by error0<PsiElement>()
@@ -30,9 +30,10 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Compatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible
@Suppress("DuplicatedCode") @Suppress("DuplicatedCode")
@@ -128,9 +129,11 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
val source = declaration.source val source = declaration.source
if (!declaration.isActual) { if (!declaration.isActual) {
if (compatibilityToMembersMap.allStrongIncompatibilities()) return if (compatibilityToMembersMap.allMismatches()) return
if (Compatible in compatibilityToMembersMap) { if (ExpectActualCheckingCompatibility.Compatible in compatibilityToMembersMap ||
ExpectActualMatchingCompatibility.MatchedSuccessfully in compatibilityToMembersMap
) {
if (checkActual) { if (checkActual) {
reporter.reportOn(source, FirErrors.ACTUAL_MISSING, context) reporter.reportOn(source, FirErrors.ACTUAL_MISSING, context)
} }
@@ -140,7 +143,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
val singleIncompatibility = compatibilityToMembersMap.keys.singleOrNull() val singleIncompatibility = compatibilityToMembersMap.keys.singleOrNull()
when { when {
singleIncompatibility is Incompatible.ClassScopes -> { singleIncompatibility is ExpectActualCheckingCompatibility.ClassScopes -> {
require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) { require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) {
"Incompatible.ClassScopes is only possible for a class or a typealias: $declaration" "Incompatible.ClassScopes is only possible for a class or a typealias: $declaration"
} }
@@ -150,13 +153,13 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
// This is needed only to reduce the number of errors. Incompatibility errors for those members will be reported // This is needed only to reduce the number of errors. Incompatibility errors for those members will be reported
// later when this checker is called for them // later when this checker is called for them
fun hasSingleActualSuspect( fun hasSingleActualSuspect(
expectedWithIncompatibility: Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> expectedWithIncompatibility: Pair<FirBasedSymbol<*>, Map<out MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>
): Boolean { ): Boolean {
val (expectedMember, incompatibility) = expectedWithIncompatibility val (expectedMember, incompatibility) = expectedWithIncompatibility
val actualMember = incompatibility.values.singleOrNull()?.singleOrNull() val actualMember = incompatibility.values.singleOrNull()?.singleOrNull()
@OptIn(SymbolInternals::class) @OptIn(SymbolInternals::class)
return actualMember != null && return actualMember != null &&
!incompatibility.allStrongIncompatibilities() && !incompatibility.allMismatches() &&
actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember
} }
@@ -167,9 +170,10 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
} }
} }
Compatible !in compatibilityToMembersMap -> { ExpectActualCheckingCompatibility.Compatible !in compatibilityToMembersMap &&
ExpectActualMatchingCompatibility.MatchedSuccessfully !in compatibilityToMembersMap -> {
// A nicer diagnostic for functions with default params // A nicer diagnostic for functions with default params
if (declaration is FirFunction && compatibilityToMembersMap.keys.any { it is Incompatible.ActualFunctionWithDefaultParameters }) { if (declaration is FirFunction && compatibilityToMembersMap.keys.any { it is ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters }) {
reporter.reportOn(declaration.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, context) reporter.reportOn(declaration.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, context)
} else if (requireActualModifier(declaration.symbol, context.session)) { } else if (requireActualModifier(declaration.symbol, context.session)) {
reporter.reportOn( reporter.reportOn(
@@ -247,8 +251,8 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
) )
} }
fun Map<out ExpectActualCompatibility<*>, *>.allStrongIncompatibilities(): Boolean { fun Map<out ExpectActualCompatibility<*>, *>.allMismatches(): Boolean {
return keys.all { it is Incompatible.StrongIncompatible } return keys.all { it is ExpectActualMatchingCompatibility.Mismatch }
} }
// we don't require `actual` modifier on // we don't require `actual` modifier on
@@ -18,14 +18,15 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.ContextIndependentParameterRenderer import org.jetbrains.kotlin.diagnostics.rendering.ContextIndependentParameterRenderer
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.MismatchOrIncompatible
class FirPlatformIncompatibilityDiagnosticRenderer( class FirPlatformIncompatibilityDiagnosticRenderer(
private val mode: MultiplatformDiagnosticRenderingMode private val mode: MultiplatformDiagnosticRenderingMode
) : ContextIndependentParameterRenderer<Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> { ) : ContextIndependentParameterRenderer<Map<out ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> {
override fun render( override fun render(
obj: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>, obj: Map<out ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
): String { ): String {
if (obj.isEmpty()) return "" if (obj.isEmpty()) return ""
@@ -43,9 +44,9 @@ class FirPlatformIncompatibilityDiagnosticRenderer(
class FirIncompatibleExpectedActualClassScopesRenderer( class FirIncompatibleExpectedActualClassScopesRenderer(
private val mode: MultiplatformDiagnosticRenderingMode private val mode: MultiplatformDiagnosticRenderingMode
) : ContextIndependentParameterRenderer<List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>> { ) : ContextIndependentParameterRenderer<List<Pair<FirBasedSymbol<*>, Map<out MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>> {
override fun render( override fun render(
obj: List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>> obj: List<Pair<FirBasedSymbol<*>, Map<out MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>
): String { ): String {
if (obj.isEmpty()) return "" if (obj.isEmpty()) return ""
@@ -90,14 +91,14 @@ private fun StringBuilder.renderIncompatibilityInformation(
append("The following declaration") append("The following declaration")
if (descriptors.size == 1) append(" is") else append("s are") if (descriptors.size == 1) append(" is") else append("s are")
append(" incompatible") append(" incompatible")
(compatibility as? Incompatible)?.reason?.let { append(" because $it") } (compatibility as? MismatchOrIncompatible)?.reason?.let { append(" because $it") }
append(":") append(":")
mode.renderList(this, descriptors.map { descriptor -> mode.renderList(this, descriptors.map { descriptor ->
{ mode.renderSymbol(this, descriptor, indent) } { mode.renderSymbol(this, descriptor, indent) }
}) })
if (compatibility is Incompatible.ClassScopes) { if (compatibility is ExpectActualCheckingCompatibility.ClassScopes) {
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)
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
@@ -450,7 +451,7 @@ class FirExpectActualMatchingContextImpl private constructor(
return symbol.source == null && symbol.origin !is FirDeclarationOrigin.Plugin return symbol.source == null && symbol.origin !is FirDeclarationOrigin.Plugin
} }
override fun onMatchedMembers( override fun onMatchedOrCompatibleMembers(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
@@ -462,13 +463,13 @@ class FirExpectActualMatchingContextImpl private constructor(
expectSymbol.asSymbol(), expectSymbol.asSymbol(),
actualSymbol.asSymbol(), actualSymbol.asSymbol(),
containingExpectClassSymbol.asSymbol(), containingExpectClassSymbol.asSymbol(),
ExpectActualCompatibility.Compatible ExpectActualMatchingCompatibility.MatchedSuccessfully
) )
} }
override fun onMismatchedMembersFromClassScope( override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker? containingActualClassSymbol: RegularClassSymbolMarker?
) { ) {
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.mpp.CallableSymbolMarker import org.jetbrains.kotlin.mpp.CallableSymbolMarker
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.getMatchedAndChecked
object FirExpectActualResolver { object FirExpectActualResolver {
fun findExpectForActual( fun findExpectForActual(
@@ -67,9 +69,9 @@ object FirExpectActualResolver {
) )
}.let { }.let {
// If there is a compatible entry, return a map only containing it // If there is a compatible entry, return a map only containing it
when (val compatibleSymbols = it[ExpectActualCompatibility.Compatible]) { when (val compatibleSymbols = it.getMatchedAndChecked()) {
null -> it null -> it
else -> mapOf<ExpectActualCompatibility<FirBasedSymbol<*>>, _>(ExpectActualCompatibility.Compatible to compatibleSymbols) else -> mapOf<ExpectActualCompatibility<FirBasedSymbol<*>>, _>(ExpectActualMatchingCompatibility.MatchedSuccessfully to compatibleSymbols)
} }
} }
} }
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
object CommonBackendErrors { object CommonBackendErrors {
val NO_ACTUAL_FOR_EXPECT by error2<PsiElement, String, ModuleDescriptor>() val NO_ACTUAL_FOR_EXPECT by error2<PsiElement, String, ModuleDescriptor>()
val INCOMPATIBLE_MATCHING by error3<PsiElement, String, String, ExpectActualCompatibility.Incompatible<*>>() val INCOMPATIBLE_MATCHING by error3<PsiElement, String, String, ExpectActualCompatibility.MismatchOrIncompatible<*>>()
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3<PsiElement, IrSymbol, IrSymbol, ExpectActualAnnotationsIncompatibilityType<IrConstructorCall>>() val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3<PsiElement, IrSymbol, IrSymbol, ExpectActualAnnotationsIncompatibilityType<IrConstructorCall>>()
val EVALUATION_ERROR by error1<PsiElement, String>() val EVALUATION_ERROR by error1<PsiElement, String>()
val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1<PsiElement, IrValueParameter>() val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1<PsiElement, IrValueParameter>()
@@ -76,7 +76,7 @@ object KtDefaultCommonBackendErrorMessages : BaseDiagnosticRendererFactory() {
} }
object BackendDiagnosticRenderers { object BackendDiagnosticRenderers {
val INCOMPATIBILITY = Renderer<ExpectActualCompatibility.Incompatible<*>> { val INCOMPATIBILITY = Renderer<ExpectActualCompatibility.MismatchOrIncompatible<*>> {
it.reason ?: "<unknown>" it.reason ?: "<unknown>"
} }
val SYMBOL_OWNER_DECLARATION_FQ_NAME = Renderer<IrSymbol> { val SYMBOL_OWNER_DECLARATION_FQ_NAME = Renderer<IrSymbol> {
@@ -258,9 +258,9 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination) recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination)
} }
override fun onMismatchedMembersFromClassScope( override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?,
) { ) {
@@ -113,7 +113,7 @@ internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportIncompatibleExpectActual( internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportIncompatibleExpectActual(
expectSymbol: IrSymbol, expectSymbol: IrSymbol,
actualSymbol: IrSymbol, actualSymbol: IrSymbol,
incompatibility: ExpectActualCompatibility.Incompatible<*> incompatibility: ExpectActualCompatibility.MismatchOrIncompatible<*>
) { ) {
val expectDeclaration = expectSymbol.owner as IrDeclaration val expectDeclaration = expectSymbol.owner as IrDeclaration
val actualDeclaration = actualSymbol.owner as IrDeclaration val actualDeclaration = actualSymbol.owner as IrDeclaration
@@ -480,7 +480,7 @@ internal abstract class IrExpectActualMatchingContext(
} }
} }
override fun onMatchedMembers( override fun onMatchedOrCompatibleMembers(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
@@ -315,7 +315,8 @@ object AbstractExpectActualAnnotationMatchChecker {
// so we are sure that we found the right member. // so we are sure that we found the right member.
checkClassScopesCompatibility = false, checkClassScopesCompatibility = false,
) )
val expectMember = expectToCompatibilityMap.filter { it.value == ExpectActualCompatibility.Compatible }.keys.singleOrNull() val expectMember = expectToCompatibilityMap
.filter { it.value is ExpectActualCompatibility.MatchedOrCompatible }.keys.singleOrNull()
// Check also incompatible members if only one is found // Check also incompatible members if only one is found
?: expectToCompatibilityMap.keys.singleOrNull() ?: expectToCompatibilityMap.keys.singleOrNull()
?: continue ?: continue
@@ -12,8 +12,10 @@ import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.mpp.* 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.ExpectActualCheckingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible 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.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
@@ -91,8 +93,8 @@ object AbstractExpectActualChecker {
actualClassLikeSymbol: ClassLikeSymbolMarker, actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
checkClassScopes: Boolean, checkClassScopes: Boolean,
): ExpectActualCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes) ): ExpectActualCheckingCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes)
?: ExpectActualCompatibility.Compatible ?: ExpectActualCheckingCompatibility.Compatible
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@Suppress("warnings") @Suppress("warnings")
@@ -101,7 +103,7 @@ object AbstractExpectActualChecker {
actualClassLikeSymbol: ClassLikeSymbolMarker, actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
): ExpectActualCompatibility.Incompatible.WeakIncompatible<*>? { ): ExpectActualCheckingCompatibility.Incompatible<*>? {
// Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name // Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name
require(expectClassSymbol.name == actualClassLikeSymbol.name) { require(expectClassSymbol.name == actualClassLikeSymbol.name) {
"This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol" "This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol"
@@ -114,28 +116,28 @@ object AbstractExpectActualChecker {
else -> error("Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol") else -> error("Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol")
} }
if (!areCompatibleClassKinds(expectClassSymbol, actualClass)) return Incompatible.ClassKind if (!areCompatibleClassKinds(expectClassSymbol, actualClass)) return ExpectActualCheckingCompatibility.ClassKind
if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline || it.isValue) }) { if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline || it.isValue) }) {
return Incompatible.ClassModifiers return ExpectActualCheckingCompatibility.ClassModifiers
} }
if (expectClassSymbol.isFun && !actualClass.isFun && actualClass.isNotSamInterface()) { if (expectClassSymbol.isFun && !actualClass.isFun && actualClass.isNotSamInterface()) {
return Incompatible.FunInterfaceModifier return ExpectActualCheckingCompatibility.FunInterfaceModifier
} }
val expectTypeParameterSymbols = expectClassSymbol.typeParameters val expectTypeParameterSymbols = expectClassSymbol.typeParameters
val actualTypeParameterSymbols = actualClass.typeParameters val actualTypeParameterSymbols = actualClass.typeParameters
if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) { if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) {
return Incompatible.ClassTypeParameterCount return ExpectActualCheckingCompatibility.ClassTypeParameterCount
} }
if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) { if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) {
return Incompatible.Modality return ExpectActualCheckingCompatibility.Modality
} }
if (!areCompatibleClassVisibilities(expectClassSymbol, actualClass)) { if (!areCompatibleClassVisibilities(expectClassSymbol, actualClass)) {
return Incompatible.Visibility return ExpectActualCheckingCompatibility.Visibility
} }
val substitutor = createExpectActualTypeParameterSubstitutor( val substitutor = createExpectActualTypeParameterSubstitutor(
@@ -145,14 +147,14 @@ object AbstractExpectActualChecker {
) )
if (!areCompatibleTypeParameterUpperBounds(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor)) { if (!areCompatibleTypeParameterUpperBounds(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor)) {
return Incompatible.ClassTypeParameterUpperBounds return ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds
} }
getTypeParametersVarianceOrReifiedIncompatibility(expectTypeParameterSymbols, actualTypeParameterSymbols) getTypeParametersVarianceOrReifiedIncompatibility(expectTypeParameterSymbols, actualTypeParameterSymbols)
?.let { return it } ?.let { return it }
if (!areCompatibleSupertypes(expectClassSymbol, actualClass, substitutor)) { if (!areCompatibleSupertypes(expectClassSymbol, actualClass, substitutor)) {
return Incompatible.Supertypes return ExpectActualCheckingCompatibility.Supertypes
} }
if (checkClassScopesCompatibility) { if (checkClassScopesCompatibility) {
@@ -213,8 +215,8 @@ object AbstractExpectActualChecker {
expectClassSymbol: RegularClassSymbolMarker, expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker, actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker, substitutor: TypeSubstitutorMarker,
): Incompatible.WeakIncompatible<*>? { ): ExpectActualCheckingCompatibility.Incompatible<*>? {
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>() val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>()
val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name } val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
@@ -241,14 +243,14 @@ object AbstractExpectActualChecker {
val aEntries = expectClassSymbol.collectEnumEntryNames() val aEntries = expectClassSymbol.collectEnumEntryNames()
val bEntries = actualClassSymbol.collectEnumEntryNames() val bEntries = actualClassSymbol.collectEnumEntryNames()
if (!bEntries.containsAll(aEntries)) return Incompatible.EnumEntries if (!bEntries.containsAll(aEntries)) return ExpectActualCheckingCompatibility.EnumEntries
} }
// TODO: check static scope? // TODO: check static scope?
if (unfulfilled.isEmpty()) return null if (unfulfilled.isEmpty()) return null
return Incompatible.ClassScopes(unfulfilled) return ExpectActualCheckingCompatibility.ClassScopes(unfulfilled)
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -258,7 +260,7 @@ object AbstractExpectActualChecker {
substitutor: TypeSubstitutorMarker?, substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?, expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?, actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>?, unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>?,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
) { ) {
val mapping = actualMembers.keysToMap { actualMember -> val mapping = actualMembers.keysToMap { actualMember ->
@@ -284,20 +286,20 @@ object AbstractExpectActualChecker {
} }
} }
val incompatibilityMap = mutableMapOf<Incompatible<*>, MutableList<DeclarationSymbolMarker>>() val incompatibilityMap = mutableMapOf<MismatchOrIncompatible<*>, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) { for ((actualMember, compatibility) in mapping) {
when (compatibility) { when (compatibility) {
ExpectActualCompatibility.Compatible -> { is ExpectActualCompatibility.MatchedOrCompatible<*> -> {
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return return
} }
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) is MismatchOrIncompatible<*> -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
} }
} }
unfulfilled?.add(expectMember to incompatibilityMap) unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -325,7 +327,7 @@ object AbstractExpectActualChecker {
expectDeclaration is ConstructorSymbolMarker && expectDeclaration is ConstructorSymbolMarker &&
actualDeclaration is ConstructorSymbolMarker actualDeclaration is ConstructorSymbolMarker
) { ) {
return ExpectActualCompatibility.Compatible return ExpectActualCheckingCompatibility.Compatible
} }
// We must prioritize to return STRONG incompatible over WEAK incompatible (because STRONG incompatibility allows to search for overloads) // We must prioritize to return STRONG incompatible over WEAK incompatible (because STRONG incompatibility allows to search for overloads)
@@ -339,7 +341,7 @@ object AbstractExpectActualChecker {
expectContainingClass, expectContainingClass,
actualContainingClass actualContainingClass
) )
?: ExpectActualCompatibility.Compatible ?: ExpectActualCheckingCompatibility.Compatible
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -348,27 +350,27 @@ object AbstractExpectActualChecker {
actualDeclaration: CallableSymbolMarker, actualDeclaration: CallableSymbolMarker,
insideAnnotationClass: Boolean, insideAnnotationClass: Boolean,
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
): Incompatible.StrongIncompatible<*>? { ): ExpectActualMatchingCompatibility.Mismatch? {
if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) { if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) {
return Incompatible.CallableKind return ExpectActualMatchingCompatibility.CallableKind
} }
val expectedReceiverType = expectDeclaration.extensionReceiverType val expectedReceiverType = expectDeclaration.extensionReceiverType
val actualReceiverType = actualDeclaration.extensionReceiverType val actualReceiverType = actualDeclaration.extensionReceiverType
if ((expectedReceiverType != null) != (actualReceiverType != null)) { if ((expectedReceiverType != null) != (actualReceiverType != null)) {
return Incompatible.ParameterShape return ExpectActualMatchingCompatibility.ParameterShape
} }
val expectedValueParameters = expectDeclaration.valueParameters val expectedValueParameters = expectDeclaration.valueParameters
val actualValueParameters = actualDeclaration.valueParameters val actualValueParameters = actualDeclaration.valueParameters
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) { if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
return Incompatible.ParameterCount return ExpectActualMatchingCompatibility.ParameterCount
} }
val expectedTypeParameters = expectDeclaration.typeParameters val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters val actualTypeParameters = actualDeclaration.typeParameters
if (expectedTypeParameters.size != actualTypeParameters.size) { if (expectedTypeParameters.size != actualTypeParameters.size) {
return Incompatible.FunctionTypeParameterCount return ExpectActualMatchingCompatibility.FunctionTypeParameterCount
} }
val substitutor = createExpectActualTypeParameterSubstitutor( val substitutor = createExpectActualTypeParameterSubstitutor(
@@ -388,11 +390,11 @@ object AbstractExpectActualChecker {
parameterOfAnnotationComparisonMode = false parameterOfAnnotationComparisonMode = false
) )
) { ) {
return Incompatible.ParameterTypes return ExpectActualMatchingCompatibility.ParameterTypes
} }
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) { if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
return Incompatible.FunctionTypeParameterUpperBounds return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds
} }
return null return null
@@ -406,7 +408,7 @@ object AbstractExpectActualChecker {
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?, expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?,
): Incompatible.WeakIncompatible<*>? { ): ExpectActualCheckingCompatibility<*>? {
val expectedTypeParameters = expectDeclaration.typeParameters val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters val actualTypeParameters = actualDeclaration.typeParameters
val expectedValueParameters = expectDeclaration.valueParameters val expectedValueParameters = expectDeclaration.valueParameters
@@ -425,16 +427,16 @@ object AbstractExpectActualChecker {
parameterOfAnnotationComparisonMode = insideAnnotationClass parameterOfAnnotationComparisonMode = insideAnnotationClass
) )
) { ) {
return Incompatible.ReturnType return ExpectActualCheckingCompatibility.ReturnType
} }
} }
if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) { if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) {
return Incompatible.ParameterNames return ExpectActualCheckingCompatibility.ParameterNames
} }
if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) { if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) {
return Incompatible.TypeParameterNames return ExpectActualCheckingCompatibility.TypeParameterNames
} }
val expectModality = expectDeclaration.modality val expectModality = expectDeclaration.modality
@@ -447,11 +449,11 @@ object AbstractExpectActualChecker {
actualContainingClass?.modality actualContainingClass?.modality
) )
) { ) {
return Incompatible.Modality return ExpectActualCheckingCompatibility.Modality
} }
if (!areCompatibleCallableVisibilities(expectDeclaration.visibility, expectModality, actualDeclaration.visibility)) { if (!areCompatibleCallableVisibilities(expectDeclaration.visibility, expectModality, actualDeclaration.visibility)) {
return Incompatible.Visibility return ExpectActualCheckingCompatibility.Visibility
} }
getTypeParametersVarianceOrReifiedIncompatibility(expectedTypeParameters, actualTypeParameters)?.let { return it } getTypeParametersVarianceOrReifiedIncompatibility(expectedTypeParameters, actualTypeParameters)?.let { return it }
@@ -467,22 +469,22 @@ object AbstractExpectActualChecker {
(actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet()) (actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet())
.flatMap { it.valueParameters }.any { it.hasDefaultValue } .flatMap { it.valueParameters }.any { it.hasDefaultValue }
) { ) {
return Incompatible.ActualFunctionWithDefaultParameters return ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters
} }
} }
} }
if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) { if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) {
return Incompatible.ValueParameterVararg return ExpectActualCheckingCompatibility.ValueParameterVararg
} }
// Adding noinline/crossinline to parameters is disallowed, except if the expected declaration was not inline at all // Adding noinline/crossinline to parameters is disallowed, except if the expected declaration was not inline at all
if (expectDeclaration is SimpleFunctionSymbolMarker && expectDeclaration.isInline) { if (expectDeclaration is SimpleFunctionSymbolMarker && expectDeclaration.isInline) {
if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isNoinline && actualValueParameters[i].isNoinline }) { if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isNoinline && actualValueParameters[i].isNoinline }) {
return Incompatible.ValueParameterNoinline return ExpectActualCheckingCompatibility.ValueParameterNoinline
} }
if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isCrossinline && actualValueParameters[i].isCrossinline }) { if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isCrossinline && actualValueParameters[i].isCrossinline }) {
return Incompatible.ValueParameterCrossinline return ExpectActualCheckingCompatibility.ValueParameterCrossinline
} }
} }
@@ -634,9 +636,9 @@ object AbstractExpectActualChecker {
private fun getTypeParametersVarianceOrReifiedIncompatibility( private fun getTypeParametersVarianceOrReifiedIncompatibility(
expectTypeParameterSymbols: List<TypeParameterSymbolMarker>, expectTypeParameterSymbols: List<TypeParameterSymbolMarker>,
actualTypeParameterSymbols: List<TypeParameterSymbolMarker>, actualTypeParameterSymbols: List<TypeParameterSymbolMarker>,
): Incompatible.WeakIncompatible<*>? { ): ExpectActualCheckingCompatibility.Incompatible<*>? {
if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) { if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) {
return Incompatible.TypeParameterVariance return ExpectActualCheckingCompatibility.TypeParameterVariance
} }
// Removing "reified" from an expected function's type parameter is fine // Removing "reified" from an expected function's type parameter is fine
@@ -645,7 +647,7 @@ object AbstractExpectActualChecker {
!expectTypeParameterSymbols[i].isReified && actualTypeParameterSymbols[i].isReified !expectTypeParameterSymbols[i].isReified && actualTypeParameterSymbols[i].isReified
} }
) { ) {
return Incompatible.TypeParameterReified return ExpectActualCheckingCompatibility.TypeParameterReified
} }
return null return null
@@ -655,9 +657,9 @@ object AbstractExpectActualChecker {
private fun getFunctionsIncompatibility( private fun getFunctionsIncompatibility(
expectFunction: CallableSymbolMarker, expectFunction: CallableSymbolMarker,
actualFunction: CallableSymbolMarker, actualFunction: CallableSymbolMarker,
): Incompatible.WeakIncompatible<*>? { ): ExpectActualCheckingCompatibility<*>? {
if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) { if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) {
return Incompatible.FunctionModifiersDifferent return ExpectActualCheckingCompatibility.FunctionModifiersDifferent
} }
if ( if (
@@ -665,7 +667,7 @@ object AbstractExpectActualChecker {
expectFunction.isInline && !actualFunction.isInline || expectFunction.isInline && !actualFunction.isInline ||
expectFunction.isOperator && !actualFunction.isOperator expectFunction.isOperator && !actualFunction.isOperator
) { ) {
return Incompatible.FunctionModifiersNotSubset return ExpectActualCheckingCompatibility.FunctionModifiersNotSubset
} }
return null return null
@@ -675,12 +677,12 @@ object AbstractExpectActualChecker {
private fun getPropertiesIncompatibility( private fun getPropertiesIncompatibility(
expected: PropertySymbolMarker, expected: PropertySymbolMarker,
actual: PropertySymbolMarker, actual: PropertySymbolMarker,
): Incompatible.WeakIncompatible<*>? { ): ExpectActualCheckingCompatibility<*>? {
return when { return when {
!equalBy(expected, actual) { p -> p.isVar } -> Incompatible.PropertyKind !equalBy(expected, actual) { p -> p.isVar } -> ExpectActualCheckingCompatibility.PropertyKind
!equalBy(expected, actual) { p -> p.isLateinit } -> Incompatible.PropertyLateinitModifier !equalBy(expected, actual) { p -> p.isLateinit } -> ExpectActualCheckingCompatibility.PropertyLateinitModifier
expected.isConst && !actual.isConst -> Incompatible.PropertyConstModifier expected.isConst && !actual.isConst -> ExpectActualCheckingCompatibility.PropertyConstModifier
!arePropertySettersWithCompatibleVisibilities(expected, actual) -> Incompatible.PropertySetterVisibility !arePropertySettersWithCompatibleVisibilities(expected, actual) -> ExpectActualCheckingCompatibility.PropertySetterVisibility
else -> null else -> null
} }
} }
@@ -10,7 +10,8 @@ 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.Incompatible 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.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
@@ -66,11 +67,11 @@ object AbstractExpectActualMatcher {
} }
} }
fun <T : DeclarationSymbolMarker> matchClassifiers( fun matchClassifiers(
expectClassSymbol: RegularClassSymbolMarker, expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker, actualClassLikeSymbol: ClassLikeSymbolMarker,
context: ExpectActualMatchingContext<T>, context: ExpectActualMatchingContext<*>,
): ExpectActualCompatibility<T> = with(context) { ): ExpectActualMatchingCompatibility = with(context) {
// Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name // Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name
check(expectClassSymbol.name == actualClassLikeSymbol.name) { check(expectClassSymbol.name == actualClassLikeSymbol.name) {
"This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol" "This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol"
@@ -78,7 +79,7 @@ object AbstractExpectActualMatcher {
check(actualClassLikeSymbol is RegularClassSymbolMarker || actualClassLikeSymbol is TypeAliasSymbolMarker) { check(actualClassLikeSymbol is RegularClassSymbolMarker || actualClassLikeSymbol is TypeAliasSymbolMarker) {
"Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol" "Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol"
} }
ExpectActualCompatibility.Compatible ExpectActualMatchingCompatibility.MatchedSuccessfully
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -117,7 +118,7 @@ object AbstractExpectActualMatcher {
substitutor: TypeSubstitutorMarker?, substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?, expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?, actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>?, unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>?,
) { ) {
val mapping = actualMembers.keysToMap { actualMember -> val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) { when (expectMember) {
@@ -136,20 +137,20 @@ object AbstractExpectActualMatcher {
} }
} }
val incompatibilityMap = mutableMapOf<Incompatible<*>, MutableList<DeclarationSymbolMarker>>() val incompatibilityMap = mutableMapOf<MismatchOrIncompatible<*>, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) { for ((actualMember, compatibility) in mapping) {
when (compatibility) { when (compatibility) {
ExpectActualCompatibility.Compatible -> { ExpectActualMatchingCompatibility.MatchedSuccessfully -> {
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol) onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return return
} }
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember) is ExpectActualMatchingCompatibility.Mismatch -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
} }
} }
unfulfilled?.add(expectMember to incompatibilityMap) unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol) onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -159,7 +160,7 @@ object AbstractExpectActualMatcher {
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?, expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?, actualContainingClass: RegularClassSymbolMarker?,
): ExpectActualCompatibility<*> { ): ExpectActualMatchingCompatibility {
require( require(
(expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) || (expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) ||
expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName
@@ -177,12 +178,12 @@ object AbstractExpectActualMatcher {
expectDeclaration is ConstructorSymbolMarker && expectDeclaration is ConstructorSymbolMarker &&
actualDeclaration is ConstructorSymbolMarker actualDeclaration is ConstructorSymbolMarker
) { ) {
return ExpectActualCompatibility.Compatible return ExpectActualMatchingCompatibility.MatchedSuccessfully
} }
val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS
return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor) return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor)
?: ExpectActualCompatibility.Compatible ?: ExpectActualMatchingCompatibility.MatchedSuccessfully
} }
context(ExpectActualMatchingContext<*>) context(ExpectActualMatchingContext<*>)
@@ -191,27 +192,27 @@ object AbstractExpectActualMatcher {
actualDeclaration: CallableSymbolMarker, actualDeclaration: CallableSymbolMarker,
insideAnnotationClass: Boolean, insideAnnotationClass: Boolean,
parentSubstitutor: TypeSubstitutorMarker?, parentSubstitutor: TypeSubstitutorMarker?,
): Incompatible.StrongIncompatible<*>? { ): ExpectActualMatchingCompatibility.Mismatch? {
if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) { if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) {
return Incompatible.CallableKind return ExpectActualMatchingCompatibility.CallableKind
} }
val expectedReceiverType = expectDeclaration.extensionReceiverType val expectedReceiverType = expectDeclaration.extensionReceiverType
val actualReceiverType = actualDeclaration.extensionReceiverType val actualReceiverType = actualDeclaration.extensionReceiverType
if ((expectedReceiverType != null) != (actualReceiverType != null)) { if ((expectedReceiverType != null) != (actualReceiverType != null)) {
return Incompatible.ParameterShape return ExpectActualMatchingCompatibility.ParameterShape
} }
val expectedValueParameters = expectDeclaration.valueParameters val expectedValueParameters = expectDeclaration.valueParameters
val actualValueParameters = actualDeclaration.valueParameters val actualValueParameters = actualDeclaration.valueParameters
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) { if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
return Incompatible.ParameterCount return ExpectActualMatchingCompatibility.ParameterCount
} }
val expectedTypeParameters = expectDeclaration.typeParameters val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters val actualTypeParameters = actualDeclaration.typeParameters
if (expectedTypeParameters.size != actualTypeParameters.size) { if (expectedTypeParameters.size != actualTypeParameters.size) {
return Incompatible.FunctionTypeParameterCount return ExpectActualMatchingCompatibility.FunctionTypeParameterCount
} }
val substitutor = createExpectActualTypeParameterSubstitutor( val substitutor = createExpectActualTypeParameterSubstitutor(
@@ -231,11 +232,11 @@ object AbstractExpectActualMatcher {
parameterOfAnnotationComparisonMode = false parameterOfAnnotationComparisonMode = false
) )
) { ) {
return Incompatible.ParameterTypes return ExpectActualMatchingCompatibility.ParameterTypes
} }
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) { if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
return Incompatible.FunctionTypeParameterUpperBounds return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds
} }
return null return null
@@ -165,16 +165,16 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
val CallableSymbolMarker.hasStableParameterNames: Boolean val CallableSymbolMarker.hasStableParameterNames: Boolean
fun onMatchedMembers( fun onMatchedOrCompatibleMembers(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?,
) {} ) {}
fun onMismatchedMembersFromClassScope( fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>, actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?, containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?, containingActualClassSymbol: RegularClassSymbolMarker?,
) {} ) {}
@@ -5,103 +5,104 @@
package org.jetbrains.kotlin.resolve.multiplatform package org.jetbrains.kotlin.resolve.multiplatform
import kotlin.contracts.ExperimentalContracts private const val TYPE_PARAMETER_COUNT = "number of type parameters is different"
import kotlin.contracts.contract
sealed class ExpectActualCompatibility<out D> { // Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer
/**
// Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer * DON'T USE THIS CLASS. This class is currently used only in diagnostics. Eventually, it will go away KT-62631
sealed class Incompatible<out D>(val reason: String?) : ExpectActualCompatibility<D>() { */
sealed interface ExpectActualCompatibility<out D> {
sealed class WeakIncompatible<out D>(reason: String?) : Incompatible<D>(reason) /**
* DON'T USE THIS CLASS. This class is currently used only in diagnostics. Eventually, it will go away KT-62631
// For StrongIncompatible `actual` declaration is considered as overload and error reports on expected declaration */
sealed class StrongIncompatible<out D>(reason: String?) : Incompatible<D>(reason) sealed interface MismatchOrIncompatible<out D> : ExpectActualCompatibility<D> {
val reason: String?
// Callables
object CallableKind : StrongIncompatible<Nothing>("callable kinds are different (function vs property)")
object ParameterShape : StrongIncompatible<Nothing>("parameter shapes are different (extension vs non-extension)")
object ParameterCount : StrongIncompatible<Nothing>("number of value parameters is different")
// FunctionTypeParameterCount is strong because functions can be overloaded by type parameter count
object FunctionTypeParameterCount : StrongIncompatible<Nothing>("number of type parameters is different")
// ClassTypeParameterCount is weak because classes cannot be overloaded
object ClassTypeParameterCount : WeakIncompatible<Nothing>(FunctionTypeParameterCount.reason)
object ParameterTypes : StrongIncompatible<Nothing>("parameter types are different")
object ReturnType : WeakIncompatible<Nothing>("return type is different")
object ParameterNames : WeakIncompatible<Nothing>("parameter names are different")
object TypeParameterNames : WeakIncompatible<Nothing>("names of type parameters are different")
object ValueParameterVararg : WeakIncompatible<Nothing>("some value parameter is vararg in one declaration and non-vararg in the other")
object ValueParameterNoinline : WeakIncompatible<Nothing>(
"some value parameter is noinline in one declaration and not noinline in the other"
)
object ValueParameterCrossinline : WeakIncompatible<Nothing>(
"some value parameter is crossinline in one declaration and not crossinline in the other"
)
// Functions
object FunctionModifiersDifferent : WeakIncompatible<Nothing>("modifiers are different (suspend)")
object FunctionModifiersNotSubset : WeakIncompatible<Nothing>(
"some modifiers on expected declaration are missing on the actual one (infix, inline, operator)"
)
object ActualFunctionWithDefaultParameters :
WeakIncompatible<Nothing>("actual function cannot have default argument values, they should be declared in the expected function")
// Properties
object PropertyKind : WeakIncompatible<Nothing>("property kinds are different (val vs var)")
object PropertyLateinitModifier : WeakIncompatible<Nothing>("modifiers are different (lateinit)")
object PropertyConstModifier : WeakIncompatible<Nothing>("modifiers are different (const)")
object PropertySetterVisibility : WeakIncompatible<Nothing>("setter visibility is different")
// Classifiers
object ClassKind : WeakIncompatible<Nothing>("class kinds are different (class, interface, object, enum, annotation)")
object ClassModifiers : WeakIncompatible<Nothing>("modifiers are different (companion, inner, inline, value)")
object FunInterfaceModifier : WeakIncompatible<Nothing>("actual declaration for fun expect interface is not a functional interface")
object Supertypes : WeakIncompatible<Nothing>("some supertypes are missing in the actual declaration")
class ClassScopes<D>(
val unfulfilled: List<Pair<D, Map<Incompatible<D>, Collection<D>>>>
) : WeakIncompatible<D>("some expected members have no actual ones")
object EnumEntries : WeakIncompatible<Nothing>("some entries from expected enum are missing in the actual enum")
// Common
object Modality : WeakIncompatible<Nothing>("modality is different")
object Visibility : WeakIncompatible<Nothing>("visibility is different")
// FunctionTypeParameterUpperBounds is weak because functions can be overloaded by type parameter upper bounds
object FunctionTypeParameterUpperBounds : StrongIncompatible<Nothing>("upper bounds of type parameters are different")
// ClassTypeParameterUpperBounds is strong because classes cannot be overloaded
object ClassTypeParameterUpperBounds : WeakIncompatible<Nothing>(FunctionTypeParameterUpperBounds.reason)
object TypeParameterVariance : WeakIncompatible<Nothing>("declaration-site variances of type parameters are different")
object TypeParameterReified : WeakIncompatible<Nothing>(
"some type parameter is reified in one declaration and non-reified in the other"
)
} }
object Compatible : ExpectActualCompatibility<Nothing>() // It's temporary class. KT-62590 is in progress
sealed interface MatchedOrCompatible<out D> : ExpectActualCompatibility<D>
}
/**
* All mismatches that can be fixed by introducing an overload without this mismatch.
* In other words: "overloadable" mismatches
*
* @see ExpectActualCheckingCompatibility
*/
sealed class ExpectActualMatchingCompatibility : ExpectActualCompatibility<Nothing> {
sealed class Mismatch(override val reason: String?) : ExpectActualMatchingCompatibility(),
ExpectActualCompatibility.MismatchOrIncompatible<Nothing>
object CallableKind : Mismatch("callable kinds are different (function vs property)")
object ParameterShape : Mismatch("parameter shapes are different (extension vs non-extension)")
object ParameterCount : Mismatch("number of value parameters is different")
object FunctionTypeParameterCount : Mismatch(TYPE_PARAMETER_COUNT)
object ParameterTypes : Mismatch("parameter types are different")
object FunctionTypeParameterUpperBounds : Mismatch("upper bounds of type parameters are different")
object MatchedSuccessfully : ExpectActualMatchingCompatibility(), ExpectActualCompatibility.MatchedOrCompatible<Nothing>
}
/**
* "Non-overloadable" compatibilities
*
* @see ExpectActualMatchingCompatibility
*/
sealed class ExpectActualCheckingCompatibility<out D> : ExpectActualCompatibility<D> {
sealed class Incompatible<out D>(override val reason: String?) : ExpectActualCheckingCompatibility<D>(),
ExpectActualCompatibility.MismatchOrIncompatible<D>
object ClassTypeParameterCount : Incompatible<Nothing>(TYPE_PARAMETER_COUNT)
// Callables
object ReturnType : Incompatible<Nothing>("return type is different")
object ParameterNames : Incompatible<Nothing>("parameter names are different")
object TypeParameterNames : Incompatible<Nothing>("names of type parameters are different")
object ValueParameterVararg : Incompatible<Nothing>("some value parameter is vararg in one declaration and non-vararg in the other")
object ValueParameterNoinline :
Incompatible<Nothing>("some value parameter is noinline in one declaration and not noinline in the other")
object ValueParameterCrossinline :
Incompatible<Nothing>("some value parameter is crossinline in one declaration and not crossinline in the other")
// Functions
object FunctionModifiersDifferent : Incompatible<Nothing>("modifiers are different (suspend)")
object FunctionModifiersNotSubset :
Incompatible<Nothing>("some modifiers on expected declaration are missing on the actual one (infix, inline, operator)")
object ActualFunctionWithDefaultParameters :
Incompatible<Nothing>("actual function cannot have default argument values, they should be declared in the expected function")
// Properties
object PropertyKind : Incompatible<Nothing>("property kinds are different (val vs var)")
object PropertyLateinitModifier : Incompatible<Nothing>("modifiers are different (lateinit)")
object PropertyConstModifier : Incompatible<Nothing>("modifiers are different (const)")
object PropertySetterVisibility : Incompatible<Nothing>("setter visibility is different")
// Classifiers
object ClassKind : Incompatible<Nothing>("class kinds are different (class, interface, object, enum, annotation)")
object ClassModifiers : Incompatible<Nothing>("modifiers are different (companion, inner, inline, value)")
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>>>>
) : Incompatible<D>("some expected members have no actual ones")
object EnumEntries : Incompatible<Nothing>("some entries from expected enum are missing in the actual enum")
// Common
object Modality : Incompatible<Nothing>("modality is different")
object Visibility : Incompatible<Nothing>("visibility is different")
object ClassTypeParameterUpperBounds : Incompatible<Nothing>(ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds.reason)
object TypeParameterVariance : Incompatible<Nothing>("declaration-site variances of type parameters are different")
object TypeParameterReified : Incompatible<Nothing>("some type parameter is reified in one declaration and non-reified in the other")
object Compatible : ExpectActualCheckingCompatibility<Nothing>(), ExpectActualCompatibility.MatchedOrCompatible<Nothing>
} }
val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean
get() = this is ExpectActualCompatibility.Compatible get() = this is ExpectActualCompatibility.MatchedOrCompatible || this is ExpectActualCheckingCompatibility
|| this is ExpectActualCompatibility.Incompatible.WeakIncompatible
val ExpectActualCompatibility<*>.compatible: Boolean val ExpectActualCompatibility<*>.compatible: Boolean
get() = this == ExpectActualCompatibility.Compatible get() = this is ExpectActualCompatibility.MatchedOrCompatible
// It's temporary function. KT-62590 is in progress
fun <T, R> Map<ExpectActualCompatibility<R>, List<T>>.getMatchedAndChecked(): List<T>? =
(get(ExpectActualMatchingCompatibility.MatchedSuccessfully).orEmpty() + get(ExpectActualCheckingCompatibility.Compatible).orEmpty())
.takeIf { it.isNotEmpty() }
@@ -56,39 +56,39 @@ data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember
} }
} }
fun ExpectActualCompatibility.Incompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) { fun ExpectActualCompatibility.MismatchOrIncompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) {
ExpectActualCompatibility.Incompatible.CallableKind, ExpectActualMatchingCompatibility.CallableKind,
ExpectActualCompatibility.Incompatible.ParameterCount, ExpectActualMatchingCompatibility.ParameterCount,
ExpectActualCompatibility.Incompatible.ParameterShape, ExpectActualMatchingCompatibility.ParameterShape,
ExpectActualCompatibility.Incompatible.ParameterTypes, ExpectActualMatchingCompatibility.ParameterTypes,
ExpectActualCompatibility.Incompatible.FunctionTypeParameterCount, ExpectActualMatchingCompatibility.FunctionTypeParameterCount,
ExpectActualCompatibility.Incompatible.FunctionTypeParameterUpperBounds, ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds,
// It's an awful API. It will be fixed in KT-62752 // It's an awful API. It will be fixed in KT-62752
-> error("It's not allowed to call this function with receiver: $this") -> error("It's not allowed to call this function with receiver: $this")
ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride ExpectActualCheckingCompatibility.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
ExpectActualCompatibility.Incompatible.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override
ExpectActualCompatibility.Incompatible.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members")
is ExpectActualCompatibility.Incompatible.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members") is ExpectActualCheckingCompatibility.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.FunInterfaceModifier -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier (suspend) ExpectActualCheckingCompatibility.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier (suspend)
ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier (infix, inline, operator) ExpectActualCheckingCompatibility.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier (infix, inline, operator)
ExpectActualCompatibility.Incompatible.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride ExpectActualCheckingCompatibility.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride
ExpectActualCompatibility.Incompatible.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride ExpectActualCheckingCompatibility.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride
ExpectActualCompatibility.Incompatible.PropertyConstModifier -> null // const fun can't be overridden ExpectActualCheckingCompatibility.PropertyConstModifier -> null // const fun can't be overridden
ExpectActualCompatibility.Incompatible.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride ExpectActualCheckingCompatibility.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride
ExpectActualCompatibility.Incompatible.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride ExpectActualCheckingCompatibility.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride
ExpectActualCompatibility.Incompatible.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride ExpectActualCheckingCompatibility.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride
ExpectActualCompatibility.Incompatible.Supertypes -> error("Not applicable because ExpectActualMemberDiff is about members") ExpectActualCheckingCompatibility.Supertypes -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride ExpectActualCheckingCompatibility.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride
ExpectActualCompatibility.Incompatible.TypeParameterReified -> null // inline fun can't be overridden ExpectActualCheckingCompatibility.TypeParameterReified -> null // inline fun can't be overridden
ExpectActualCompatibility.Incompatible.TypeParameterVariance -> null // Members are not allowed to have variance ExpectActualCheckingCompatibility.TypeParameterVariance -> null // Members are not allowed to have variance
ExpectActualCompatibility.Incompatible.ValueParameterCrossinline -> null // inline fun can't be overridden ExpectActualCheckingCompatibility.ValueParameterCrossinline -> null // inline fun can't be overridden
ExpectActualCompatibility.Incompatible.ValueParameterNoinline -> null // inline fun can't be overridden ExpectActualCheckingCompatibility.ValueParameterNoinline -> null // inline fun can't be overridden
ExpectActualCompatibility.Incompatible.ValueParameterVararg -> ExpectActualMemberDiff.Kind.VarargChangedInOverride ExpectActualCheckingCompatibility.ValueParameterVararg -> ExpectActualMemberDiff.Kind.VarargChangedInOverride
ExpectActualCompatibility.Incompatible.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride ExpectActualCheckingCompatibility.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride
} }