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