From 6f17022449384dc767421e50dfe26f5688992b89 Mon Sep 17 00:00:00 2001 From: Nikita Bobko Date: Fri, 17 Nov 2023 10:49:09 +0100 Subject: [PATCH] [FIR] Cleanup: extract reportClassScopesIncompatibility into its own function Review: https://jetbrains.team/p/kt/reviews/13094/timeline --- .../FirExpectActualDeclarationChecker.kt | 114 ++++++++++-------- 1 file changed, 63 insertions(+), 51 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt index 2948ac3013c..7da024f2775 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt @@ -155,57 +155,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { when { checkingCompatibility is ExpectActualCheckingCompatibility.ClassScopes -> { - require((symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) && expectedSingleCandidate is FirRegularClassSymbol) { - "Incompatible.ClassScopes is only possible for a class or a typealias: $declaration" - } - - // Do not report "expected members have no actual ones" for those expected members, for which there's a clear - // (albeit maybe incompatible) single actual suspect, declared in the actual class. - // This is needed only to reduce the number of errors. Incompatibility errors for those members will be reported - // later when this checker is called for them - fun hasSingleActualSuspect( - expectedWithIncompatibility: Pair, Map>, Collection>>>, - ): Boolean { - val (expectedMember, incompatibility) = expectedWithIncompatibility - val actualMember = incompatibility.values.singleOrNull()?.singleOrNull() - @OptIn(SymbolInternals::class) - return actualMember != null && - actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember - } - - val nonTrivialIncompatibleMembers = checkingCompatibility.incompatibleMembers.filterNot(::hasSingleActualSuspect) - - if (nonTrivialIncompatibleMembers.isNotEmpty()) { - val (defaultArgsIncompatibleMembers, otherIncompatibleMembers) = - nonTrivialIncompatibleMembers.partition { it.second.contains(ExpectActualCheckingCompatibility.DefaultArgumentsInExpectActualizedByFakeOverride) } - - if (defaultArgsIncompatibleMembers.isNotEmpty()) { // report a nicer diagnostic for DefaultArgumentsInExpectActualizedByFakeOverride - val problematicExpectMembers = defaultArgsIncompatibleMembers - .map { - it.first as? FirNamedFunctionSymbol - ?: error("${ExpectActualCheckingCompatibility.DefaultArgumentsInExpectActualizedByFakeOverride} can be reported only for ${FirNamedFunctionSymbol::class}") - } - reporter.reportOn( - source, - FirErrors.DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE, - expectedSingleCandidate, - problematicExpectMembers, - context - ) - } - if (otherIncompatibleMembers.isNotEmpty()) { - reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, otherIncompatibleMembers, context) - } - } - if (checkingCompatibility.mismatchedMembers.isNotEmpty()) { - reporter.reportOn( - source, - FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, - symbol, - checkingCompatibility.mismatchedMembers, - context - ) - } + reportClassScopesIncompatibility(symbol, expectedSingleCandidate, declaration, checkingCompatibility, reporter, source, context) } ExpectActualMatchingCompatibility.MatchedSuccessfully !in matchingCompatibilityToMembersMap && @@ -242,6 +192,68 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() { } } + private fun reportClassScopesIncompatibility( + symbol: FirBasedSymbol, + expectedSingleCandidate: FirBasedSymbol<*>?, + declaration: FirMemberDeclaration, + checkingCompatibility: ExpectActualCheckingCompatibility.ClassScopes>, + reporter: DiagnosticReporter, + source: KtSourceElement?, + context: CheckerContext, + ) { + require((symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) && expectedSingleCandidate is FirRegularClassSymbol) { + "Incompatible.ClassScopes is only possible for a class or a typealias: $declaration" + } + + // Do not report "expected members have no actual ones" for those expected members, for which there's a clear + // (albeit maybe incompatible) single actual suspect, declared in the actual class. + // This is needed only to reduce the number of errors. Incompatibility errors for those members will be reported + // later when this checker is called for them + fun hasSingleActualSuspect( + expectedWithIncompatibility: Pair, Map>, Collection>>>, + ): Boolean { + val (expectedMember, incompatibility) = expectedWithIncompatibility + val actualMember = incompatibility.values.singleOrNull()?.singleOrNull() + @OptIn(SymbolInternals::class) + return actualMember != null && + actualMember.fir.expectForActual?.values?.singleOrNull()?.singleOrNull() == expectedMember + } + + val nonTrivialIncompatibleMembers = checkingCompatibility.incompatibleMembers.filterNot(::hasSingleActualSuspect) + + if (nonTrivialIncompatibleMembers.isNotEmpty()) { + val (defaultArgsIncompatibleMembers, otherIncompatibleMembers) = + nonTrivialIncompatibleMembers.partition { it.second.contains(ExpectActualCheckingCompatibility.DefaultArgumentsInExpectActualizedByFakeOverride) } + + if (defaultArgsIncompatibleMembers.isNotEmpty()) { // report a nicer diagnostic for DefaultArgumentsInExpectActualizedByFakeOverride + val problematicExpectMembers = defaultArgsIncompatibleMembers + .map { + it.first as? FirNamedFunctionSymbol + ?: error("${ExpectActualCheckingCompatibility.DefaultArgumentsInExpectActualizedByFakeOverride} can be reported only for ${FirNamedFunctionSymbol::class}") + } + reporter.reportOn( + source, + FirErrors.DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE, + expectedSingleCandidate, + problematicExpectMembers, + context + ) + } + if (otherIncompatibleMembers.isNotEmpty()) { + reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, otherIncompatibleMembers, context) + } + } + if (checkingCompatibility.mismatchedMembers.isNotEmpty()) { + reporter.reportOn( + source, + FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, + symbol, + checkingCompatibility.mismatchedMembers, + context + ) + } + } + private fun getCheckingCompatibility( actualSymbol: FirBasedSymbol<*>, expectSymbol: FirBasedSymbol<*>,