[FE] Check annotations on actual class members including fake overrides

^KT-60668
^KT-60936
This commit is contained in:
Roman Efremov
2023-08-04 13:09:36 +02:00
committed by Space Team
parent d614f06259
commit ab1c0e3c48
30 changed files with 601 additions and 34 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.ClassId
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 {
@@ -33,7 +34,9 @@ object AbstractExpectActualAnnotationMatchChecker {
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
context: ExpectActualMatchingContext<*>,
): Incompatibility? = with(context) { areAnnotationsCompatible(expectSymbol, actualSymbol) }
): Incompatibility? = with(context) {
areAnnotationsCompatible(expectSymbol, actualSymbol)
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsCompatible(
@@ -70,9 +73,13 @@ object AbstractExpectActualAnnotationMatchChecker {
val expanded = actualSymbol.expandToRegularClass() ?: return null
return areClassAnnotationsCompatible(expectSymbol, expanded)
}
check(actualSymbol is RegularClassSymbolMarker)
commonForClassAndCallableChecks(expectSymbol, actualSymbol)?.let { return it }
// TODO(Roman.Efremov, KT-58551): fix actual typealias class members not checked in FE checkers
// TODO(Roman.Efremov, KT-58551): check annotations on fake overrides in case of implicit actualization
if (checkClassScopesForAnnotationCompatibility) {
checkAnnotationsInClassMemberScope(expectSymbol, actualSymbol)?.let { return it }
}
return null
}
@@ -137,4 +144,23 @@ object AbstractExpectActualAnnotationMatchChecker {
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)
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
}
}
@@ -189,4 +189,26 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
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,
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>>
}