[FIR, IR] Commonize isFakeOverride check for AbstractExpectActualAnnotationMatchChecker

This commit fixes fakeOverrides.fir.kt test that got broken in the
previous commit

Previously isFakeOverride was only checked on IR backend. Now this check
is moved to AbstractExpectActualAnnotationMatchChecker which is used by
both: frontend and backend. That's why frontend no longer reports false
positive ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT

Review: https://jetbrains.team/p/kt/reviews/13094/timeline
This commit is contained in:
Nikita Bobko
2023-11-17 12:35:34 +01:00
committed by teamcity
parent e6bfcc7c65
commit 5b72c127bd
9 changed files with 34 additions and 34 deletions
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isActual
import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory
import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
/** /**
@@ -33,19 +34,23 @@ internal object FirActualAnnotationsMatchExpectChecker : FirBasicDeclarationChec
val actualSymbol = declaration.symbol val actualSymbol = declaration.symbol
val expectSymbol = actualSymbol.getSingleMatchedExpectForActualOrNull() ?: return val expectSymbol = actualSymbol.getSingleMatchedExpectForActualOrNull() ?: return
checkAnnotationsMatch(expectSymbol, actualSymbol, context, reporter)
val actualContainingClass = context.containingDeclarations.lastOrNull()?.symbol as? FirRegularClassSymbol
val expectContainingClass = actualContainingClass?.getSingleMatchedExpectForActualOrNull() as? FirRegularClassSymbol
checkAnnotationsMatch(expectSymbol, actualSymbol, expectContainingClass, context, reporter)
} }
@OptIn(InternalDiagnosticFactoryMethod::class) @OptIn(InternalDiagnosticFactoryMethod::class)
private fun checkAnnotationsMatch( private fun checkAnnotationsMatch(
expectSymbol: FirBasedSymbol<*>, expectSymbol: FirBasedSymbol<*>,
actualSymbol: FirBasedSymbol<*>, actualSymbol: FirBasedSymbol<*>,
expectContainingClass: FirRegularClassSymbol?,
context: CheckerContext, context: CheckerContext,
reporter: DiagnosticReporter, reporter: DiagnosticReporter,
) { ) {
val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession) val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession)
val incompatibility = val incompatibility =
AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, matchingContext) ?: return AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, expectContainingClass, matchingContext) ?: return
val actualAnnotationTargetSourceElement = (incompatibility.actualAnnotationTargetElement as FirSourceElement).element val actualAnnotationTargetSourceElement = (incompatibility.actualAnnotationTargetElement as FirSourceElement).element
reporter.report( reporter.report(
@@ -128,13 +128,13 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
val matchingCompatibilityToMembersMap = symbol.expectForActual ?: return val matchingCompatibilityToMembersMap = symbol.expectForActual ?: return
val expectedSingleCandidate = val expectedSingleCandidate =
matchingCompatibilityToMembersMap[ExpectActualMatchingCompatibility.MatchedSuccessfully]?.singleOrNull() matchingCompatibilityToMembersMap[ExpectActualMatchingCompatibility.MatchedSuccessfully]?.singleOrNull()
val expectActualMatchingContext = context.session.expectActualMatchingContextFactory.create(
context.session, context.scopeSession,
allowedWritingMemberExpectForActualMapping = true,
)
val actualContainingClass = context.containingDeclarations.lastOrNull()?.symbol as? FirRegularClassSymbol
val expectContainingClass = actualContainingClass?.getSingleMatchedExpectForActualOrNull() as? FirRegularClassSymbol
val checkingCompatibility = if (expectedSingleCandidate != null) { val checkingCompatibility = if (expectedSingleCandidate != null) {
val expectActualMatchingContext = context.session.expectActualMatchingContextFactory.create(
context.session, context.scopeSession,
allowedWritingMemberExpectForActualMapping = true,
)
val actualContainingClass = context.containingDeclarations.lastOrNull()?.symbol as? FirRegularClassSymbol
val expectContainingClass = actualContainingClass?.getSingleMatchedExpectForActualOrNull() as? FirRegularClassSymbol
getCheckingCompatibility( getCheckingCompatibility(
symbol, symbol,
expectedSingleCandidate, expectedSingleCandidate,
@@ -363,15 +363,16 @@ class FirExpectActualMatchingContextImpl private constructor(
return !isSam return !isSam
} }
override fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean { override fun CallableSymbolMarker.isFakeOverride(containingExpectClass: RegularClassSymbolMarker?): Boolean {
if (containingExpectClass == null) {
return false
}
val symbol = asSymbol() val symbol = asSymbol()
val classSymbol = containingExpectClass.asSymbol() val classSymbol = containingExpectClass.asSymbol()
if (symbol !is FirConstructorSymbol && symbol.dispatchReceiverType?.classId != classSymbol.classId) { if (symbol !is FirConstructorSymbol && symbol.dispatchReceiverType?.classId != classSymbol.classId) {
// Skip fake overrides
return true return true
} }
return symbol.isSubstitutionOrIntersectionOverride // Skip fake overrides return symbol.isSubstitutionOrIntersectionOverride
|| !symbol.isExpect // Skip non-expect declarations like equals, hashCode, toString and any inherited declarations from non-expect super types
} }
override val CallableSymbolMarker.hasStableParameterNames: Boolean override val CallableSymbolMarker.hasStableParameterNames: Boolean
@@ -454,8 +454,8 @@ internal abstract class IrExpectActualMatchingContext(
return !asIr().isFun return !asIr().isFun
} }
override fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean { override fun CallableSymbolMarker.isFakeOverride(containingExpectClass: RegularClassSymbolMarker?): Boolean {
return false return asIr().isFakeOverride
} }
override val CallableSymbolMarker.hasStableParameterNames: Boolean override val CallableSymbolMarker.hasStableParameterNames: Boolean
@@ -25,13 +25,8 @@ internal object IrExpectActualAnnotationMatchingChecker : IrExpectActualChecker
if (expectSymbol is IrTypeParameterSymbol) { if (expectSymbol is IrTypeParameterSymbol) {
continue continue
} }
// If the expect declaration is fake-override and is incompatible, then it means that it was overridden on actual. val incompatibility = AbstractExpectActualAnnotationMatchChecker
// In such case, regular rules for annotations on overridden declarations apply. .areAnnotationsCompatible(expectSymbol, actualSymbol, containingExpectClass = null, matchingContext) ?: continue
if (expectSymbol.isFakeOverride) {
continue
}
val incompatibility =
AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, matchingContext) ?: continue
val reportOn = getTypealiasSymbolIfActualizedViaTypealias(expectSymbol.owner as IrDeclaration, classActualizationInfo) val reportOn = getTypealiasSymbolIfActualizedViaTypealias(expectSymbol.owner as IrDeclaration, classActualizationInfo)
?: getContainingActualClassIfFakeOverride(actualSymbol) ?: getContainingActualClassIfFakeOverride(actualSymbol)
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType as IncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType as IncompatibilityType
@@ -53,19 +52,21 @@ object AbstractExpectActualAnnotationMatchChecker {
fun areAnnotationsCompatible( fun areAnnotationsCompatible(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClass: RegularClassSymbolMarker?, // Only necessary for the frontend. IR doesn't use it
context: ExpectActualMatchingContext<*>, context: ExpectActualMatchingContext<*>,
): Incompatibility? = with(context) { ): Incompatibility? = with(context) {
areAnnotationsCompatible(expectSymbol, actualSymbol) areAnnotationsCompatible(expectSymbol, actualSymbol, containingExpectClass)
} }
context (ExpectActualMatchingContext<*>) context (ExpectActualMatchingContext<*>)
private fun areAnnotationsCompatible( private fun areAnnotationsCompatible(
expectSymbol: DeclarationSymbolMarker, expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker,
containingExpectClass: RegularClassSymbolMarker?,
): Incompatibility? { ): Incompatibility? {
return when (expectSymbol) { return when (expectSymbol) {
is CallableSymbolMarker -> { is CallableSymbolMarker -> {
areCallableAnnotationsCompatible(expectSymbol, actualSymbol as CallableSymbolMarker) areCallableAnnotationsCompatible(expectSymbol, actualSymbol as CallableSymbolMarker, containingExpectClass)
} }
is RegularClassSymbolMarker -> { is RegularClassSymbolMarker -> {
areClassAnnotationsCompatible(expectSymbol, actualSymbol as ClassLikeSymbolMarker) areClassAnnotationsCompatible(expectSymbol, actualSymbol as ClassLikeSymbolMarker)
@@ -78,7 +79,11 @@ object AbstractExpectActualAnnotationMatchChecker {
private fun areCallableAnnotationsCompatible( private fun areCallableAnnotationsCompatible(
expectSymbol: CallableSymbolMarker, expectSymbol: CallableSymbolMarker,
actualSymbol: CallableSymbolMarker, actualSymbol: CallableSymbolMarker,
containingExpectClass: RegularClassSymbolMarker?,
): Incompatibility? { ): Incompatibility? {
// If the expect declaration is fake-override and is incompatible, then it means that it was overridden on actual.
// In such case, regular rules for annotations on overridden declarations apply.
if (expectSymbol.isFakeOverride(containingExpectClass)) return null
commonForClassAndCallableChecks(expectSymbol, actualSymbol)?.let { return it } commonForClassAndCallableChecks(expectSymbol, actualSymbol)?.let { return it }
areAnnotationsOnValueParametersCompatible(expectSymbol, actualSymbol)?.let { return it } areAnnotationsOnValueParametersCompatible(expectSymbol, actualSymbol)?.let { return it }
@@ -316,7 +321,7 @@ object AbstractExpectActualAnnotationMatchChecker {
// Check also incompatible members if only one is found // Check also incompatible members if only one is found
?: expectToCompatibilityMap.keys.singleOrNull() ?: expectToCompatibilityMap.keys.singleOrNull()
?: continue ?: continue
areAnnotationsCompatible(expectMember, actualMember)?.let { return it } areAnnotationsCompatible(expectMember, actualMember, expectClass)?.let { return it }
} }
return null return null
} }
@@ -156,13 +156,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
fun RegularClassSymbolMarker.isNotSamInterface(): Boolean fun RegularClassSymbolMarker.isNotSamInterface(): Boolean
/* fun CallableSymbolMarker.isFakeOverride(containingExpectClass: RegularClassSymbolMarker?): 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 val CallableSymbolMarker.hasStableParameterNames: Boolean
@@ -20,7 +20,7 @@ expect class FakeOverrideActual : I {
// MODULE: m1-jvm()()(m1-common) // MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt // FILE: jvm.kt
actual class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>FakeOverrideExpect<!> : A() { actual class FakeOverrideExpect : A() {
override fun noAnnotationOnActual() {} override fun noAnnotationOnActual() {}
} }
@@ -20,7 +20,7 @@ expect class FakeOverrideActual : I {
// MODULE: m1-jvm()()(m1-common) // MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt // FILE: jvm.kt
actual class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>FakeOverrideExpect<!> : A() { actual class FakeOverrideExpect : A() {
override fun noAnnotationOnActual() {} override fun noAnnotationOnActual() {}
} }