[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,
FirModuleData::class,
ExpectActualCompatibility::class,
ExpectActualCompatibility.Incompatible::class,
ExpectActualCompatibility.MismatchOrIncompatible::class,
ExpectActualAnnotationsIncompatibilityType::class,
DeprecationInfo::class,
ApiVersion::class,
@@ -3763,8 +3763,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
NoActualClassMemberForExpectedClassImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic.b.map { pair ->
firSymbolBuilder.buildSymbol(pair.first) to pair.second.mapKeys { (incompatible, _) ->
incompatible
firSymbolBuilder.buildSymbol(pair.first) to pair.second.mapKeys { (mismatchOrIncompatible, _) ->
mismatchOrIncompatible
}.mapValues { (_, collection) ->
collection.map { 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.multiplatform.ExpectActualAnnotationsIncompatibilityType
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.types.Variance
@@ -2630,7 +2630,7 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
interface NoActualClassMemberForExpectedClass : KtFirDiagnostic<KtNamedDeclaration> {
override val diagnosticClass get() = NoActualClassMemberForExpectedClass::class
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> {
@@ -85,7 +85,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
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.types.Variance
@@ -3166,7 +3166,7 @@ internal class AmbiguousExpectsImpl(
internal class NoActualClassMemberForExpectedClassImpl(
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,
token: KtLifetimeToken,
) : 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.multiplatform.ExpectActualAnnotationsIncompatibilityType
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.types.Variance
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) {
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) {
@@ -1285,7 +1284,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error<KtNamedDeclaration>(PositioningStrategy.ACTUAL_DECLARATION_NAME) {
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)
@@ -110,7 +110,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
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.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 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 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_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 EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING by warning0<KtClassLikeDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER)
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.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
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.Compatible
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.resolve.multiplatform.isCompatibleOrWeaklyIncompatible
@Suppress("DuplicatedCode")
@@ -128,9 +129,11 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
val source = declaration.source
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) {
reporter.reportOn(source, FirErrors.ACTUAL_MISSING, context)
}
@@ -140,7 +143,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
val singleIncompatibility = compatibilityToMembersMap.keys.singleOrNull()
when {
singleIncompatibility is Incompatible.ClassScopes -> {
singleIncompatibility is ExpectActualCheckingCompatibility.ClassScopes -> {
require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) {
"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
// later when this checker is called for them
fun hasSingleActualSuspect(
expectedWithIncompatibility: Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>
expectedWithIncompatibility: Pair<FirBasedSymbol<*>, Map<out MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>
): Boolean {
val (expectedMember, incompatibility) = expectedWithIncompatibility
val actualMember = incompatibility.values.singleOrNull()?.singleOrNull()
@OptIn(SymbolInternals::class)
return actualMember != null &&
!incompatibility.allStrongIncompatibilities() &&
!incompatibility.allMismatches() &&
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
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)
} else if (requireActualModifier(declaration.symbol, context.session)) {
reporter.reportOn(
@@ -247,8 +251,8 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
)
}
fun Map<out ExpectActualCompatibility<*>, *>.allStrongIncompatibilities(): Boolean {
return keys.all { it is Incompatible.StrongIncompatible }
fun Map<out ExpectActualCompatibility<*>, *>.allMismatches(): Boolean {
return keys.all { it is ExpectActualMatchingCompatibility.Mismatch }
}
// 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.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCheckingCompatibility
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(
private val mode: MultiplatformDiagnosticRenderingMode
) : ContextIndependentParameterRenderer<Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> {
) : ContextIndependentParameterRenderer<Map<out ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> {
override fun render(
obj: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
obj: Map<out ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
): String {
if (obj.isEmpty()) return ""
@@ -43,9 +44,9 @@ class FirPlatformIncompatibilityDiagnosticRenderer(
class FirIncompatibleExpectedActualClassScopesRenderer(
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(
obj: List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>
obj: List<Pair<FirBasedSymbol<*>, Map<out MismatchOrIncompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>
): String {
if (obj.isEmpty()) return ""
@@ -90,14 +91,14 @@ private fun StringBuilder.renderIncompatibilityInformation(
append("The following declaration")
if (descriptors.size == 1) append(" is") else append("s are")
append(" incompatible")
(compatibility as? Incompatible)?.reason?.let { append(" because $it") }
(compatibility as? MismatchOrIncompatible)?.reason?.let { append(" because $it") }
append(":")
mode.renderList(this, descriptors.map { descriptor ->
{ mode.renderSymbol(this, descriptor, indent) }
})
if (compatibility is Incompatible.ClassScopes) {
if (compatibility is ExpectActualCheckingCompatibility.ClassScopes) {
append(indent)
append("No actual members are found for expected members listed below:")
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.checkers.OptInNames
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.TypeCheckerState
import org.jetbrains.kotlin.types.Variance
@@ -450,7 +451,7 @@ class FirExpectActualMatchingContextImpl private constructor(
return symbol.source == null && symbol.origin !is FirDeclarationOrigin.Plugin
}
override fun onMatchedMembers(
override fun onMatchedOrCompatibleMembers(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?,
@@ -462,13 +463,13 @@ class FirExpectActualMatchingContextImpl private constructor(
expectSymbol.asSymbol(),
actualSymbol.asSymbol(),
containingExpectClassSymbol.asSymbol(),
ExpectActualCompatibility.Compatible
ExpectActualMatchingCompatibility.MatchedSuccessfully
)
}
override fun onMismatchedMembersFromClassScope(
override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?
) {
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.mpp.CallableSymbolMarker
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualChecker
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.getMatchedAndChecked
object FirExpectActualResolver {
fun findExpectForActual(
@@ -67,9 +69,9 @@ object FirExpectActualResolver {
)
}.let {
// 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
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 {
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 EVALUATION_ERROR by error1<PsiElement, String>()
val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1<PsiElement, IrValueParameter>()
@@ -76,7 +76,7 @@ object KtDefaultCommonBackendErrorMessages : BaseDiagnosticRendererFactory() {
}
object BackendDiagnosticRenderers {
val INCOMPATIBILITY = Renderer<ExpectActualCompatibility.Incompatible<*>> {
val INCOMPATIBILITY = Renderer<ExpectActualCompatibility.MismatchOrIncompatible<*>> {
it.reason ?: "<unknown>"
}
val SYMBOL_OWNER_DECLARATION_FQ_NAME = Renderer<IrSymbol> {
@@ -258,9 +258,9 @@ private class ExpectActualLinkCollector : IrElementVisitor<Unit, ExpectActualLin
recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination)
}
override fun onMismatchedMembersFromClassScope(
override fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {
@@ -113,7 +113,7 @@ internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportIncompatibleExpectActual(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
incompatibility: ExpectActualCompatibility.Incompatible<*>
incompatibility: ExpectActualCompatibility.MismatchOrIncompatible<*>
) {
val expectDeclaration = expectSymbol.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,
actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?,
@@ -315,7 +315,8 @@ object AbstractExpectActualAnnotationMatchChecker {
// so we are sure that we found the right member.
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
?: expectToCompatibilityMap.keys.singleOrNull()
?: continue
@@ -12,8 +12,10 @@ import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.Name
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.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.TypeSubstitutorMarker
import org.jetbrains.kotlin.utils.SmartList
@@ -91,8 +93,8 @@ object AbstractExpectActualChecker {
actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
checkClassScopes: Boolean,
): ExpectActualCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes)
?: ExpectActualCompatibility.Compatible
): ExpectActualCheckingCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes)
?: ExpectActualCheckingCompatibility.Compatible
context(ExpectActualMatchingContext<*>)
@Suppress("warnings")
@@ -101,7 +103,7 @@ object AbstractExpectActualChecker {
actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?,
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
require(expectClassSymbol.name == actualClassLikeSymbol.name) {
"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")
}
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) }) {
return Incompatible.ClassModifiers
return ExpectActualCheckingCompatibility.ClassModifiers
}
if (expectClassSymbol.isFun && !actualClass.isFun && actualClass.isNotSamInterface()) {
return Incompatible.FunInterfaceModifier
return ExpectActualCheckingCompatibility.FunInterfaceModifier
}
val expectTypeParameterSymbols = expectClassSymbol.typeParameters
val actualTypeParameterSymbols = actualClass.typeParameters
if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) {
return Incompatible.ClassTypeParameterCount
return ExpectActualCheckingCompatibility.ClassTypeParameterCount
}
if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) {
return Incompatible.Modality
return ExpectActualCheckingCompatibility.Modality
}
if (!areCompatibleClassVisibilities(expectClassSymbol, actualClass)) {
return Incompatible.Visibility
return ExpectActualCheckingCompatibility.Visibility
}
val substitutor = createExpectActualTypeParameterSubstitutor(
@@ -145,14 +147,14 @@ object AbstractExpectActualChecker {
)
if (!areCompatibleTypeParameterUpperBounds(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor)) {
return Incompatible.ClassTypeParameterUpperBounds
return ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds
}
getTypeParametersVarianceOrReifiedIncompatibility(expectTypeParameterSymbols, actualTypeParameterSymbols)
?.let { return it }
if (!areCompatibleSupertypes(expectClassSymbol, actualClass, substitutor)) {
return Incompatible.Supertypes
return ExpectActualCheckingCompatibility.Supertypes
}
if (checkClassScopesCompatibility) {
@@ -213,8 +215,8 @@ object AbstractExpectActualChecker {
expectClassSymbol: RegularClassSymbolMarker,
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): Incompatible.WeakIncompatible<*>? {
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>()
): ExpectActualCheckingCompatibility.Incompatible<*>? {
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>()
val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
@@ -241,14 +243,14 @@ object AbstractExpectActualChecker {
val aEntries = expectClassSymbol.collectEnumEntryNames()
val bEntries = actualClassSymbol.collectEnumEntryNames()
if (!bEntries.containsAll(aEntries)) return Incompatible.EnumEntries
if (!bEntries.containsAll(aEntries)) return ExpectActualCheckingCompatibility.EnumEntries
}
// TODO: check static scope?
if (unfulfilled.isEmpty()) return null
return Incompatible.ClassScopes(unfulfilled)
return ExpectActualCheckingCompatibility.ClassScopes(unfulfilled)
}
context(ExpectActualMatchingContext<*>)
@@ -258,7 +260,7 @@ object AbstractExpectActualChecker {
substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>?,
checkClassScopesCompatibility: Boolean,
) {
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) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> {
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
is ExpectActualCompatibility.MatchedOrCompatible<*> -> {
onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return
}
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
is MismatchOrIncompatible<*> -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
}
context(ExpectActualMatchingContext<*>)
@@ -325,7 +327,7 @@ object AbstractExpectActualChecker {
expectDeclaration 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)
@@ -339,7 +341,7 @@ object AbstractExpectActualChecker {
expectContainingClass,
actualContainingClass
)
?: ExpectActualCompatibility.Compatible
?: ExpectActualCheckingCompatibility.Compatible
}
context(ExpectActualMatchingContext<*>)
@@ -348,27 +350,27 @@ object AbstractExpectActualChecker {
actualDeclaration: CallableSymbolMarker,
insideAnnotationClass: Boolean,
parentSubstitutor: TypeSubstitutorMarker?,
): Incompatible.StrongIncompatible<*>? {
): ExpectActualMatchingCompatibility.Mismatch? {
if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) {
return Incompatible.CallableKind
return ExpectActualMatchingCompatibility.CallableKind
}
val expectedReceiverType = expectDeclaration.extensionReceiverType
val actualReceiverType = actualDeclaration.extensionReceiverType
if ((expectedReceiverType != null) != (actualReceiverType != null)) {
return Incompatible.ParameterShape
return ExpectActualMatchingCompatibility.ParameterShape
}
val expectedValueParameters = expectDeclaration.valueParameters
val actualValueParameters = actualDeclaration.valueParameters
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
return Incompatible.ParameterCount
return ExpectActualMatchingCompatibility.ParameterCount
}
val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters
if (expectedTypeParameters.size != actualTypeParameters.size) {
return Incompatible.FunctionTypeParameterCount
return ExpectActualMatchingCompatibility.FunctionTypeParameterCount
}
val substitutor = createExpectActualTypeParameterSubstitutor(
@@ -388,11 +390,11 @@ object AbstractExpectActualChecker {
parameterOfAnnotationComparisonMode = false
)
) {
return Incompatible.ParameterTypes
return ExpectActualMatchingCompatibility.ParameterTypes
}
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
return Incompatible.FunctionTypeParameterUpperBounds
return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds
}
return null
@@ -406,7 +408,7 @@ object AbstractExpectActualChecker {
parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?,
): Incompatible.WeakIncompatible<*>? {
): ExpectActualCheckingCompatibility<*>? {
val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters
val expectedValueParameters = expectDeclaration.valueParameters
@@ -425,16 +427,16 @@ object AbstractExpectActualChecker {
parameterOfAnnotationComparisonMode = insideAnnotationClass
)
) {
return Incompatible.ReturnType
return ExpectActualCheckingCompatibility.ReturnType
}
}
if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) {
return Incompatible.ParameterNames
return ExpectActualCheckingCompatibility.ParameterNames
}
if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) {
return Incompatible.TypeParameterNames
return ExpectActualCheckingCompatibility.TypeParameterNames
}
val expectModality = expectDeclaration.modality
@@ -447,11 +449,11 @@ object AbstractExpectActualChecker {
actualContainingClass?.modality
)
) {
return Incompatible.Modality
return ExpectActualCheckingCompatibility.Modality
}
if (!areCompatibleCallableVisibilities(expectDeclaration.visibility, expectModality, actualDeclaration.visibility)) {
return Incompatible.Visibility
return ExpectActualCheckingCompatibility.Visibility
}
getTypeParametersVarianceOrReifiedIncompatibility(expectedTypeParameters, actualTypeParameters)?.let { return it }
@@ -467,22 +469,22 @@ object AbstractExpectActualChecker {
(actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet())
.flatMap { it.valueParameters }.any { it.hasDefaultValue }
) {
return Incompatible.ActualFunctionWithDefaultParameters
return ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters
}
}
}
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
if (expectDeclaration is SimpleFunctionSymbolMarker && expectDeclaration.isInline) {
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 }) {
return Incompatible.ValueParameterCrossinline
return ExpectActualCheckingCompatibility.ValueParameterCrossinline
}
}
@@ -634,9 +636,9 @@ object AbstractExpectActualChecker {
private fun getTypeParametersVarianceOrReifiedIncompatibility(
expectTypeParameterSymbols: List<TypeParameterSymbolMarker>,
actualTypeParameterSymbols: List<TypeParameterSymbolMarker>,
): Incompatible.WeakIncompatible<*>? {
): ExpectActualCheckingCompatibility.Incompatible<*>? {
if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) {
return Incompatible.TypeParameterVariance
return ExpectActualCheckingCompatibility.TypeParameterVariance
}
// Removing "reified" from an expected function's type parameter is fine
@@ -645,7 +647,7 @@ object AbstractExpectActualChecker {
!expectTypeParameterSymbols[i].isReified && actualTypeParameterSymbols[i].isReified
}
) {
return Incompatible.TypeParameterReified
return ExpectActualCheckingCompatibility.TypeParameterReified
}
return null
@@ -655,9 +657,9 @@ object AbstractExpectActualChecker {
private fun getFunctionsIncompatibility(
expectFunction: CallableSymbolMarker,
actualFunction: CallableSymbolMarker,
): Incompatible.WeakIncompatible<*>? {
): ExpectActualCheckingCompatibility<*>? {
if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) {
return Incompatible.FunctionModifiersDifferent
return ExpectActualCheckingCompatibility.FunctionModifiersDifferent
}
if (
@@ -665,7 +667,7 @@ object AbstractExpectActualChecker {
expectFunction.isInline && !actualFunction.isInline ||
expectFunction.isOperator && !actualFunction.isOperator
) {
return Incompatible.FunctionModifiersNotSubset
return ExpectActualCheckingCompatibility.FunctionModifiersNotSubset
}
return null
@@ -675,12 +677,12 @@ object AbstractExpectActualChecker {
private fun getPropertiesIncompatibility(
expected: PropertySymbolMarker,
actual: PropertySymbolMarker,
): Incompatible.WeakIncompatible<*>? {
): ExpectActualCheckingCompatibility<*>? {
return when {
!equalBy(expected, actual) { p -> p.isVar } -> Incompatible.PropertyKind
!equalBy(expected, actual) { p -> p.isLateinit } -> Incompatible.PropertyLateinitModifier
expected.isConst && !actual.isConst -> Incompatible.PropertyConstModifier
!arePropertySettersWithCompatibleVisibilities(expected, actual) -> Incompatible.PropertySetterVisibility
!equalBy(expected, actual) { p -> p.isVar } -> ExpectActualCheckingCompatibility.PropertyKind
!equalBy(expected, actual) { p -> p.isLateinit } -> ExpectActualCheckingCompatibility.PropertyLateinitModifier
expected.isConst && !actual.isConst -> ExpectActualCheckingCompatibility.PropertyConstModifier
!arePropertySettersWithCompatibleVisibilities(expected, actual) -> ExpectActualCheckingCompatibility.PropertySetterVisibility
else -> null
}
}
@@ -10,7 +10,8 @@ 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.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.TypeSubstitutorMarker
import org.jetbrains.kotlin.utils.SmartList
@@ -66,11 +67,11 @@ object AbstractExpectActualMatcher {
}
}
fun <T : DeclarationSymbolMarker> matchClassifiers(
fun matchClassifiers(
expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker,
context: ExpectActualMatchingContext<T>,
): ExpectActualCompatibility<T> = with(context) {
context: ExpectActualMatchingContext<*>,
): 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
check(expectClassSymbol.name == actualClassLikeSymbol.name) {
"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) {
"Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol"
}
ExpectActualCompatibility.Compatible
ExpectActualMatchingCompatibility.MatchedSuccessfully
}
context(ExpectActualMatchingContext<*>)
@@ -117,7 +118,7 @@ object AbstractExpectActualMatcher {
substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<MismatchOrIncompatible<*>, List<DeclarationSymbolMarker?>>>>?,
) {
val mapping = actualMembers.keysToMap { actualMember ->
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) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> {
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
ExpectActualMatchingCompatibility.MatchedSuccessfully -> {
onMatchedOrCompatibleMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
return
}
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
is ExpectActualMatchingCompatibility.Mismatch -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
onMismatchedOrIncompatibleMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
}
context(ExpectActualMatchingContext<*>)
@@ -159,7 +160,7 @@ object AbstractExpectActualMatcher {
parentSubstitutor: TypeSubstitutorMarker?,
expectContainingClass: RegularClassSymbolMarker?,
actualContainingClass: RegularClassSymbolMarker?,
): ExpectActualCompatibility<*> {
): ExpectActualMatchingCompatibility {
require(
(expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) ||
expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName
@@ -177,12 +178,12 @@ object AbstractExpectActualMatcher {
expectDeclaration is ConstructorSymbolMarker &&
actualDeclaration is ConstructorSymbolMarker
) {
return ExpectActualCompatibility.Compatible
return ExpectActualMatchingCompatibility.MatchedSuccessfully
}
val annotationMode = expectContainingClass?.classKind == ClassKind.ANNOTATION_CLASS
return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, annotationMode, parentSubstitutor)
?: ExpectActualCompatibility.Compatible
?: ExpectActualMatchingCompatibility.MatchedSuccessfully
}
context(ExpectActualMatchingContext<*>)
@@ -191,27 +192,27 @@ object AbstractExpectActualMatcher {
actualDeclaration: CallableSymbolMarker,
insideAnnotationClass: Boolean,
parentSubstitutor: TypeSubstitutorMarker?,
): Incompatible.StrongIncompatible<*>? {
): ExpectActualMatchingCompatibility.Mismatch? {
if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) {
return Incompatible.CallableKind
return ExpectActualMatchingCompatibility.CallableKind
}
val expectedReceiverType = expectDeclaration.extensionReceiverType
val actualReceiverType = actualDeclaration.extensionReceiverType
if ((expectedReceiverType != null) != (actualReceiverType != null)) {
return Incompatible.ParameterShape
return ExpectActualMatchingCompatibility.ParameterShape
}
val expectedValueParameters = expectDeclaration.valueParameters
val actualValueParameters = actualDeclaration.valueParameters
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
return Incompatible.ParameterCount
return ExpectActualMatchingCompatibility.ParameterCount
}
val expectedTypeParameters = expectDeclaration.typeParameters
val actualTypeParameters = actualDeclaration.typeParameters
if (expectedTypeParameters.size != actualTypeParameters.size) {
return Incompatible.FunctionTypeParameterCount
return ExpectActualMatchingCompatibility.FunctionTypeParameterCount
}
val substitutor = createExpectActualTypeParameterSubstitutor(
@@ -231,11 +232,11 @@ object AbstractExpectActualMatcher {
parameterOfAnnotationComparisonMode = false
)
) {
return Incompatible.ParameterTypes
return ExpectActualMatchingCompatibility.ParameterTypes
}
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
return Incompatible.FunctionTypeParameterUpperBounds
return ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds
}
return null
@@ -165,16 +165,16 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
val CallableSymbolMarker.hasStableParameterNames: Boolean
fun onMatchedMembers(
fun onMatchedOrCompatibleMembers(
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {}
fun onMismatchedMembersFromClassScope(
fun onMismatchedOrIncompatibleMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.MismatchOrIncompatible<*>, List<DeclarationSymbolMarker>>,
containingExpectClassSymbol: RegularClassSymbolMarker?,
containingActualClassSymbol: RegularClassSymbolMarker?,
) {}
@@ -5,103 +5,104 @@
package org.jetbrains.kotlin.resolve.multiplatform
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
private const val TYPE_PARAMETER_COUNT = "number of type parameters is different"
sealed class ExpectActualCompatibility<out D> {
// Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer
sealed class Incompatible<out D>(val reason: String?) : ExpectActualCompatibility<D>() {
sealed class WeakIncompatible<out D>(reason: String?) : Incompatible<D>(reason)
// For StrongIncompatible `actual` declaration is considered as overload and error reports on expected declaration
sealed class StrongIncompatible<out D>(reason: String?) : Incompatible<D>(reason)
// 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"
)
// 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 interface ExpectActualCompatibility<out D> {
/**
* DON'T USE THIS CLASS. This class is currently used only in diagnostics. Eventually, it will go away KT-62631
*/
sealed interface MismatchOrIncompatible<out D> : ExpectActualCompatibility<D> {
val reason: String?
}
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
get() = this is ExpectActualCompatibility.Compatible
|| this is ExpectActualCompatibility.Incompatible.WeakIncompatible
get() = this is ExpectActualCompatibility.MatchedOrCompatible || this is ExpectActualCheckingCompatibility
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) {
ExpectActualCompatibility.Incompatible.CallableKind,
ExpectActualCompatibility.Incompatible.ParameterCount,
ExpectActualCompatibility.Incompatible.ParameterShape,
ExpectActualCompatibility.Incompatible.ParameterTypes,
ExpectActualCompatibility.Incompatible.FunctionTypeParameterCount,
ExpectActualCompatibility.Incompatible.FunctionTypeParameterUpperBounds,
fun ExpectActualCompatibility.MismatchOrIncompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) {
ExpectActualMatchingCompatibility.CallableKind,
ExpectActualMatchingCompatibility.ParameterCount,
ExpectActualMatchingCompatibility.ParameterShape,
ExpectActualMatchingCompatibility.ParameterTypes,
ExpectActualMatchingCompatibility.FunctionTypeParameterCount,
ExpectActualMatchingCompatibility.FunctionTypeParameterUpperBounds,
// It's an awful API. It will be fixed in KT-62752
-> error("It's not allowed to call this function with receiver: $this")
ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
ExpectActualCompatibility.Incompatible.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override
ExpectActualCompatibility.Incompatible.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members")
is ExpectActualCompatibility.Incompatible.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.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)
ExpectActualCompatibility.Incompatible.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride
ExpectActualCompatibility.Incompatible.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride
ExpectActualCompatibility.Incompatible.PropertyConstModifier -> null // const fun can't be overridden
ExpectActualCompatibility.Incompatible.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride
ExpectActualCompatibility.Incompatible.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride
ExpectActualCompatibility.Incompatible.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride
ExpectActualCompatibility.Incompatible.Supertypes -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCompatibility.Incompatible.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride
ExpectActualCompatibility.Incompatible.TypeParameterReified -> null // inline fun can't be overridden
ExpectActualCompatibility.Incompatible.TypeParameterVariance -> null // Members are not allowed to have variance
ExpectActualCompatibility.Incompatible.ValueParameterCrossinline -> null // inline fun can't be overridden
ExpectActualCompatibility.Incompatible.ValueParameterNoinline -> null // inline fun can't be overridden
ExpectActualCompatibility.Incompatible.ValueParameterVararg -> ExpectActualMemberDiff.Kind.VarargChangedInOverride
ExpectActualCompatibility.Incompatible.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride
ExpectActualCheckingCompatibility.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
ExpectActualCheckingCompatibility.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override
ExpectActualCheckingCompatibility.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members")
is ExpectActualCheckingCompatibility.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.FunInterfaceModifier -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier (suspend)
ExpectActualCheckingCompatibility.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier (infix, inline, operator)
ExpectActualCheckingCompatibility.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride
ExpectActualCheckingCompatibility.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride
ExpectActualCheckingCompatibility.PropertyConstModifier -> null // const fun can't be overridden
ExpectActualCheckingCompatibility.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride
ExpectActualCheckingCompatibility.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride
ExpectActualCheckingCompatibility.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride
ExpectActualCheckingCompatibility.Supertypes -> error("Not applicable because ExpectActualMemberDiff is about members")
ExpectActualCheckingCompatibility.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride
ExpectActualCheckingCompatibility.TypeParameterReified -> null // inline fun can't be overridden
ExpectActualCheckingCompatibility.TypeParameterVariance -> null // Members are not allowed to have variance
ExpectActualCheckingCompatibility.ValueParameterCrossinline -> null // inline fun can't be overridden
ExpectActualCheckingCompatibility.ValueParameterNoinline -> null // inline fun can't be overridden
ExpectActualCheckingCompatibility.ValueParameterVararg -> ExpectActualMemberDiff.Kind.VarargChangedInOverride
ExpectActualCheckingCompatibility.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride
}