1/2 Extract K1 KMP matcher in its own subsystem. Copy files
This commit is literally this shell command: ``` cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualAnnotationMatchChecker.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1AbstractExpectActualAnnotationMatchChecker.kt cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1AbstractExpectActualCompatibilityChecker.kt cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualCollectionArgumentsCompatibilityCheckStrategy.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualCollectionArgumentsCompatibilityCheckStrategy.kt cp core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualCompatibility.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualCompatibility.kt cp compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualMatchingContext.kt cp core/compiler.common/src/org/jetbrains/kotlin/resolve/multiplatform/ExpectActualMemberDiff.kt compiler/resolution/src/org/jetbrains/kotlin/resolve/multiplatform/K1ExpectActualMemberDiff.kt ``` Motivation: KMP is going to evolve in K2 a lot. But we don't want to touch K1 version of KMP. That's why it's easeir to just copy-paste expect-actual matcher into K1 flavor
This commit is contained in:
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.mpp
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.mpp.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType as IncompatibilityType
|
||||
|
||||
object AbstractExpectActualAnnotationMatchChecker {
|
||||
private val SKIPPED_CLASS_IDS = setOf(
|
||||
StandardClassIds.Annotations.Deprecated,
|
||||
StandardClassIds.Annotations.DeprecatedSinceKotlin,
|
||||
StandardClassIds.Annotations.ImplicitlyActualizedByJvmDeclaration,
|
||||
StandardClassIds.Annotations.OptionalExpectation,
|
||||
StandardClassIds.Annotations.RequireKotlin,
|
||||
StandardClassIds.Annotations.SinceKotlin,
|
||||
StandardClassIds.Annotations.Suppress,
|
||||
StandardClassIds.Annotations.WasExperimental,
|
||||
OptInNames.OPT_IN_CLASS_ID,
|
||||
)
|
||||
|
||||
class Incompatibility(
|
||||
/**
|
||||
* [expectSymbol] and [actualSymbol] are declaration symbols where annotation been mismatched.
|
||||
* They are needed for writing whole declarations in diagnostic text.
|
||||
* They are not the same as symbols passed to checker as arguments in [areAnnotationsCompatible] in following cases:
|
||||
* 1. If [actualSymbol] is typealias, it will be expanded.
|
||||
* 2. If problem is in class member, [expectSymbol] will be mismatched member, not the original class.
|
||||
* 3. If annotation mismatched on function value parameter, symbols will be whole functions, not value parameter symbols.
|
||||
*/
|
||||
val expectSymbol: DeclarationSymbolMarker,
|
||||
val actualSymbol: DeclarationSymbolMarker,
|
||||
|
||||
/**
|
||||
* Link to source code element (possibly holding null, if no source) from actual declaration
|
||||
* where mismatched actual annotation is set (or should be set if it is missing).
|
||||
* Needed for the implementation of IDE intention.
|
||||
*/
|
||||
val actualAnnotationTargetElement: SourceElementMarker,
|
||||
val type: IncompatibilityType<ExpectActualMatchingContext.AnnotationCallInfo>,
|
||||
)
|
||||
|
||||
fun areAnnotationsCompatible(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbol: DeclarationSymbolMarker,
|
||||
context: ExpectActualMatchingContext<*>,
|
||||
): Incompatibility? = with(context) {
|
||||
areAnnotationsCompatible(expectSymbol, actualSymbol)
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun areAnnotationsCompatible(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbol: DeclarationSymbolMarker,
|
||||
): Incompatibility? {
|
||||
return when (expectSymbol) {
|
||||
is CallableSymbolMarker -> {
|
||||
areCallableAnnotationsCompatible(expectSymbol, actualSymbol as CallableSymbolMarker)
|
||||
}
|
||||
is RegularClassSymbolMarker -> {
|
||||
areClassAnnotationsCompatible(expectSymbol, actualSymbol as ClassLikeSymbolMarker)
|
||||
}
|
||||
else -> error("Incorrect types: $expectSymbol $actualSymbol")
|
||||
}
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun areCallableAnnotationsCompatible(
|
||||
expectSymbol: CallableSymbolMarker,
|
||||
actualSymbol: CallableSymbolMarker,
|
||||
): Incompatibility? {
|
||||
commonForClassAndCallableChecks(expectSymbol, actualSymbol)?.let { return it }
|
||||
areAnnotationsOnValueParametersCompatible(expectSymbol, actualSymbol)?.let { return it }
|
||||
|
||||
if (expectSymbol is PropertySymbolMarker && actualSymbol is PropertySymbolMarker) {
|
||||
arePropertyGetterAndSetterAnnotationsCompatible(expectSymbol, actualSymbol)?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun arePropertyGetterAndSetterAnnotationsCompatible(
|
||||
expectSymbol: PropertySymbolMarker,
|
||||
actualSymbol: PropertySymbolMarker,
|
||||
): Incompatibility? {
|
||||
listOf(
|
||||
expectSymbol.getter to actualSymbol.getter,
|
||||
expectSymbol.setter to actualSymbol.setter,
|
||||
).forEach { (expectAccessor, actualAccessor) ->
|
||||
if (expectAccessor != null && actualAccessor != null) {
|
||||
areAnnotationsSetOnDeclarationsCompatible(expectAccessor, actualAccessor)?.let {
|
||||
// Write containing declarations into diagnostic
|
||||
return Incompatibility(expectSymbol, actualSymbol, actualAccessor.getSourceElement(), it.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun areClassAnnotationsCompatible(
|
||||
expectSymbol: RegularClassSymbolMarker,
|
||||
actualSymbol: ClassLikeSymbolMarker,
|
||||
): Incompatibility? {
|
||||
if (actualSymbol is TypeAliasSymbolMarker) {
|
||||
val expanded = actualSymbol.expandToRegularClass() ?: return null
|
||||
return areClassAnnotationsCompatible(expectSymbol, expanded)
|
||||
}
|
||||
check(actualSymbol is RegularClassSymbolMarker)
|
||||
|
||||
commonForClassAndCallableChecks(expectSymbol, actualSymbol)?.let { return it }
|
||||
|
||||
if (checkClassScopesForAnnotationCompatibility) {
|
||||
checkAnnotationsInClassMemberScope(expectSymbol, actualSymbol)?.let { return it }
|
||||
}
|
||||
if (expectSymbol.classKind == ClassKind.ENUM_CLASS && actualSymbol.classKind == ClassKind.ENUM_CLASS) {
|
||||
checkAnnotationsOnEnumEntries(expectSymbol, actualSymbol)?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun commonForClassAndCallableChecks(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbol: DeclarationSymbolMarker,
|
||||
): Incompatibility? {
|
||||
areAnnotationsSetOnDeclarationsCompatible(expectSymbol, actualSymbol)?.let { return it }
|
||||
areAnnotationsOnTypeParametersCompatible(expectSymbol, actualSymbol)?.let { return it }
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun areAnnotationsOnValueParametersCompatible(
|
||||
expectSymbol: CallableSymbolMarker,
|
||||
actualSymbol: CallableSymbolMarker,
|
||||
): Incompatibility? {
|
||||
val expectParams = expectSymbol.valueParameters
|
||||
val actualParams = actualSymbol.valueParameters
|
||||
|
||||
if (expectParams.size != actualParams.size) return null
|
||||
|
||||
return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) ->
|
||||
areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let {
|
||||
// Write containing declarations into diagnostic
|
||||
Incompatibility(expectSymbol, actualSymbol, actualParam.getSourceElement(), it.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun areAnnotationsOnTypeParametersCompatible(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbol: DeclarationSymbolMarker,
|
||||
): Incompatibility? {
|
||||
fun DeclarationSymbolMarker.getTypeParameters(): List<TypeParameterSymbolMarker>? {
|
||||
return when (this) {
|
||||
is FunctionSymbolMarker -> typeParameters
|
||||
is RegularClassSymbolMarker -> typeParameters
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val expectParams = expectSymbol.getTypeParameters() ?: return null
|
||||
val actualParams = actualSymbol.getTypeParameters() ?: return null
|
||||
if (expectParams.size != actualParams.size) return null
|
||||
|
||||
return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) ->
|
||||
areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let {
|
||||
// Write containing declarations into diagnostic
|
||||
Incompatibility(expectSymbol, actualSymbol, actualParam.getSourceElement(), it.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun areAnnotationsSetOnDeclarationsCompatible(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbol: DeclarationSymbolMarker,
|
||||
): Incompatibility? {
|
||||
// TODO(Roman.Efremov, KT-60671): check annotations set on types
|
||||
|
||||
val skipSourceAnnotations = actualSymbol.hasSourceAnnotationsErased
|
||||
val actualAnnotationsByName = actualSymbol.annotations.groupBy { it.classId }
|
||||
|
||||
for (expectAnnotation in expectSymbol.annotations) {
|
||||
val expectClassId = expectAnnotation.classId ?: continue
|
||||
if (expectClassId in SKIPPED_CLASS_IDS || expectAnnotation.isOptIn) {
|
||||
continue
|
||||
}
|
||||
if (expectAnnotation.isRetentionSource && skipSourceAnnotations) {
|
||||
continue
|
||||
}
|
||||
val actualAnnotationsWithSameClassId = actualAnnotationsByName[expectClassId] ?: emptyList()
|
||||
if (actualAnnotationsWithSameClassId.isEmpty()) {
|
||||
return Incompatibility(
|
||||
expectSymbol,
|
||||
actualSymbol,
|
||||
actualSymbol.getSourceElement(),
|
||||
IncompatibilityType.MissingOnActual(expectAnnotation)
|
||||
)
|
||||
}
|
||||
val collectionCompatibilityChecker = getAnnotationCollectionArgumentsCompatibilityChecker(expectClassId)
|
||||
if (actualAnnotationsWithSameClassId.none {
|
||||
areAnnotationArgumentsEqual(expectAnnotation, it, collectionCompatibilityChecker)
|
||||
}) {
|
||||
val incompatibilityType = if (actualAnnotationsWithSameClassId.size == 1) {
|
||||
IncompatibilityType.DifferentOnActual(expectAnnotation, actualAnnotationsWithSameClassId.single())
|
||||
} else {
|
||||
// In the case of repeatable annotations, we can't choose on which to report
|
||||
IncompatibilityType.MissingOnActual(expectAnnotation)
|
||||
}
|
||||
return Incompatibility(expectSymbol, actualSymbol, actualSymbol.getSourceElement(), incompatibilityType)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getAnnotationCollectionArgumentsCompatibilityChecker(annotationClassId: ClassId):
|
||||
ExpectActualCollectionArgumentsCompatibilityCheckStrategy {
|
||||
return if (annotationClassId == StandardClassIds.Annotations.Target) {
|
||||
ExpectActualCollectionArgumentsCompatibilityCheckStrategy.ExpectIsSubsetOfActual
|
||||
} else {
|
||||
ExpectActualCollectionArgumentsCompatibilityCheckStrategy.Default
|
||||
}
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun checkAnnotationsInClassMemberScope(
|
||||
expectClass: RegularClassSymbolMarker,
|
||||
actualClass: RegularClassSymbolMarker,
|
||||
): Incompatibility? {
|
||||
for (actualMember in actualClass.collectAllMembers(isActualDeclaration = true)) {
|
||||
if (skipCheckingAnnotationsOfActualClassMember(actualMember)) {
|
||||
continue
|
||||
}
|
||||
val expectToCompatibilityMap = findPotentialExpectClassMembersForActual(
|
||||
expectClass, actualClass, actualMember,
|
||||
// Optimization: don't check class scopes, because:
|
||||
// 1. Annotation checker runs no matter if found expect class is compatible or not.
|
||||
// 2. Class always has at most one corresponding `expect` class (unlike for functions, which may have several overrides),
|
||||
// so we are sure that we found the right member.
|
||||
checkClassScopesCompatibility = false,
|
||||
)
|
||||
val expectMember = expectToCompatibilityMap.filter { it.value == ExpectActualCompatibility.Compatible }.keys.singleOrNull()
|
||||
// Check also incompatible members if only one is found
|
||||
?: expectToCompatibilityMap.keys.singleOrNull()
|
||||
?: continue
|
||||
areAnnotationsCompatible(expectMember, actualMember)?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
context (ExpectActualMatchingContext<*>)
|
||||
private fun checkAnnotationsOnEnumEntries(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassSymbol: RegularClassSymbolMarker,
|
||||
): Incompatibility? {
|
||||
fun DeclarationSymbolMarker.getEnumEntryName(): Name =
|
||||
when (this) {
|
||||
is CallableSymbolMarker -> callableId.callableName
|
||||
is RegularClassSymbolMarker -> classId.shortClassName
|
||||
else -> error("Unexpected type $this")
|
||||
}
|
||||
|
||||
val expectEnumEntries = expectClassSymbol.collectEnumEntries()
|
||||
val actualEnumEntriesByName = actualClassSymbol.collectEnumEntries().associateBy { it.getEnumEntryName() }
|
||||
|
||||
for (expectEnumEntry in expectEnumEntries) {
|
||||
val actualEnumEntry = actualEnumEntriesByName[expectEnumEntry.getEnumEntryName()] ?: continue
|
||||
areAnnotationsSetOnDeclarationsCompatible(expectEnumEntry, actualEnumEntry)
|
||||
?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
+693
@@ -0,0 +1,693 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.mpp
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
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.ExpectActualCompatibility
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.enumMapOf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.enumSetOf
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import java.util.*
|
||||
|
||||
object AbstractExpectActualCompatibilityChecker {
|
||||
fun <T : DeclarationSymbolMarker> getClassifiersCompatibility(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassLikeSymbol: ClassLikeSymbolMarker,
|
||||
checkClassScopesCompatibility: Boolean,
|
||||
context: ExpectActualMatchingContext<T>,
|
||||
): ExpectActualCompatibility<T> {
|
||||
val result = with(context) {
|
||||
getClassifiersCompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor = null, checkClassScopesCompatibility)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return result as ExpectActualCompatibility<T>
|
||||
}
|
||||
|
||||
fun <T : DeclarationSymbolMarker> getCallablesCompatibility(
|
||||
expectDeclaration: CallableSymbolMarker,
|
||||
actualDeclaration: CallableSymbolMarker,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
expectContainingClass: RegularClassSymbolMarker?,
|
||||
actualContainingClass: RegularClassSymbolMarker?,
|
||||
context: ExpectActualMatchingContext<T>,
|
||||
): ExpectActualCompatibility<T> {
|
||||
val result = with(context) {
|
||||
getCallablesCompatibility(expectDeclaration, actualDeclaration, parentSubstitutor, expectContainingClass, actualContainingClass)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return result as ExpectActualCompatibility<T>
|
||||
}
|
||||
|
||||
fun <T : DeclarationSymbolMarker> matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
|
||||
expectDeclaration: DeclarationSymbolMarker,
|
||||
actualDeclarations: List<DeclarationSymbolMarker>,
|
||||
context: ExpectActualMatchingContext<T>,
|
||||
) {
|
||||
with(context) {
|
||||
matchSingleExpectAgainstPotentialActuals(
|
||||
expectDeclaration,
|
||||
actualDeclarations,
|
||||
substitutor = null,
|
||||
expectClassSymbol = null,
|
||||
actualClassSymbol = null,
|
||||
unfulfilled = null,
|
||||
checkClassScopesCompatibility = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
@Suppress("warnings")
|
||||
private fun getClassifiersCompatibility(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassLikeSymbol: ClassLikeSymbolMarker,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
checkClassScopes: Boolean,
|
||||
): ExpectActualCompatibility<*> = getClassifiersIncompatibility(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor, checkClassScopes)
|
||||
?: ExpectActualCompatibility.Compatible
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
@Suppress("warnings")
|
||||
private fun getClassifiersIncompatibility(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassLikeSymbol: ClassLikeSymbolMarker,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
checkClassScopesCompatibility: Boolean,
|
||||
): ExpectActualCompatibility.Incompatible.WeakIncompatible<*>? {
|
||||
// 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"
|
||||
}
|
||||
|
||||
val actualClass = when (actualClassLikeSymbol) {
|
||||
is RegularClassSymbolMarker -> actualClassLikeSymbol
|
||||
is TypeAliasSymbolMarker -> actualClassLikeSymbol.expandToRegularClass()
|
||||
?: return null // do not report extra error on erroneous typealias
|
||||
else -> error("Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol")
|
||||
}
|
||||
|
||||
if (!areCompatibleClassKinds(expectClassSymbol, actualClass)) return Incompatible.ClassKind
|
||||
|
||||
if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline || it.isValue) }) {
|
||||
return Incompatible.ClassModifiers
|
||||
}
|
||||
|
||||
if (expectClassSymbol.isFun && !actualClass.isFun && actualClass.isNotSamInterface()) {
|
||||
return Incompatible.FunInterfaceModifier
|
||||
}
|
||||
|
||||
val expectTypeParameterSymbols = expectClassSymbol.typeParameters
|
||||
val actualTypeParameterSymbols = actualClass.typeParameters
|
||||
if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) {
|
||||
return Incompatible.ClassTypeParameterCount
|
||||
}
|
||||
|
||||
if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) {
|
||||
return Incompatible.Modality
|
||||
}
|
||||
|
||||
if (!areCompatibleClassVisibilities(expectClassSymbol, actualClass)) {
|
||||
return Incompatible.Visibility
|
||||
}
|
||||
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameterSymbols,
|
||||
actualTypeParameterSymbols,
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
if (!areCompatibleTypeParameterUpperBounds(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor)) {
|
||||
return Incompatible.ClassTypeParameterUpperBounds
|
||||
}
|
||||
|
||||
getTypeParametersVarianceOrReifiedIncompatibility(expectTypeParameterSymbols, actualTypeParameterSymbols)
|
||||
?.let { return it }
|
||||
|
||||
if (!areCompatibleSupertypes(expectClassSymbol, actualClass, substitutor)) {
|
||||
return Incompatible.Supertypes
|
||||
}
|
||||
|
||||
if (checkClassScopesCompatibility) {
|
||||
getClassScopesIncompatibility(expectClassSymbol, actualClass, substitutor)?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun areCompatibleSupertypes(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassSymbol: RegularClassSymbolMarker,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Boolean {
|
||||
return when (allowTransitiveSupertypesActualization) {
|
||||
false -> areCompatibleSupertypesOneByOne(expectClassSymbol, actualClassSymbol, substitutor)
|
||||
true -> areCompatibleSupertypesTransitive(expectClassSymbol, actualClassSymbol, substitutor)
|
||||
}
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun areCompatibleSupertypesOneByOne(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassSymbol: RegularClassSymbolMarker,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Boolean {
|
||||
// Subtract kotlin.Any from supertypes because it's implicitly added if no explicit supertype is specified,
|
||||
// and not added if an explicit supertype _is_ specified
|
||||
val expectSupertypes = expectClassSymbol.superTypes.filterNot { it.typeConstructor().isAnyConstructor() }
|
||||
val actualSupertypes = actualClassSymbol.superTypes.filterNot { it.typeConstructor().isAnyConstructor() }
|
||||
return expectSupertypes.all { expectSupertype ->
|
||||
val substitutedExpectType = substitutor.safeSubstitute(expectSupertype)
|
||||
actualSupertypes.any { actualSupertype ->
|
||||
areCompatibleExpectActualTypes(substitutedExpectType, actualSupertype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun areCompatibleSupertypesTransitive(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassSymbol: RegularClassSymbolMarker,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Boolean {
|
||||
val expectSupertypes = expectClassSymbol.superTypes.filterNot { it.typeConstructor().isAnyConstructor() }
|
||||
val actualType = actualClassSymbol.defaultType
|
||||
return expectSupertypes.all { expectSupertype ->
|
||||
actualTypeIsSubtypeOfExpectType(
|
||||
expectType = substitutor.safeSubstitute(expectSupertype),
|
||||
actualType = actualType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun getClassScopesIncompatibility(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassSymbol: RegularClassSymbolMarker,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Incompatible.WeakIncompatible<*>? {
|
||||
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>()
|
||||
|
||||
val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
|
||||
|
||||
outer@ for (expectMember in expectClassSymbol.collectAllMembers(isActualDeclaration = false)) {
|
||||
if (expectMember is CallableSymbolMarker && expectMember.shouldSkipMatching(expectClassSymbol)) continue
|
||||
|
||||
val actualMembers = actualMembersByName[expectMember.name]?.filter { actualMember ->
|
||||
expectMember is CallableSymbolMarker && actualMember is CallableSymbolMarker ||
|
||||
expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker
|
||||
}.orEmpty()
|
||||
|
||||
matchSingleExpectAgainstPotentialActuals(
|
||||
expectMember,
|
||||
actualMembers,
|
||||
substitutor,
|
||||
expectClassSymbol,
|
||||
actualClassSymbol,
|
||||
unfulfilled,
|
||||
checkClassScopesCompatibility = true,
|
||||
)
|
||||
}
|
||||
|
||||
if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
|
||||
val aEntries = expectClassSymbol.collectEnumEntryNames()
|
||||
val bEntries = actualClassSymbol.collectEnumEntryNames()
|
||||
|
||||
if (!bEntries.containsAll(aEntries)) return Incompatible.EnumEntries
|
||||
}
|
||||
|
||||
// TODO: check static scope?
|
||||
|
||||
if (unfulfilled.isEmpty()) return null
|
||||
|
||||
return Incompatible.ClassScopes(unfulfilled)
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun matchSingleExpectAgainstPotentialActuals(
|
||||
expectMember: DeclarationSymbolMarker,
|
||||
actualMembers: List<DeclarationSymbolMarker>,
|
||||
substitutor: TypeSubstitutorMarker?,
|
||||
expectClassSymbol: RegularClassSymbolMarker?,
|
||||
actualClassSymbol: RegularClassSymbolMarker?,
|
||||
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>?,
|
||||
checkClassScopesCompatibility: Boolean,
|
||||
) {
|
||||
val mapping = actualMembers.keysToMap { actualMember ->
|
||||
when (expectMember) {
|
||||
is CallableSymbolMarker -> getCallablesCompatibility(
|
||||
expectMember,
|
||||
actualMember as CallableSymbolMarker,
|
||||
substitutor,
|
||||
expectClassSymbol,
|
||||
actualClassSymbol
|
||||
)
|
||||
|
||||
is RegularClassSymbolMarker -> {
|
||||
val parentSubstitutor = substitutor?.takeIf { !innerClassesCapturesOuterTypeParameters }
|
||||
getClassifiersCompatibility(
|
||||
expectMember,
|
||||
actualMember as ClassLikeSymbolMarker,
|
||||
parentSubstitutor,
|
||||
checkClassScopesCompatibility,
|
||||
)
|
||||
}
|
||||
else -> error("Unsupported declaration: $expectMember ($actualMembers)")
|
||||
}
|
||||
}
|
||||
|
||||
val incompatibilityMap = mutableMapOf<Incompatible<*>, MutableList<DeclarationSymbolMarker>>()
|
||||
for ((actualMember, compatibility) in mapping) {
|
||||
when (compatibility) {
|
||||
ExpectActualCompatibility.Compatible -> {
|
||||
onMatchedMembers(expectMember, actualMember, expectClassSymbol, actualClassSymbol)
|
||||
return
|
||||
}
|
||||
|
||||
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
|
||||
}
|
||||
}
|
||||
|
||||
unfulfilled?.add(expectMember to incompatibilityMap)
|
||||
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap, expectClassSymbol, actualClassSymbol)
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun getCallablesCompatibility(
|
||||
expectDeclaration: CallableSymbolMarker,
|
||||
actualDeclaration: CallableSymbolMarker,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
expectContainingClass: RegularClassSymbolMarker?,
|
||||
actualContainingClass: RegularClassSymbolMarker?,
|
||||
): ExpectActualCompatibility<*> {
|
||||
require(
|
||||
(expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) ||
|
||||
expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName
|
||||
) {
|
||||
"This function should be invoked only for declarations with the same name: $expectDeclaration, $actualDeclaration"
|
||||
}
|
||||
require((expectDeclaration.dispatchReceiverType == null) == (actualDeclaration.dispatchReceiverType == null)) {
|
||||
"This function should be invoked only for declarations in the same kind of container (both members or both top level): $expectDeclaration, $actualDeclaration"
|
||||
}
|
||||
|
||||
if (
|
||||
enumConstructorsAreAlwaysCompatible &&
|
||||
expectContainingClass?.classKind == ClassKind.ENUM_CLASS &&
|
||||
actualContainingClass?.classKind == ClassKind.ENUM_CLASS &&
|
||||
expectDeclaration is ConstructorSymbolMarker &&
|
||||
actualDeclaration is ConstructorSymbolMarker
|
||||
) {
|
||||
return ExpectActualCompatibility.Compatible
|
||||
}
|
||||
|
||||
// We must prioritize to return STRONG incompatible over WEAK incompatible (because STRONG incompatibility allows to search for overloads)
|
||||
return getCallablesStrongIncompatibility(expectDeclaration, actualDeclaration, parentSubstitutor)
|
||||
?: getCallablesWeakIncompatibility(expectDeclaration, actualDeclaration, expectContainingClass, actualContainingClass)
|
||||
?: ExpectActualCompatibility.Compatible
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun getCallablesStrongIncompatibility(
|
||||
expectDeclaration: CallableSymbolMarker,
|
||||
actualDeclaration: CallableSymbolMarker,
|
||||
parentSubstitutor: TypeSubstitutorMarker?,
|
||||
): Incompatible.StrongIncompatible<*>? {
|
||||
if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) {
|
||||
return Incompatible.CallableKind
|
||||
}
|
||||
|
||||
val expectedReceiverType = expectDeclaration.extensionReceiverType
|
||||
val actualReceiverType = actualDeclaration.extensionReceiverType
|
||||
if ((expectedReceiverType != null) != (actualReceiverType != null)) {
|
||||
return Incompatible.ParameterShape
|
||||
}
|
||||
|
||||
val expectedValueParameters = expectDeclaration.valueParameters
|
||||
val actualValueParameters = actualDeclaration.valueParameters
|
||||
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
|
||||
return Incompatible.ParameterCount
|
||||
}
|
||||
|
||||
val expectedTypeParameters = expectDeclaration.typeParameters
|
||||
val actualTypeParameters = actualDeclaration.typeParameters
|
||||
if (expectedTypeParameters.size != actualTypeParameters.size) {
|
||||
return Incompatible.FunctionTypeParameterCount
|
||||
}
|
||||
|
||||
val substitutor = createExpectActualTypeParameterSubstitutor(
|
||||
expectedTypeParameters,
|
||||
actualTypeParameters,
|
||||
parentSubstitutor
|
||||
)
|
||||
|
||||
if (
|
||||
!areCompatibleTypeLists(
|
||||
expectedValueParameters.toTypeList(substitutor),
|
||||
actualValueParameters.toTypeList(createEmptySubstitutor())
|
||||
) ||
|
||||
!areCompatibleExpectActualTypes(
|
||||
expectedReceiverType?.let { substitutor.safeSubstitute(it) },
|
||||
actualReceiverType
|
||||
)
|
||||
) {
|
||||
return Incompatible.ParameterTypes
|
||||
}
|
||||
|
||||
if (shouldCheckReturnTypesOfCallables) {
|
||||
if (!areCompatibleExpectActualTypes(substitutor.safeSubstitute(expectDeclaration.returnType), actualDeclaration.returnType)) {
|
||||
return Incompatible.ReturnType
|
||||
}
|
||||
}
|
||||
|
||||
if (!areCompatibleTypeParameterUpperBounds(expectedTypeParameters, actualTypeParameters, substitutor)) {
|
||||
return Incompatible.FunctionTypeParameterUpperBounds
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun getCallablesWeakIncompatibility(
|
||||
expectDeclaration: CallableSymbolMarker,
|
||||
actualDeclaration: CallableSymbolMarker,
|
||||
expectContainingClass: RegularClassSymbolMarker?,
|
||||
actualContainingClass: RegularClassSymbolMarker?,
|
||||
): Incompatible.WeakIncompatible<*>? {
|
||||
val expectedTypeParameters = expectDeclaration.typeParameters
|
||||
val actualTypeParameters = actualDeclaration.typeParameters
|
||||
val expectedValueParameters = expectDeclaration.valueParameters
|
||||
val actualValueParameters = actualDeclaration.valueParameters
|
||||
|
||||
if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) {
|
||||
return Incompatible.ParameterNames
|
||||
}
|
||||
|
||||
if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) {
|
||||
return Incompatible.TypeParameterNames
|
||||
}
|
||||
|
||||
val expectModality = expectDeclaration.modality
|
||||
val actualModality = actualDeclaration.modality
|
||||
if (
|
||||
!areCompatibleModalities(
|
||||
expectModality,
|
||||
actualModality,
|
||||
expectContainingClass?.modality,
|
||||
actualContainingClass?.modality
|
||||
)
|
||||
) {
|
||||
return Incompatible.Modality
|
||||
}
|
||||
|
||||
if (!areCompatibleCallableVisibilities(expectDeclaration.visibility, expectModality, actualDeclaration.visibility)) {
|
||||
return Incompatible.Visibility
|
||||
}
|
||||
|
||||
getTypeParametersVarianceOrReifiedIncompatibility(expectedTypeParameters, actualTypeParameters)?.let { return it }
|
||||
|
||||
if (shouldCheckAbsenceOfDefaultParamsInActual) {
|
||||
// "Default parameters in actual" check is required only for functions, because only functions can have parameters
|
||||
if (actualDeclaration is FunctionSymbolMarker && expectDeclaration is FunctionSymbolMarker) {
|
||||
// Actual annotation constructors can have default argument values; their consistency with arguments in the expected annotation
|
||||
// is checked in ExpectedActualDeclarationChecker.checkAnnotationConstructors
|
||||
if (!actualDeclaration.isAnnotationConstructor() &&
|
||||
// If default params came from common supertypes of actual class and expect class then it's a valid code.
|
||||
// Here we filter out such default params.
|
||||
(actualDeclaration.allOverriddenDeclarationsRecursive() - expectDeclaration.allOverriddenDeclarationsRecursive().toSet())
|
||||
.flatMap { it.valueParameters }.any { it.hasDefaultValue }
|
||||
) {
|
||||
return Incompatible.ActualFunctionWithDefaultParameters
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) {
|
||||
return Incompatible.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
|
||||
}
|
||||
if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isCrossinline && actualValueParameters[i].isCrossinline }) {
|
||||
return Incompatible.ValueParameterCrossinline
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
expectDeclaration is FunctionSymbolMarker && actualDeclaration is FunctionSymbolMarker ->
|
||||
getFunctionsIncompatibility(expectDeclaration, actualDeclaration)?.let { return it }
|
||||
|
||||
expectDeclaration is PropertySymbolMarker && actualDeclaration is PropertySymbolMarker ->
|
||||
getPropertiesIncompatibility(expectDeclaration, actualDeclaration)?.let { return it }
|
||||
|
||||
expectDeclaration is EnumEntrySymbolMarker && actualDeclaration is EnumEntrySymbolMarker -> {
|
||||
// do nothing, entries are matched only by name
|
||||
}
|
||||
|
||||
else -> error("Unsupported declarations: $expectDeclaration, $actualDeclaration")
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun valueParametersCountCompatible(
|
||||
expectDeclaration: CallableSymbolMarker,
|
||||
actualDeclaration: CallableSymbolMarker,
|
||||
expectValueParameters: List<ValueParameterSymbolMarker>,
|
||||
actualValueParameters: List<ValueParameterSymbolMarker>,
|
||||
): Boolean {
|
||||
if (expectValueParameters.size == actualValueParameters.size) return true
|
||||
|
||||
return if (expectDeclaration.isAnnotationConstructor() && actualDeclaration.isAnnotationConstructor()) {
|
||||
expectValueParameters.isEmpty() && actualValueParameters.all { it.hasDefaultValue }
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun areCompatibleTypeLists(
|
||||
expectedTypes: List<KotlinTypeMarker?>,
|
||||
actualTypes: List<KotlinTypeMarker?>,
|
||||
): Boolean {
|
||||
for (i in expectedTypes.indices) {
|
||||
if (!areCompatibleExpectActualTypes(expectedTypes[i], actualTypes[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun areCompatibleClassKinds(
|
||||
expectClass: RegularClassSymbolMarker,
|
||||
actualClass: RegularClassSymbolMarker,
|
||||
): Boolean {
|
||||
if (expectClass.classKind == actualClass.classKind) return true
|
||||
|
||||
if (expectClass.classKind == ClassKind.CLASS && expectClass.isFinal && expectClass.isCtorless) {
|
||||
if (actualClass.classKind == ClassKind.OBJECT) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun areCompatibleModalities(
|
||||
expectModality: Modality?,
|
||||
actualModality: Modality?,
|
||||
expectContainingClassModality: Modality? = null,
|
||||
actualContainingClassModality: Modality? = null,
|
||||
): Boolean {
|
||||
val expectEffectiveModality = effectiveModality(expectModality, expectContainingClassModality)
|
||||
val actualEffectiveModality = effectiveModality(actualModality, actualContainingClassModality)
|
||||
|
||||
return actualEffectiveModality in compatibleModalityMap.getValue(expectEffectiveModality)
|
||||
}
|
||||
|
||||
/*
|
||||
* If containing class is final then all declarations in it effectively final
|
||||
*/
|
||||
private fun effectiveModality(declarationModality: Modality?, containingClassModality: Modality?): Modality? {
|
||||
return when (containingClassModality) {
|
||||
Modality.FINAL -> Modality.FINAL
|
||||
else -> declarationModality
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Key is expect modality, value is a set of compatible actual modalities
|
||||
*/
|
||||
private val compatibleModalityMap: EnumMap<Modality, EnumSet<Modality>> = enumMapOf(
|
||||
Modality.ABSTRACT to enumSetOf(Modality.ABSTRACT),
|
||||
Modality.OPEN to enumSetOf(Modality.OPEN),
|
||||
Modality.FINAL to enumSetOf(Modality.OPEN, Modality.FINAL),
|
||||
Modality.SEALED to enumSetOf(Modality.SEALED),
|
||||
)
|
||||
|
||||
private fun areCompatibleCallableVisibilities(
|
||||
expectVisibility: Visibility,
|
||||
expectModality: Modality?,
|
||||
actualVisibility: Visibility,
|
||||
): Boolean {
|
||||
val compare = Visibilities.compare(expectVisibility, actualVisibility)
|
||||
return if (expectModality != Modality.FINAL) {
|
||||
// For overridable declarations visibility should match precisely, see KT-19664
|
||||
compare == 0
|
||||
} else {
|
||||
// For non-overridable declarations actuals are allowed to have more permissive visibility
|
||||
compare != null && compare <= 0
|
||||
}
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun areCompatibleClassVisibilities(
|
||||
expectClassSymbol: RegularClassSymbolMarker,
|
||||
actualClassSymbol: RegularClassSymbolMarker,
|
||||
): Boolean {
|
||||
val expectVisibility = expectClassSymbol.visibility
|
||||
val actualVisibility = actualClassSymbol.visibility
|
||||
if (expectVisibility == actualVisibility) return true
|
||||
if (!allowClassActualizationWithWiderVisibility) return false
|
||||
val result = Visibilities.compare(actualVisibility, expectVisibility)
|
||||
return result != null && result > 0
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun areCompatibleTypeParameterUpperBounds(
|
||||
expectTypeParameterSymbols: List<TypeParameterSymbolMarker>,
|
||||
actualTypeParameterSymbols: List<TypeParameterSymbolMarker>,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Boolean {
|
||||
for (i in expectTypeParameterSymbols.indices) {
|
||||
val expectBounds = expectTypeParameterSymbols[i].bounds
|
||||
val actualBounds = actualTypeParameterSymbols[i].bounds
|
||||
if (
|
||||
expectBounds.size != actualBounds.size ||
|
||||
!areCompatibleTypeLists(expectBounds.map { substitutor.safeSubstitute(it) }, actualBounds)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun getTypeParametersVarianceOrReifiedIncompatibility(
|
||||
expectTypeParameterSymbols: List<TypeParameterSymbolMarker>,
|
||||
actualTypeParameterSymbols: List<TypeParameterSymbolMarker>,
|
||||
): Incompatible.WeakIncompatible<*>? {
|
||||
if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) {
|
||||
return Incompatible.TypeParameterVariance
|
||||
}
|
||||
|
||||
// Removing "reified" from an expected function's type parameter is fine
|
||||
if (
|
||||
expectTypeParameterSymbols.indices.any { i ->
|
||||
!expectTypeParameterSymbols[i].isReified && actualTypeParameterSymbols[i].isReified
|
||||
}
|
||||
) {
|
||||
return Incompatible.TypeParameterReified
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun getFunctionsIncompatibility(
|
||||
expectFunction: CallableSymbolMarker,
|
||||
actualFunction: CallableSymbolMarker,
|
||||
): Incompatible.WeakIncompatible<*>? {
|
||||
if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) {
|
||||
return Incompatible.FunctionModifiersDifferent
|
||||
}
|
||||
|
||||
if (
|
||||
expectFunction.isInfix && !actualFunction.isInfix ||
|
||||
expectFunction.isInline && !actualFunction.isInline ||
|
||||
expectFunction.isOperator && !actualFunction.isOperator
|
||||
) {
|
||||
return Incompatible.FunctionModifiersNotSubset
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun getPropertiesIncompatibility(
|
||||
expected: PropertySymbolMarker,
|
||||
actual: PropertySymbolMarker,
|
||||
): Incompatible.WeakIncompatible<*>? {
|
||||
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
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun arePropertySettersWithCompatibleVisibilities(
|
||||
expected: PropertySymbolMarker,
|
||||
actual: PropertySymbolMarker,
|
||||
): Boolean {
|
||||
val expectedSetter = expected.setter ?: return true
|
||||
val actualSetter = actual.setter ?: return true
|
||||
return areCompatibleCallableVisibilities(expectedSetter.visibility, expectedSetter.modality, actualSetter.visibility)
|
||||
}
|
||||
|
||||
// ---------------------------------------- Utils ----------------------------------------
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private fun List<ValueParameterSymbolMarker>.toTypeList(substitutor: TypeSubstitutorMarker): List<KotlinTypeMarker> {
|
||||
return this.map { substitutor.safeSubstitute(it.returnType) }
|
||||
}
|
||||
|
||||
private inline fun <T, K> equalsBy(first: List<T>, second: List<T>, selector: (T) -> K): Boolean {
|
||||
for (i in first.indices) {
|
||||
if (selector(first[i]) != selector(second[i])) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private inline fun <T, K> equalBy(first: T, second: T, selector: (T) -> K): Boolean =
|
||||
selector(first) == selector(second)
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private val DeclarationSymbolMarker.name: Name
|
||||
get() = when (this) {
|
||||
is ConstructorSymbolMarker -> SpecialNames.INIT
|
||||
is ValueParameterSymbolMarker -> parameterName
|
||||
is CallableSymbolMarker -> callableId.callableName
|
||||
is RegularClassSymbolMarker -> classId.shortClassName
|
||||
is TypeAliasSymbolMarker -> classId.shortClassName
|
||||
is TypeParameterSymbolMarker -> parameterName
|
||||
else -> error("Unsupported declaration: $this")
|
||||
}
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private val RegularClassSymbolMarker.isCtorless: Boolean
|
||||
get() = getMembersForExpectClass(SpecialNames.INIT).isEmpty()
|
||||
|
||||
context(ExpectActualMatchingContext<*>)
|
||||
private val RegularClassSymbolMarker.isFinal: Boolean
|
||||
get() = modality == Modality.FINAL
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.mpp
|
||||
|
||||
|
||||
sealed class ExpectActualCollectionArgumentsCompatibilityCheckStrategy {
|
||||
abstract fun <T> areCompatible(
|
||||
expectArg: Collection<T>,
|
||||
actualArg: Collection<T>,
|
||||
elementsEqual: (T, T) -> Boolean,
|
||||
): Boolean
|
||||
|
||||
data object Default : ExpectActualCollectionArgumentsCompatibilityCheckStrategy() {
|
||||
override fun <T> areCompatible(
|
||||
expectArg: Collection<T>,
|
||||
actualArg: Collection<T>,
|
||||
elementsEqual: (T, T) -> Boolean,
|
||||
): Boolean {
|
||||
return expectArg.size == actualArg.size && expectArg.zip(actualArg).all { (e1, e2) -> elementsEqual(e1, e2) }
|
||||
}
|
||||
}
|
||||
|
||||
internal data object ExpectIsSubsetOfActual : ExpectActualCollectionArgumentsCompatibilityCheckStrategy() {
|
||||
override fun <T> areCompatible(
|
||||
expectArg: Collection<T>,
|
||||
actualArg: Collection<T>,
|
||||
elementsEqual: (T, T) -> Boolean,
|
||||
): Boolean {
|
||||
return expectArg.all { e1 ->
|
||||
actualArg.any { e2 -> elementsEqual(e1, e2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.multiplatform
|
||||
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
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 : StrongIncompatible<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>()
|
||||
}
|
||||
|
||||
val ExpectActualCompatibility<*>.isCompatibleOrWeaklyIncompatible: Boolean
|
||||
get() = this is ExpectActualCompatibility.Compatible
|
||||
|| this is ExpectActualCompatibility.Incompatible.WeakIncompatible
|
||||
|
||||
val ExpectActualCompatibility<*>.compatible: Boolean
|
||||
get() = this == ExpectActualCompatibility.Compatible
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun ExpectActualCompatibility<*>.isIncompatible(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isIncompatible is ExpectActualCompatibility.Incompatible<*>)
|
||||
}
|
||||
return !compatible
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.mpp
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.mpp.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemContext
|
||||
|
||||
interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemContext {
|
||||
val shouldCheckReturnTypesOfCallables: Boolean
|
||||
|
||||
/*
|
||||
* This flag indicates how are type parameters of inner classes stored in the specific implementation of RegularClassSymbolMarker
|
||||
*
|
||||
* class Outer<T> {
|
||||
* inner class Inner<U>
|
||||
* }
|
||||
*
|
||||
* If flag is set to `true` then `typeParameters` for class `Outer.Inner` contains both parameters: [U, R]
|
||||
* Otherwise it contains only parameters of itself: [U]
|
||||
*
|
||||
* This flag is needed for proper calculation of substitutions for components of inner classes
|
||||
*/
|
||||
val innerClassesCapturesOuterTypeParameters: Boolean
|
||||
get() = true
|
||||
|
||||
val enumConstructorsAreAlwaysCompatible: Boolean
|
||||
get() = false
|
||||
|
||||
// Try to drop it once KT-61105 is fixed
|
||||
val shouldCheckAbsenceOfDefaultParamsInActual: Boolean
|
||||
|
||||
/**
|
||||
* This flag determines, how visibilities for classes/typealiases will be matched
|
||||
* - `false` means that visibilities should be identical
|
||||
* - `true` means that visibility of actual class should be the same or wider comparing to expect visibility
|
||||
* this means that following actualizations will be additionally allowed:
|
||||
* - protected -> public
|
||||
* - internal -> public
|
||||
*/
|
||||
val allowClassActualizationWithWiderVisibility: Boolean
|
||||
get() = false
|
||||
|
||||
/**
|
||||
* This flag determines strategy for matching supertypes between expect and actual class
|
||||
* - `false` means that expect and actual supertypes are matched one by one
|
||||
* - `true` means that type of actual class should be subtype of each expect supertype of the expect class
|
||||
*/
|
||||
val allowTransitiveSupertypesActualization: Boolean
|
||||
get() = false
|
||||
|
||||
val RegularClassSymbolMarker.classId: ClassId
|
||||
val TypeAliasSymbolMarker.classId: ClassId
|
||||
val CallableSymbolMarker.callableId: CallableId
|
||||
val TypeParameterSymbolMarker.parameterName: Name
|
||||
val ValueParameterSymbolMarker.parameterName: Name
|
||||
|
||||
fun TypeAliasSymbolMarker.expandToRegularClass(): RegularClassSymbolMarker?
|
||||
|
||||
val RegularClassSymbolMarker.classKind: ClassKind
|
||||
|
||||
val RegularClassSymbolMarker.isCompanion: Boolean
|
||||
val RegularClassSymbolMarker.isInner: Boolean
|
||||
val RegularClassSymbolMarker.isInline: Boolean
|
||||
val RegularClassSymbolMarker.isValue: Boolean
|
||||
val RegularClassSymbolMarker.isFun: Boolean
|
||||
val ClassLikeSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
|
||||
|
||||
val ClassLikeSymbolMarker.modality: Modality?
|
||||
val ClassLikeSymbolMarker.visibility: Visibility
|
||||
|
||||
val CallableSymbolMarker.modality: Modality?
|
||||
val CallableSymbolMarker.visibility: Visibility
|
||||
|
||||
val RegularClassSymbolMarker.superTypes: List<KotlinTypeMarker>
|
||||
val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
|
||||
|
||||
val CallableSymbolMarker.isExpect: Boolean
|
||||
val CallableSymbolMarker.isInline: Boolean
|
||||
val CallableSymbolMarker.isSuspend: Boolean
|
||||
val CallableSymbolMarker.isExternal: Boolean
|
||||
val CallableSymbolMarker.isInfix: Boolean
|
||||
val CallableSymbolMarker.isOperator: Boolean
|
||||
val CallableSymbolMarker.isTailrec: Boolean
|
||||
|
||||
val PropertySymbolMarker.isVar: Boolean
|
||||
val PropertySymbolMarker.isLateinit: Boolean
|
||||
val PropertySymbolMarker.isConst: Boolean
|
||||
|
||||
val PropertySymbolMarker.getter: FunctionSymbolMarker?
|
||||
val PropertySymbolMarker.setter: FunctionSymbolMarker?
|
||||
|
||||
fun createExpectActualTypeParameterSubstitutor(
|
||||
expectTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
actualTypeParameters: List<TypeParameterSymbolMarker>,
|
||||
parentSubstitutor: TypeSubstitutorMarker?
|
||||
): TypeSubstitutorMarker
|
||||
|
||||
fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List<DeclarationSymbolMarker>
|
||||
fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List<DeclarationSymbolMarker>
|
||||
|
||||
fun RegularClassSymbolMarker.collectEnumEntryNames(): List<Name>
|
||||
fun RegularClassSymbolMarker.collectEnumEntries(): List<DeclarationSymbolMarker>
|
||||
|
||||
val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
|
||||
val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
|
||||
val CallableSymbolMarker.returnType: KotlinTypeMarker
|
||||
val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
|
||||
val FunctionSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
|
||||
|
||||
/**
|
||||
* Returns all symbols that are overridden by [this] symbol
|
||||
*/
|
||||
fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence<CallableSymbolMarker>
|
||||
|
||||
val CallableSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
|
||||
get() = (this as? FunctionSymbolMarker)?.valueParameters ?: emptyList()
|
||||
|
||||
val ValueParameterSymbolMarker.isVararg: Boolean
|
||||
val ValueParameterSymbolMarker.isNoinline: Boolean
|
||||
val ValueParameterSymbolMarker.isCrossinline: Boolean
|
||||
val ValueParameterSymbolMarker.hasDefaultValue: Boolean
|
||||
|
||||
fun CallableSymbolMarker.isAnnotationConstructor(): Boolean
|
||||
|
||||
val TypeParameterSymbolMarker.bounds: List<KotlinTypeMarker>
|
||||
val TypeParameterSymbolMarker.variance: Variance
|
||||
val TypeParameterSymbolMarker.isReified: Boolean
|
||||
|
||||
fun areCompatibleExpectActualTypes(
|
||||
expectType: KotlinTypeMarker?,
|
||||
actualType: KotlinTypeMarker?,
|
||||
): Boolean
|
||||
|
||||
fun actualTypeIsSubtypeOfExpectType(
|
||||
expectType: KotlinTypeMarker,
|
||||
actualType: KotlinTypeMarker
|
||||
): Boolean
|
||||
|
||||
fun RegularClassSymbolMarker.isNotSamInterface(): Boolean
|
||||
|
||||
/*
|
||||
* Determines should some declaration from expect class scope be checked
|
||||
* - FE 1.0: skip fake overrides
|
||||
* - FIR: skip fake overrides
|
||||
* - IR: skip nothing
|
||||
*/
|
||||
fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean
|
||||
|
||||
val CallableSymbolMarker.hasStableParameterNames: Boolean
|
||||
|
||||
fun onMatchedMembers(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbol: DeclarationSymbolMarker,
|
||||
containingExpectClassSymbol: RegularClassSymbolMarker?,
|
||||
containingActualClassSymbol: RegularClassSymbolMarker?,
|
||||
) {}
|
||||
|
||||
fun onMismatchedMembersFromClassScope(
|
||||
expectSymbol: DeclarationSymbolMarker,
|
||||
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
|
||||
containingExpectClassSymbol: RegularClassSymbolMarker?,
|
||||
containingActualClassSymbol: RegularClassSymbolMarker?,
|
||||
) {}
|
||||
|
||||
val DeclarationSymbolMarker.annotations: List<AnnotationCallInfo>
|
||||
|
||||
fun areAnnotationArgumentsEqual(
|
||||
expectAnnotation: AnnotationCallInfo,
|
||||
actualAnnotation: AnnotationCallInfo,
|
||||
collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
|
||||
): Boolean
|
||||
|
||||
val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean
|
||||
|
||||
interface AnnotationCallInfo {
|
||||
val annotationSymbol: Any
|
||||
val classId: ClassId?
|
||||
val isRetentionSource: Boolean
|
||||
val isOptIn: Boolean
|
||||
}
|
||||
|
||||
val checkClassScopesForAnnotationCompatibility: Boolean
|
||||
|
||||
/**
|
||||
* Determines whether it is needed to skip checking annotations on class member in [AbstractExpectActualAnnotationMatchChecker].
|
||||
*
|
||||
* This is needed to prevent checking member twice if it is real `actual` member (not fake override or member of
|
||||
* class being typealiased).
|
||||
* Example:
|
||||
* ```
|
||||
* actual class A {
|
||||
* actual fun foo() {} // 1: checked itself, 2: checked as member of A
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
fun skipCheckingAnnotationsOfActualClassMember(actualMember: DeclarationSymbolMarker): Boolean
|
||||
|
||||
fun findPotentialExpectClassMembersForActual(
|
||||
expectClass: RegularClassSymbolMarker,
|
||||
actualClass: RegularClassSymbolMarker,
|
||||
actualMember: DeclarationSymbolMarker,
|
||||
checkClassScopesCompatibility: Boolean,
|
||||
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>>
|
||||
|
||||
fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.multiplatform
|
||||
|
||||
// This class will be later used by K2. That's why it's placed in compiler.common module
|
||||
data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember: M, val expectClass: C) {
|
||||
/**
|
||||
* Diff kinds that are legal for fake-overrides in final `expect class`, but illegal for non-final `expect class`
|
||||
*
|
||||
* Also see: [toMemberDiffKind]
|
||||
*/
|
||||
enum class Kind(val rawMessage: String) {
|
||||
NonPrivateCallableAdded(
|
||||
"{0}: non-private member must be declared in both the actual class and the expect class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
ReturnTypeChangedInOverride(
|
||||
"{0}: the return type of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
ModalityChangedInOverride(
|
||||
"{0}: the modality of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
VisibilityChangedInOverride(
|
||||
"{0}: the visibility of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
SetterVisibilityChangedInOverride(
|
||||
"{0}: the setter visibility of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
ParameterNameChangedInOverride(
|
||||
"{0}: the parameter names of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
PropertyKindChangedInOverride(
|
||||
"{0}: the property kind (val vs var) of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
LateinitChangedInOverride(
|
||||
"{0}: the property modifiers (lateinit) of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
TypeParameterNamesChangedInOverride(
|
||||
"{0}: the type parameter names of this member must be the same in the expect class and the actual class. " +
|
||||
"This error happens because the expect class ''{1}'' is non-final"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fun ExpectActualCompatibility.Incompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) {
|
||||
ExpectActualCompatibility.Incompatible.CallableKind -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ParameterShape -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ParameterTypes -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.FunctionTypeParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.FunctionTypeParameterUpperBounds -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
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.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.Visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride
|
||||
}
|
||||
Reference in New Issue
Block a user