[FIR, IR] Convert FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker into ExpectActualCheckingCompatibility
FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker is an adhoc checker which can be converted to ExpectActualCheckingCompatibility to reuse common expect-actual checking infrastructure. ^KT-62913 Fixed Review: https://jetbrains.team/p/kt/reviews/13094/timeline Tests that were broken by one of my previous commits are now fixed: - actualFakeOverride_paramsAreCompatibleViaSharedMethodWithDefaultParams.kt - inheritedJavaMembers.kt DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE diagnostic disappeared in delegation.fir.kt because only one AbstractExpectActualChecker incompatibility can be reported at a time (DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE is now reported not by adhoc checker but by common AbstractExpectActualChecker). It would be nice to report both of them, but it's a separate issue KT-62631 delegation2 test makes sure that DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE is reported when NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS is fixed
This commit is contained in:
+6
@@ -1243,6 +1243,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("delegation2.kt")
|
||||
public void testDelegation2() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("disabledFeature.kt")
|
||||
public void testDisabledFeature() throws Exception {
|
||||
|
||||
+6
@@ -1243,6 +1243,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("delegation2.kt")
|
||||
public void testDelegation2() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/defaultArguments/methodDefaultArgsViaActualFakeOverride/delegation2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("disabledFeature.kt")
|
||||
public void testDisabledFeature() throws Exception {
|
||||
|
||||
-1
@@ -142,7 +142,6 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirMultipleDefaultsInheritedFromSupertypesChecker,
|
||||
FirFiniteBoundRestrictionChecker,
|
||||
FirNonExpansiveInheritanceRestrictionChecker,
|
||||
FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker,
|
||||
)
|
||||
|
||||
override val constructorCheckers: Set<FirConstructorChecker>
|
||||
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* 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.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirExpectActualMatchingContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.getSingleMatchedExpectForActualOrNull
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isActual
|
||||
import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory
|
||||
import org.jetbrains.kotlin.fir.isDelegated
|
||||
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.classId
|
||||
import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualMatcher
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
|
||||
|
||||
// TODO KT-62913 create one more ExpectActualCheckingCompatibility incompatibility, and replace this checker with this incompatibility
|
||||
internal object FirDefaultArgumentsInExpectActualizedByFakeOverrideChecker : FirRegularClassChecker() {
|
||||
override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) ||
|
||||
!context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitDefaultArgumentsInExpectActualizedByFakeOverride)) {
|
||||
return
|
||||
}
|
||||
if (!declaration.isActual) {
|
||||
return
|
||||
}
|
||||
val actualClassSymbol = declaration.symbol
|
||||
// We want to report errors even if a candidate is incompatible, but it's single
|
||||
val expectedSingleCandidate = actualClassSymbol.getSingleMatchedExpectForActualOrNull() ?: return
|
||||
val expectClassSymbol = expectedSingleCandidate as FirRegularClassSymbol
|
||||
|
||||
val expectActualMatchingContext = context.session.expectActualMatchingContextFactory.create(
|
||||
context.session, context.scopeSession,
|
||||
allowedWritingMemberExpectForActualMapping = true,
|
||||
)
|
||||
AbstractExpectActualMatcher.recursivelyMatchClassScopes(expectClassSymbol, actualClassSymbol, expectActualMatchingContext)
|
||||
|
||||
val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession)
|
||||
val problematicExpectMembers = with(matchingContext) { findProblematicExpectMembers(expectClassSymbol, actualClassSymbol) }
|
||||
if (problematicExpectMembers.isNotEmpty()) {
|
||||
reporter.reportOn(
|
||||
declaration.source, FirErrors.DEFAULT_ARGUMENTS_IN_EXPECT_ACTUALIZED_BY_FAKE_OVERRIDE,
|
||||
expectClassSymbol, problematicExpectMembers, context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirExpectActualMatchingContext.findProblematicExpectMembers(
|
||||
expectClassSymbol: FirRegularClassSymbol, actualClassSymbol: FirRegularClassSymbol,
|
||||
): List<FirNamedFunctionSymbol> {
|
||||
val actualFakeOverrideMembers = actualClassSymbol.collectAllMembers(isActualDeclaration = true)
|
||||
.filterIsInstance<FirNamedFunctionSymbol>()
|
||||
.filter { it.isFakeOverride(actualClassSymbol) || it.isDelegated }
|
||||
|
||||
return actualFakeOverrideMembers
|
||||
.mapNotNull { getSingleMatchingExpect(it, expectClassSymbol, actualClassSymbol) }
|
||||
.filter(::hasDefaultArgumentValues)
|
||||
}
|
||||
|
||||
private fun FirExpectActualMatchingContext.getSingleMatchingExpect(
|
||||
actualMember: FirNamedFunctionSymbol,
|
||||
expectSymbol: FirRegularClassSymbol,
|
||||
actualSymbol: FirRegularClassSymbol
|
||||
): FirNamedFunctionSymbol? {
|
||||
val potentialExpects = findPotentialExpectClassMembersForActual(
|
||||
expectSymbol, actualSymbol, actualMember,
|
||||
)
|
||||
val expectMember: DeclarationSymbolMarker = potentialExpects.entries
|
||||
.singleOrNull { it.value == ExpectActualMatchingCompatibility.MatchedSuccessfully }?.key
|
||||
?: potentialExpects.keys.singleOrNull()
|
||||
?: return null
|
||||
return expectMember as FirNamedFunctionSymbol
|
||||
}
|
||||
|
||||
private fun hasDefaultArgumentValues(function: FirFunctionSymbol<*>): Boolean {
|
||||
return function.valueParameterSymbols.any { it.hasDefaultValue }
|
||||
}
|
||||
}
|
||||
+30
-7
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
@@ -25,10 +26,7 @@ import org.jetbrains.kotlin.fir.expectActualMatchingContextFactory
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.mpp.RegularClassSymbolMarker
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
@@ -157,7 +155,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
|
||||
when {
|
||||
checkingCompatibility is ExpectActualCheckingCompatibility.ClassScopes -> {
|
||||
require(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) {
|
||||
require((symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) && expectedSingleCandidate is FirRegularClassSymbol) {
|
||||
"Incompatible.ClassScopes is only possible for a class or a typealias: $declaration"
|
||||
}
|
||||
|
||||
@@ -178,10 +176,35 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
val nonTrivialIncompatibleMembers = checkingCompatibility.incompatibleMembers.filterNot(::hasSingleActualSuspect)
|
||||
|
||||
if (nonTrivialIncompatibleMembers.isNotEmpty()) {
|
||||
reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialIncompatibleMembers, context)
|
||||
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)
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS,
|
||||
symbol,
|
||||
checkingCompatibility.mismatchedMembers,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -43,7 +43,7 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
private val actualScopeSession: ScopeSession,
|
||||
private val allowedWritingMemberExpectForActualMapping: Boolean,
|
||||
) : FirExpectActualMatchingContext, TypeSystemContext by actualSession.typeContext {
|
||||
override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean
|
||||
override val shouldCheckDefaultParams: Boolean
|
||||
get() = true
|
||||
|
||||
override val allowClassActualizationWithWiderVisibility: Boolean
|
||||
@@ -257,7 +257,7 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
override val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
|
||||
get() = asSymbol().typeParameterSymbols
|
||||
|
||||
override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence<CallableSymbolMarker> {
|
||||
override fun FunctionSymbolMarker.allRecursivelyOverriddenDeclarationsIncludingSelf(): Sequence<CallableSymbolMarker> {
|
||||
return when (val symbol = asSymbol()) {
|
||||
is FirConstructorSymbol, is FirFunctionWithoutNameSymbol -> sequenceOf(this)
|
||||
is FirNamedFunctionSymbol -> {
|
||||
@@ -285,6 +285,9 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
override val ValueParameterSymbolMarker.hasDefaultValue: Boolean
|
||||
get() = asSymbol().hasDefaultValue
|
||||
|
||||
override val ValueParameterSymbolMarker.hasDefaultValueNonRecursive: Boolean
|
||||
get() = asSymbol().hasDefaultValue
|
||||
|
||||
override fun CallableSymbolMarker.isAnnotationConstructor(): Boolean {
|
||||
val symbol = asSymbol()
|
||||
return symbol.isAnnotationConstructor(symbol.moduleData.session)
|
||||
@@ -375,6 +378,9 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
return symbol.isSubstitutionOrIntersectionOverride
|
||||
}
|
||||
|
||||
override val CallableSymbolMarker.isDelegatedMember: Boolean
|
||||
get() = asSymbol().isDelegated
|
||||
|
||||
override val CallableSymbolMarker.hasStableParameterNames: Boolean
|
||||
get() = asSymbol().rawStatus.hasStableParameterNames
|
||||
|
||||
|
||||
Reference in New Issue
Block a user