[FIR] Add expect actual checker

This commit is contained in:
Dmitriy Novozhilov
2021-05-24 16:44:23 +03:00
committed by teamcityserver
parent faadb08174
commit c3060e861f
45 changed files with 1687 additions and 80 deletions
@@ -38,10 +38,6 @@ FILE: RedundantModalityModifierChecker.kt
public abstract interface Foo : R|kotlin/Any| {
}
public abstract expect class AbstractClass : R|Foo| {
public expect constructor(): R|AbstractClass| {
super<R|kotlin/Any|>()
}
public abstract expect override fun foo(): R|kotlin/Unit|
public abstract expect fun bar(): R|kotlin/Unit|
@@ -2,10 +2,6 @@ FILE: B.kt
public abstract expect interface Other : R|kotlin/Any| {
}
public final expect class Another : R|kotlin/Any| {
public expect constructor(): R|b/d/Another| {
super<R|kotlin/Any|>()
}
}
public final fun baz(): R|kotlin/Unit| {
}
@@ -1,10 +1,6 @@
Module: m1-common
FILE: common.kt
public final expect class MyList : R|kotlin/Any| {
public expect constructor(): R|MyList| {
super<R|kotlin/Any|>()
}
public final expect fun get(i: R|kotlin/Int|): R|kotlin/Int|
}
@@ -7,6 +7,7 @@ plugins {
dependencies {
implementation(project(":compiler:fir:tree"))
implementation(project(":compiler:fir:tree:tree-generator"))
implementation(project(":compiler:resolution.common"))
implementation(project(":kotlin-reflect"))
implementation(project(":kotlin-reflect-api"))
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
import org.jetbrains.kotlin.fir.declarations.*
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
@@ -785,7 +787,6 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ABSTRACT_PROPERTY_WITH_SETTER by error<KtPropertyAccessor>()
val PRIVATE_SETTER_FOR_ABSTRACT_PROPERTY by error<KtModifierListOwner>(PositioningStrategy.PRIVATE_MODIFIER)
val PRIVATE_SETTER_FOR_OPEN_PROPERTY by error<KtModifierListOwner>(PositioningStrategy.PRIVATE_MODIFIER)
val EXPECTED_PRIVATE_DECLARATION by error<KtModifierListOwner>(PositioningStrategy.VISIBILITY_MODIFIER)
val VAL_WITH_SETTER by error<KtPropertyAccessor>()
val CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT by error<KtElement>(PositioningStrategy.CONST_MODIFIER)
val CONST_VAL_WITH_GETTER by error<KtElement>()
@@ -815,11 +816,56 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val MPP_PROJECTS by object : DiagnosticGroup("Multi-platform projects") {
val EXPECTED_DECLARATION_WITH_BODY by error<KtDeclaration>(PositioningStrategy.DECLARATION_SIGNATURE)
val EXPECTED_CLASS_CONSTRUCTOR_DELEGATION_CALL by error<KtConstructorDelegationCall>()
val EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER by error<KtParameter>()
val EXPECTED_ENUM_CONSTRUCTOR by error<KtConstructor<*>>()
val EXPECTED_ENUM_ENTRY_WITH_BODY by error<KtEnumEntry>()
val EXPECTED_PROPERTY_INITIALIZER by error<KtExpression>()
// TODO: need to cover `by` as well as delegate expression
val EXPECTED_DELEGATED_PROPERTY by error<KtExpression>()
val EXPECTED_LATEINIT_PROPERTY by error<KtModifierListOwner>(PositioningStrategy.LATEINIT_MODIFIER)
val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error<PsiElement>()
val EXPECTED_PRIVATE_DECLARATION by error<KtModifierListOwner>(PositioningStrategy.VISIBILITY_MODIFIER)
val IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS by error<KtDelegatedSuperTypeEntry>()
val ACTUAL_TYPE_ALIAS_NOT_TO_CLASS by error<KtTypeAlias>(PositioningStrategy.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE by error<KtTypeAlias>(PositioningStrategy.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE by error<KtTypeAlias>(PositioningStrategy.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION by error<KtTypeAlias>(PositioningStrategy.DECLARATION_SIGNATURE)
val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error<PsiElement>()
val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error<PsiElement> {
parameter<FirVariableSymbol<*>>("parameter")
}
val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error<PsiElement>()
val NO_ACTUAL_FOR_EXPECT by error<KtNamedDeclaration>(PositioningStrategy.INCOMPATIBLE_DECLARATION) {
parameter<Symbol>("declaration")
parameter<FirModuleData>("module")
parameter<Map<Incompatible<Symbol>, Collection<Symbol>>>("compatibility")
}
val ACTUAL_WITHOUT_EXPECT by error<KtNamedDeclaration> {
parameter<Symbol>("declaration")
parameter<Map<Incompatible<Symbol>, Collection<Symbol>>>("compatibility")
}
val AMBIGUOUS_ACTUALS by error<KtNamedDeclaration>(PositioningStrategy.INCOMPATIBLE_DECLARATION) {
parameter<Symbol>("declaration")
parameter<Collection<Symbol>>("candidates")
}
val AMBIGUOUS_EXPECTS by error<KtNamedDeclaration>(PositioningStrategy.INCOMPATIBLE_DECLARATION) {
parameter<Symbol>("declaration")
parameter<Collection<FirModuleData>>("modules")
}
val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error<KtNamedDeclaration>(PositioningStrategy.ACTUAL_DECLARATION_NAME) {
parameter<Symbol>("declaration")
parameter<List<Pair<Symbol, Map<Incompatible<Symbol>, Collection<Symbol>>>>>("members")
}
val ACTUAL_MISSING by error<KtNamedDeclaration>(PositioningStrategy.ACTUAL_DECLARATION_NAME)
}
val DESTRUCTING_DECLARATION by object : DiagnosticGroup("Destructuring declaration") {
@@ -77,6 +77,8 @@ enum class PositioningStrategy(private val strategy: String? = null) {
DATA_MODIFIER,
SPREAD_OPERATOR,
DECLARATION_WITH_BODY,
INCOMPATIBLE_DECLARATION,
ACTUAL_DECLARATION_NAME,
;
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.FirExpression
@@ -40,10 +41,14 @@ import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtIfExpression
@@ -66,6 +71,7 @@ import org.jetbrains.kotlin.psi.KtWhenCondition
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
/*
@@ -425,7 +431,6 @@ object FirErrors {
val ABSTRACT_PROPERTY_WITH_SETTER by error0<KtPropertyAccessor>()
val PRIVATE_SETTER_FOR_ABSTRACT_PROPERTY by error0<KtModifierListOwner>(SourceElementPositioningStrategies.PRIVATE_MODIFIER)
val PRIVATE_SETTER_FOR_OPEN_PROPERTY by error0<KtModifierListOwner>(SourceElementPositioningStrategies.PRIVATE_MODIFIER)
val EXPECTED_PRIVATE_DECLARATION by error0<KtModifierListOwner>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
val VAL_WITH_SETTER by error0<KtPropertyAccessor>()
val CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT by error0<KtElement>(SourceElementPositioningStrategies.CONST_MODIFIER)
val CONST_VAL_WITH_GETTER by error0<KtElement>()
@@ -443,9 +448,29 @@ object FirErrors {
// Multi-platform projects
val EXPECTED_DECLARATION_WITH_BODY by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val EXPECTED_CLASS_CONSTRUCTOR_DELEGATION_CALL by error0<KtConstructorDelegationCall>()
val EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER by error0<KtParameter>()
val EXPECTED_ENUM_CONSTRUCTOR by error0<KtConstructor<*>>()
val EXPECTED_ENUM_ENTRY_WITH_BODY by error0<KtEnumEntry>()
val EXPECTED_PROPERTY_INITIALIZER by error0<KtExpression>()
val EXPECTED_DELEGATED_PROPERTY by error0<KtExpression>()
val EXPECTED_LATEINIT_PROPERTY by error0<KtModifierListOwner>(SourceElementPositioningStrategies.LATEINIT_MODIFIER)
val SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS by error0<PsiElement>()
val EXPECTED_PRIVATE_DECLARATION by error0<KtModifierListOwner>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
val IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS by error0<KtDelegatedSuperTypeEntry>()
val ACTUAL_TYPE_ALIAS_NOT_TO_CLASS by error0<KtTypeAlias>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE by error0<KtTypeAlias>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE by error0<KtTypeAlias>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION by error0<KtTypeAlias>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error0<PsiElement>()
val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1<PsiElement, FirVariableSymbol<*>>()
val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error0<PsiElement>()
val NO_ACTUAL_FOR_EXPECT by error3<KtNamedDeclaration, FirBasedSymbol<*>, FirModuleData, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val ACTUAL_WITHOUT_EXPECT by error2<KtNamedDeclaration, FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>()
val AMBIGUOUS_ACTUALS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val AMBIGUOUS_EXPECTS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirModuleData>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error2<KtNamedDeclaration, FirBasedSymbol<*>, List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME)
val ACTUAL_MISSING by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME)
// Destructuring declaration
val INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION by error0<KtDestructuringDeclaration>()
@@ -30,6 +30,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirInfixFunctionDeclarationChecker,
FirExposedVisibilityDeclarationChecker,
FirCyclicTypeBoundsChecker,
FirExpectActualDeclarationChecker
)
override val functionCheckers: Set<FirFunctionChecker>
@@ -0,0 +1,242 @@
/*
* Copyright 2010-2021 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.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.utils.isActual
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.*
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@Suppress("DuplicatedCode")
object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (!context.session.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) return
if (declaration !is FirMemberDeclaration) return
if (declaration.isActual) {
checkActualDeclarationHasExpected(declaration, context, reporter)
}
}
private fun <T> checkActualDeclarationHasExpected(
declaration: T,
context: CheckerContext,
reporter: DiagnosticReporter,
checkActual: Boolean = true
) where T : FirDeclaration, T : FirMemberDeclaration {
val scopeSession = ScopeSession()
val symbol = declaration.symbol
val compatibilityToMembersMap = FirExpectActualResolver.findExpectForActual(symbol, context.session, scopeSession) ?: return
val session = context.session
checkAmbiguousExpects(symbol, compatibilityToMembersMap, symbol, context, reporter)
val source = declaration.source
if (!declaration.isActual) {
if (compatibilityToMembersMap.allStrongIncompatibilities()) return
if (Compatible in compatibilityToMembersMap) {
if (checkActual && requireActualModifier(symbol, session)) {
reporter.reportOn(source, FirErrors.ACTUAL_MISSING, context)
}
return
}
}
val singleIncompatibility = compatibilityToMembersMap.keys.singleOrNull()
when {
singleIncompatibility is Incompatible.ClassScopes -> {
assert(symbol is FirRegularClassSymbol || symbol is FirTypeAliasSymbol) {
"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<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>
): Boolean {
val (expectedMember, incompatibility) = expectedWithIncompatibility
val actualMember = incompatibility.values.singleOrNull()?.singleOrNull()
return actualMember != null &&
actualMember.isExplicitActualDeclaration() &&
!incompatibility.allStrongIncompatibilities() &&
FirExpectActualResolver.findExpectForActual(
actualMember,
session,
scopeSession
)?.values?.singleOrNull()?.singleOrNull() == expectedMember
}
val nonTrivialUnfulfilled = singleIncompatibility.unfulfilled.filterNot(::hasSingleActualSuspect)
if (nonTrivialUnfulfilled.isNotEmpty()) {
reporter.reportOn(source, FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, symbol, nonTrivialUnfulfilled, context)
}
}
Compatible !in compatibilityToMembersMap -> {
reporter.reportOn(source, FirErrors.ACTUAL_WITHOUT_EXPECT, symbol, compatibilityToMembersMap.cast(), context)
}
else -> {
val expected = compatibilityToMembersMap[Compatible]!!.first()
if (expected is FirRegularClassSymbol && expected.classKind == ClassKind.ANNOTATION_CLASS) {
val klass = symbol.expandedClass(session)
val actualConstructor = klass?.declarationSymbols?.firstIsInstanceOrNull<FirConstructorSymbol>()
val expectedConstructor = expected.declarationSymbols.firstIsInstanceOrNull<FirConstructorSymbol>()
if (expectedConstructor != null && actualConstructor != null) {
checkAnnotationConstructors(source, expectedConstructor, actualConstructor, context, reporter)
}
}
}
}
}
@Suppress("UNUSED_PARAMETER")
private fun checkAnnotationConstructors(
source: FirSourceElement?,
expected: FirConstructorSymbol,
actual: FirConstructorSymbol,
context: CheckerContext,
reporter: DiagnosticReporter
) {
for (expectedValueParameter in expected.valueParameterSymbols) {
// Actual parameter with the same name is guaranteed to exist because this method is only called for compatible annotations
val actualValueDescriptor = actual.valueParameterSymbols.first { it.name == expectedValueParameter.name }
if (expectedValueParameter.hasDefaultValue && actualValueDescriptor.hasDefaultValue) {
// TODO
// val expectedParameter =
// DescriptorToSourceUtils.descriptorToDeclaration(expectedValueParameter) as? KtParameter ?: continue
//
// val expectedValue = trace.bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expectedParameter.defaultValue)
// ?.toConstantValue(expectedValueParameter.type)
//
// val actualValue =
// getActualAnnotationParameterValue(actualValueDescriptor, trace.bindingContext, expectedValueParameter.type)
// if (expectedValue != actualValue) {
// val ktParameter = DescriptorToSourceUtils.descriptorToDeclaration(actualValueDescriptor)
// val target = (ktParameter as? KtParameter)?.defaultValue ?: (reportOn as? KtTypeAlias)?.nameIdentifier ?: reportOn
// trace.report(Errors.ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE.on(target, actualValueDescriptor))
// }
}
}
}
private fun checkAmbiguousExpects(
actualDeclaration: FirBasedSymbol<*>,
compatibility: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<FirBasedSymbol<*>>>,
symbol: FirBasedSymbol<*>,
context: CheckerContext,
reporter: DiagnosticReporter
) {
val filesWithAtLeastWeaklyCompatibleExpects = compatibility.asSequence()
.filter { (compatibility, _) ->
compatibility.isCompatibleOrWeakCompatible()
}
.map { (_, members) -> members }
.flatten()
.map { it.moduleData }
.sortedBy { it.name.asString() }
.toList()
if (filesWithAtLeastWeaklyCompatibleExpects.size > 1) {
reporter.reportOn(
actualDeclaration.source,
FirErrors.AMBIGUOUS_EXPECTS,
symbol,
filesWithAtLeastWeaklyCompatibleExpects,
context
)
}
}
fun Map<out ExpectActualCompatibility<*>, *>.allStrongIncompatibilities(): Boolean {
return keys.all { it is Incompatible && it.kind == IncompatibilityKind.STRONG }
}
private fun ExpectActualCompatibility<FirBasedSymbol<*>>.isCompatibleOrWeakCompatible(): Boolean {
return this is Compatible ||
this is Incompatible && kind == IncompatibilityKind.WEAK
}
// we don't require `actual` modifier on
// - annotation constructors, because annotation classes can only have one constructor
// - inline class primary constructors, because inline class must have primary constructor
// - value parameter inside primary constructor of inline class, because inline class must have one value parameter
private fun requireActualModifier(declaration: FirBasedSymbol<*>, session: FirSession): Boolean {
return !declaration.isAnnotationConstructor(session) &&
!declaration.isPrimaryConstructorOfInlineClass(session) &&
!isUnderlyingPropertyOfInlineClass(declaration)
}
@Suppress("UNUSED_PARAMETER")
private fun isUnderlyingPropertyOfInlineClass(declaration: FirBasedSymbol<*>): Boolean {
// TODO
// return declaration is PropertyDescriptor && declaration.isUnderlyingPropertyOfInlineClass()
return false
}
private fun FirBasedSymbol<*>.isExplicitActualDeclaration(): Boolean {
// return when (this) {
// is FirConstructor -> DescriptorToSourceUtils.getSourceFromDescriptor(this) is KtConstructor<*>
// is FirCallableMemberDeclaration<*> -> kind == CallableMemberDescriptor.Kind.DECLARATION
// else -> true
// }
return true
}
}
fun FirBasedSymbol<*>.isAnnotationConstructor(session: FirSession): Boolean {
if (this !is FirConstructorSymbol) return false
return getConstructedClass(session)?.classKind == ClassKind.ANNOTATION_CLASS
}
fun FirBasedSymbol<*>.isEnumConstructor(session: FirSession): Boolean {
if (this !is FirConstructorSymbol) return false
return getConstructedClass(session)?.classKind == ClassKind.ENUM_CLASS
}
fun FirBasedSymbol<*>.isPrimaryConstructorOfInlineClass(session: FirSession): Boolean {
if (this !is FirConstructorSymbol) return false
return getConstructedClass(session)?.isInlineOrValueClass() == true && this.isPrimary
}
fun FirConstructorSymbol.getConstructedClass(session: FirSession): FirRegularClassSymbol? {
return resolvedReturnTypeRef.coneType
.fullyExpandedType(session)
.toSymbol(session) as? FirRegularClassSymbol?
}
fun FirBasedSymbol<*>.expandedClass(session: FirSession): FirRegularClassSymbol? {
return when (this) {
is FirTypeAliasSymbol -> resolvedExpandedTypeRef.coneType.fullyExpandedType(session).toSymbol(session) as? FirRegularClassSymbol
is FirRegularClassSymbol -> this
else -> null
}
}
@@ -0,0 +1,658 @@
/*
* Copyright 2010-2021 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.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.collectEnumEntries
import org.jetbrains.kotlin.fir.analysis.checkers.fullyExpandedClass
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.substitution.chain
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.keysToMap
object FirExpectActualResolver {
@OptIn(ExperimentalStdlibApi::class)
fun findExpectForActual(
actualSymbol: FirBasedSymbol<*>,
useSiteSession: FirSession,
scopeSession: ScopeSession
): Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<FirBasedSymbol<*>>>? {
return when (actualSymbol) {
is FirCallableSymbol<*> -> {
val callableId = actualSymbol.callableId
val classId = callableId.classId
var parentSubstitutor: ConeSubstitutor? = null
var expectContainingClass: FirRegularClassSymbol? = null
var actualContainingClass: FirRegularClassSymbol? = null
val candidates = when {
classId != null -> {
expectContainingClass = useSiteSession.dependenciesSymbolProvider.getClassLikeSymbolByFqName(classId)?.let {
it.fullyExpandedClass(it.moduleData.session)
}
actualContainingClass = useSiteSession.symbolProvider.getClassLikeSymbolByFqName(classId)
?.fullyExpandedClass(useSiteSession)
val expectTypeParameters = expectContainingClass?.typeParameterSymbols.orEmpty()
val actualTypeParameters = actualContainingClass
?.typeParameterSymbols
.orEmpty()
parentSubstitutor = createTypeParameterSubstitutor(expectTypeParameters, actualTypeParameters, useSiteSession)
when (actualSymbol) {
is FirConstructorSymbol -> expectContainingClass?.getConstructors(scopeSession)
else -> expectContainingClass?.getMembers(callableId.callableName, scopeSession)
}.orEmpty()
}
callableId.isLocal -> return null
else -> {
val scope = FirPackageMemberScope(callableId.packageName, useSiteSession, useSiteSession.dependenciesSymbolProvider)
mutableListOf<FirCallableSymbol<*>>().apply {
scope.processFunctionsByName(callableId.callableName) { add(it) }
scope.processPropertiesByName(callableId.callableName) { add(it) }
}
}
}
candidates.filter { expectSymbol ->
actualSymbol != expectSymbol && expectSymbol.isExpect
}.groupBy { expectDeclaration ->
areCompatibleCallables(
expectDeclaration,
actualSymbol,
useSiteSession,
parentSubstitutor,
expectContainingClass,
actualContainingClass
)
}
}
is FirClassLikeSymbol<*> -> {
val expectClassSymbol = useSiteSession.dependenciesSymbolProvider
.getClassLikeSymbolByFqName(actualSymbol.classId) as? FirRegularClassSymbol ?: return null
val compatibility = areCompatibleClassifiers(expectClassSymbol, actualSymbol, useSiteSession, scopeSession)
mapOf(compatibility to listOf(expectClassSymbol))
}
else -> null
}
}
private fun areCompatibleClassifiers(
expectClassSymbol: FirRegularClassSymbol,
actualClassLikeSymbol: FirClassLikeSymbol<*>,
actualSession: FirSession,
scopeSession: ScopeSession
): ExpectActualCompatibility<FirBasedSymbol<*>> {
// Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name
assert(expectClassSymbol.classId.shortClassName == actualClassLikeSymbol.classId.shortClassName) {
"This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol"
}
val actualClass = when (actualClassLikeSymbol) {
is FirRegularClassSymbol -> actualClassLikeSymbol
is FirTypeAliasSymbol -> actualClassLikeSymbol.resolvedExpandedTypeRef.coneType.fullyExpandedType(actualSession)
.toSymbol(actualSession) as? FirRegularClassSymbol
?: return ExpectActualCompatibility.Compatible // do not report extra error on erroneous typealias
else -> throw IllegalArgumentException("Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol")
}
if (expectClassSymbol.classKind != actualClass.classKind) return ExpectActualCompatibility.Incompatible.ClassKind
if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline /*|| it.isValue*/) }) {
return ExpectActualCompatibility.Incompatible.ClassModifiers
}
val expectTypeParameterSymbols = expectClassSymbol.typeParameterSymbols
val actualTypeParameterSymbols = actualClass.typeParameterSymbols
if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) {
return ExpectActualCompatibility.Incompatible.TypeParameterCount
}
if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) {
return ExpectActualCompatibility.Incompatible.Modality
}
if (expectClassSymbol.visibility != actualClass.visibility) {
return ExpectActualCompatibility.Incompatible.Visibility
}
val substitutor = createTypeParameterSubstitutor(expectTypeParameterSymbols, actualTypeParameterSymbols, actualSession)
val expectSession = expectClassSymbol.moduleData.session
areCompatibleTypeParameters(expectTypeParameterSymbols, actualTypeParameterSymbols, actualSession, expectSession, substitutor).let {
if (it != ExpectActualCompatibility.Compatible) {
return it
}
}
// 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.superConeTypes.filterNot { it.classId == actualSession.builtinTypes.anyType.id }
val actualSupertypes = actualClass.superConeTypes.filterNot { it.classId == actualSession.builtinTypes.anyType.id }
if (
expectSupertypes.map(substitutor::substituteOrSelf).any { expectSupertype ->
actualSupertypes.none { actualSupertype ->
areCompatibleTypes(expectSupertype, actualSupertype, expectSession, actualSession)
}
}
) {
return ExpectActualCompatibility.Incompatible.Supertypes
}
areCompatibleClassScopes(expectClassSymbol, actualClass, actualSession, scopeSession, substitutor).let {
if (it != ExpectActualCompatibility.Compatible) {
return it
}
}
return ExpectActualCompatibility.Compatible
}
private fun areCompatibleClassScopes(
expectClassSymbol: FirRegularClassSymbol,
actualClassSymbol: FirRegularClassSymbol,
actualSession: FirSession,
scopeSession: ScopeSession,
substitutor: ConeSubstitutor
): ExpectActualCompatibility<FirBasedSymbol<*>> {
val unfulfilled =
mutableListOf<Pair<FirBasedSymbol<*>, Map<ExpectActualCompatibility.Incompatible<FirBasedSymbol<*>>, MutableCollection<FirBasedSymbol<*>>>>>()
val allActualMembers = actualClassSymbol.getMembers(scopeSession, actualSession)
val actualMembersByName = allActualMembers.groupBy { it.name }
val actualConstructors = allActualMembers.filterIsInstance<FirConstructorSymbol>()
outer@ for (expectMember in expectClassSymbol.getMembers(scopeSession)) {
// if (expectMember is CallableMemberDescriptor && !expectMember.kind.isReal) continue
val actualMembers = when (expectMember) {
is FirConstructorSymbol -> actualConstructors
else -> actualMembersByName[expectMember.name]?.filter { actualMember ->
expectMember is FirRegularClassSymbol && actualMember is FirRegularClassSymbol ||
expectMember is FirCallableSymbol<*> && actualMember is FirCallableSymbol<*>
}.orEmpty()
}
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is FirCallableSymbol<*> ->
areCompatibleCallables(
expectMember,
actualMember as FirCallableSymbol<*>,
actualSession,
substitutor,
expectClassSymbol,
actualClassSymbol
)
is FirRegularClassSymbol ->
areCompatibleClassifiers(expectMember, actualMember as FirRegularClassSymbol, actualSession, scopeSession)
else -> throw UnsupportedOperationException("Unsupported declaration: $expectMember ($actualMembers)")
}
}
if (mapping.values.any { it == ExpectActualCompatibility.Compatible }) continue
val incompatibilityMap =
mutableMapOf<ExpectActualCompatibility.Incompatible<FirBasedSymbol<*>>, MutableCollection<FirBasedSymbol<*>>>()
for ((declaration, compatibility) in mapping) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> continue@outer
is ExpectActualCompatibility.Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(declaration)
}
}
unfulfilled.add(expectMember to incompatibilityMap)
}
if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
val expectEntries = expectClassSymbol.collectEnumEntries().map { it.name }
val actualEntries = actualClassSymbol.collectEnumEntries().map { it.name }
if (!actualEntries.containsAll(expectEntries)) {
return ExpectActualCompatibility.Incompatible.EnumEntries
}
}
// TODO: check static scope?
if (unfulfilled.isEmpty()) return ExpectActualCompatibility.Compatible
return ExpectActualCompatibility.Incompatible.ClassScopes(unfulfilled)
}
private fun areCompatibleCallables(
expectDeclaration: FirCallableSymbol<*>,
actualDeclaration: FirCallableSymbol<*>,
actualSession: FirSession,
parentSubstitutor: ConeSubstitutor?,
expectContainingClass: FirRegularClassSymbol?,
actualContainingClass: FirRegularClassSymbol?,
): ExpectActualCompatibility<FirBasedSymbol<*>> {
assert(
(expectDeclaration is FirConstructorSymbol && actualDeclaration is FirConstructorSymbol) ||
expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName
) {
"This function should be invoked only for declarations with the same name: $expectDeclaration, $actualDeclaration"
}
assert((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"
}
val expectSession = expectDeclaration.moduleData.session
if (
expectDeclaration is FirConstructorSymbol &&
actualDeclaration is FirConstructorSymbol &&
expectContainingClass?.classKind == ClassKind.ENUM_CLASS &&
actualContainingClass?.classKind == ClassKind.ENUM_CLASS
) {
return ExpectActualCompatibility.Compatible
}
if (expectDeclaration is FirNamedFunctionSymbol != actualDeclaration is FirNamedFunctionSymbol) {
return ExpectActualCompatibility.Incompatible.CallableKind
}
val expectedReceiverType = expectDeclaration.resolvedReceiverTypeRef
val actualReceiverType = actualDeclaration.resolvedReceiverTypeRef
if ((expectedReceiverType != null) != (actualReceiverType != null)) {
return ExpectActualCompatibility.Incompatible.ParameterShape
}
val expectedValueParameters = expectDeclaration.valueParameterSymbols
val actualValueParameters = actualDeclaration.valueParameterSymbols
if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) {
return ExpectActualCompatibility.Incompatible.ParameterCount
}
val expectedTypeParameters = expectDeclaration.typeParameterSymbols
val actualTypeParameters = actualDeclaration.typeParameterSymbols
if (expectedTypeParameters.size != actualTypeParameters.size) {
return ExpectActualCompatibility.Incompatible.TypeParameterCount
}
val substitutor = createTypeParameterSubstitutor(expectedTypeParameters, actualTypeParameters, actualSession, parentSubstitutor)
if (
!areCompatibleTypeLists(
expectedValueParameters.toTypeList(substitutor),
actualValueParameters.toTypeList(ConeSubstitutor.Empty),
expectSession,
actualSession
) ||
!areCompatibleTypes(
expectedReceiverType?.coneType?.let { substitutor.substituteOrSelf(it) },
actualReceiverType?.coneType,
expectSession,
actualSession
)
) {
return ExpectActualCompatibility.Incompatible.ParameterTypes
}
if (
!areCompatibleTypes(
substitutor.substituteOrSelf(expectDeclaration.resolvedReturnTypeRef.coneType),
actualDeclaration.resolvedReturnTypeRef.coneType,
expectSession,
actualSession
)
) {
return ExpectActualCompatibility.Incompatible.ReturnType
}
// TODO: implement hasStableParameterNames calculation
// if (actualDeclaration.hasStableParameterNames() && !equalsBy(expectedValueParameters, actualValueParameters, ValueParameterDescriptor::getName)) return Incompatible.ParameterNames
if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) {
return ExpectActualCompatibility.Incompatible.TypeParameterNames
}
if (
!areCompatibleModalities(
expectDeclaration.modality,
actualDeclaration.modality,
(expectDeclaration.dispatchReceiverType?.toSymbol(expectSession) as? FirRegularClassSymbol)?.modality,
actualContainingClass?.modality
)
) {
return ExpectActualCompatibility.Incompatible.Modality
}
if (!areDeclarationsWithCompatibleVisibilities(expectDeclaration.resolvedStatus, actualDeclaration.resolvedStatus)) {
return ExpectActualCompatibility.Incompatible.Visibility
}
areCompatibleTypeParameters(expectedTypeParameters, actualTypeParameters, actualSession, expectSession, substitutor).let {
if (it != ExpectActualCompatibility.Compatible) {
return it
}
}
if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) {
return ExpectActualCompatibility.Incompatible.ValueParameterVararg
}
// Adding noinline/crossinline to parameters is disallowed, except if the expected declaration was not inline at all
if (expectDeclaration is FirNamedFunctionSymbol && expectDeclaration.isInline) {
if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isNoinline && actualValueParameters[i].isNoinline }) {
return ExpectActualCompatibility.Incompatible.ValueParameterNoinline
}
if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isCrossinline && actualValueParameters[i].isCrossinline }) {
return ExpectActualCompatibility.Incompatible.ValueParameterCrossinline
}
}
when {
expectDeclaration is FirNamedFunctionSymbol && actualDeclaration is FirNamedFunctionSymbol -> areCompatibleFunctions(
expectDeclaration,
actualDeclaration
).let { if (it != ExpectActualCompatibility.Compatible) return it }
expectDeclaration is FirConstructorSymbol && actualDeclaration is FirConstructorSymbol -> areCompatibleFunctions(
expectDeclaration,
actualDeclaration
).let { if (it != ExpectActualCompatibility.Compatible) return it }
expectDeclaration is FirPropertySymbol && actualDeclaration is FirPropertySymbol -> areCompatibleProperties(
expectDeclaration,
actualDeclaration
).let { if (it != ExpectActualCompatibility.Compatible) return it }
else -> throw AssertionError("Unsupported declarations: $expectDeclaration, $actualDeclaration")
}
return ExpectActualCompatibility.Compatible
}
private fun createTypeParameterSubstitutor(
expectedTypeParameters: List<FirTypeParameterSymbol>,
actualTypeParameters: List<FirTypeParameterSymbol>,
useSiteSession: FirSession,
parentSubstitutor: ConeSubstitutor? = null
): ConeSubstitutor {
val substitution = expectedTypeParameters.zip(actualTypeParameters).associate { (expectedParameterSymbol, actualParameterSymbol) ->
expectedParameterSymbol to actualParameterSymbol.toLookupTag().constructType(emptyArray(), isNullable = false)
}
val substitutor = ConeSubstitutorByMap(
substitution,
useSiteSession
)
if (parentSubstitutor == null) {
return substitutor
}
return substitutor.chain(parentSubstitutor)
}
private fun valueParametersCountCompatible(
expectDeclaration: FirCallableSymbol<*>,
actualDeclaration: FirCallableSymbol<*>,
expectValueParameters: List<FirValueParameterSymbol>,
actualValueParameters: List<FirValueParameterSymbol>
): Boolean {
if (expectValueParameters.size == actualValueParameters.size) return true
return if (
expectDeclaration.isAnnotationConstructor(expectDeclaration.moduleData.session) &&
actualDeclaration.isAnnotationConstructor(actualDeclaration.moduleData.session)
) {
expectValueParameters.isEmpty() && actualValueParameters.all { it.hasDefaultValue }
} else {
false
}
}
private fun areCompatibleTypeLists(
expectedTypes: List<ConeKotlinType?>,
actualTypes: List<ConeKotlinType?>,
expectSession: FirSession,
actualSession: FirSession
): Boolean {
for (i in expectedTypes.indices) {
if (!areCompatibleTypes(expectedTypes[i], actualTypes[i], expectSession, actualSession)) {
return false
}
}
return true
}
private fun areCompatibleTypes(
expectedType: ConeKotlinType?,
actualType: ConeKotlinType?,
expectSession: FirSession,
actualSession: FirSession
): Boolean {
if (expectedType == null) return actualType == null
if (actualType == null) return false
val typeCheckerContext = ConeInferenceContextForExpectActual(expectSession, actualSession).newBaseTypeCheckerContext(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = true
)
return AbstractTypeChecker.equalTypes(
typeCheckerContext,
expectedType,
actualType
)
}
private fun areCompatibleModalities(
expectModality: Modality?,
actualModality: Modality?,
expectContainingClassModality: Modality? = null,
actualContainingClassModality: Modality? = null
): Boolean {
val result = (expectModality == actualModality) ||
(expectModality == Modality.FINAL && (actualModality == Modality.OPEN || actualModality == Modality.ABSTRACT))
if (result) return true
if (expectContainingClassModality == null || actualContainingClassModality == null) return result
return expectModality == expectContainingClassModality && actualModality == actualContainingClassModality
}
private fun areDeclarationsWithCompatibleVisibilities(
expectStatus: FirDeclarationStatus,
actualStatus: FirDeclarationStatus
): Boolean {
val compare = Visibilities.compare(expectStatus.visibility, actualStatus.visibility)
return if (expectStatus.modality != 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
}
}
private fun areCompatibleTypeParameters(
expectTypeParameterSymbols: List<FirTypeParameterSymbol>,
actualTypeParameterSymbols: List<FirTypeParameterSymbol>,
actualSession: FirSession,
expectSession: FirSession,
substitutor: ConeSubstitutor
): ExpectActualCompatibility<FirBasedSymbol<*>> {
for (i in expectTypeParameterSymbols.indices) {
val expectBounds = expectTypeParameterSymbols[i].resolvedBounds.map { it.coneType }
val actualBounds = actualTypeParameterSymbols[i].resolvedBounds.map { it.coneType }
if (
expectBounds.size != actualBounds.size ||
!areCompatibleTypeLists(expectBounds.map(substitutor::substituteOrSelf), actualBounds, expectSession, actualSession)
) {
return ExpectActualCompatibility.Incompatible.TypeParameterUpperBounds
}
}
if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) {
return ExpectActualCompatibility.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 ExpectActualCompatibility.Incompatible.TypeParameterReified
}
return ExpectActualCompatibility.Compatible
}
private fun areCompatibleFunctions(
expectFunction: FirCallableSymbol<*>,
actualFunction: FirCallableSymbol<*>
): ExpectActualCompatibility<FirBasedSymbol<*>> {
if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) {
return ExpectActualCompatibility.Incompatible.FunctionModifiersDifferent
}
if (
expectFunction.isExternal && !actualFunction.isExternal ||
expectFunction.isInfix && !actualFunction.isInfix ||
expectFunction.isInline && !actualFunction.isInline ||
expectFunction.isOperator && !actualFunction.isOperator ||
expectFunction.isTailRec && !actualFunction.isTailRec
) {
return ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset
}
return ExpectActualCompatibility.Compatible
}
private fun areCompatibleProperties(a: FirPropertySymbol, b: FirPropertySymbol): ExpectActualCompatibility<FirBasedSymbol<*>> {
if (!equalBy(a, b) { p -> p.isVar }) {
return ExpectActualCompatibility.Incompatible.PropertyKind
}
if (!equalBy(a, b) { p -> listOf(p.isConst, p.isLateInit) }) {
return ExpectActualCompatibility.Incompatible.PropertyModifiers
}
return ExpectActualCompatibility.Compatible
}
// ---------------------------------------- Utils ----------------------------------------
private class ConeInferenceContextForExpectActual(val expectSession: FirSession, val actualSession: FirSession) : ConeInferenceContext {
override val session: FirSession
get() = actualSession
override fun areEqualTypeConstructors(c1: TypeConstructorMarker, c2: TypeConstructorMarker): Boolean {
if (c1 !is ConeClassifierLookupTag || c2 !is ConeClassifierLookupTag) {
return c1 == c2
}
return isExpectedClassAndActualTypeAlias(c1, c2) ||
isExpectedClassAndActualTypeAlias(c2, c1) ||
c1 == c2
}
// For example, expectedTypeConstructor may be the expected class kotlin.text.StringBuilder, while actualTypeConstructor
// is java.lang.StringBuilder. For the purposes of type compatibility checking, we must consider these types equal here.
// Note that the case of an "actual class" works as expected though, because the actual class by definition has the same FQ name
// as the corresponding expected class, so their type constructors are equal as per AbstractClassTypeConstructor#equals
private fun isExpectedClassAndActualTypeAlias(
expectLookupTag: ConeClassifierLookupTag,
actualLookupTag: ConeClassifierLookupTag
): Boolean {
val expectDeclaration = expectLookupTag.toClassLikeDeclaration(expectSession) ?: return false
val actualDeclaration = actualLookupTag.toClassLikeDeclaration(actualSession) ?: return false
if (!expectDeclaration.isExpect) return false
val expectClassId = when (expectDeclaration) {
is FirRegularClassSymbol -> expectDeclaration.classId
is FirTypeAliasSymbol -> expectDeclaration.resolvedExpandedTypeRef.coneType.classId
else -> null
} ?: return false
return expectClassId == actualDeclaration.classId
}
private fun ConeClassifierLookupTag.toClassLikeDeclaration(session: FirSession): FirClassLikeSymbol<*>? {
return this.toSymbol(session) as? FirClassLikeSymbol<*>
}
}
private fun List<FirValueParameterSymbol>.toTypeList(substitutor: ConeSubstitutor): List<ConeKotlinType> {
return this.map { substitutor.substituteOrSelf(it.resolvedReturnTypeRef.coneType) }
}
private val FirCallableSymbol<*>.valueParameterSymbols: List<FirValueParameterSymbol>
get() = (this as? FirFunctionSymbol<*>)?.valueParameterSymbols ?: emptyList()
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)
private fun FirClassSymbol<*>.getMembers(
scopeSession: ScopeSession,
session: FirSession = moduleData.session
): Collection<FirBasedSymbol<*>> {
val scope = defaultType().scope(useSiteSession = session, scopeSession, FakeOverrideTypeCalculator.DoNothing)
?: return emptyList()
return mutableListOf<FirBasedSymbol<*>>().apply {
for (name in scope.getCallableNames()) {
scope.getMembersTo(this, name)
}
for (name in declarationSymbols.mapNotNull { (it as? FirRegularClassSymbol)?.classId?.shortClassName }) {
addIfNotNull(scope.getSingleClassifier(name) as? FirRegularClassSymbol)
}
getConstructorsTo(this, scope)
}
}
private fun FirClassSymbol<*>.getConstructors(
scopeSession: ScopeSession,
session: FirSession = moduleData.session
): Collection<FirConstructorSymbol> {
return mutableListOf<FirConstructorSymbol>().apply {
getConstructorsTo(this, unsubstitutedScope(session, scopeSession, withForcedTypeCalculator = false))
}
}
private fun getConstructorsTo(destination: MutableList<in FirConstructorSymbol>, scope: FirTypeScope) {
scope.getDeclaredConstructors().mapTo(destination) { it }
}
private fun FirClassSymbol<*>.getMembers(name: Name, scopeSession: ScopeSession): Collection<FirCallableSymbol<*>> {
val scope = defaultType().scope(useSiteSession = moduleData.session, scopeSession, FakeOverrideTypeCalculator.DoNothing)
?: return emptyList()
return mutableListOf<FirCallableSymbol<*>>().apply {
scope.getMembersTo(this, name)
}
}
@OptIn(ExperimentalStdlibApi::class)
private fun FirTypeScope.getMembersTo(
destination: MutableList<in FirCallableSymbol<*>>,
name: Name,
) {
processFunctionsByName(name) { destination.add(it) }
processPropertiesByName(name) { destination.add(it) }
}
private val FirBasedSymbol<*>.name: Name
get() = when (this) {
is FirCallableSymbol<*> -> name
is FirRegularClassSymbol -> classId.shortClassName
else -> error("Should not be here")
}
}
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.AMBI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FQ_NAMES_IN_TYPES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.COLLECTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.MODULE_DATA
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FUNCTION_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NOT_RENDERED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NULLABLE_STRING
@@ -38,6 +40,16 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_PROPERTY_WITH_SETTER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_SUPER_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACCESSOR_FOR_DELEGATED_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_MISSING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS_NOT_TO_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_WITHOUT_EXPECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ACTUALS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_EXPECTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_ARGUMENT_MUST_BE_CONST
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_CLASS_CONSTRUCTOR_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ANNOTATION_CLASS_MEMBER
@@ -124,9 +136,14 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EQUALITY_NOT_APPL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_FROM_JAVA_RESOLUTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ERROR_IN_CONTRACT_DESCRIPTION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPANDED_TYPE_CANNOT_BE_INHERITED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_CLASS_CONSTRUCTOR_DELEGATION_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_CONDITION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_DECLARATION_WITH_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_DELEGATED_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_ENUM_CONSTRUCTOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_ENUM_ENTRY_WITH_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_LATEINIT_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_PRIVATE_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPECTED_PROPERTY_INITIALIZER
@@ -171,6 +188,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_CONST_EXP
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SELECTOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_FILE_TARGET
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_INFIX_MODIFIER
@@ -244,6 +262,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_LOOP_LABEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ACTUAL_FOR_EXPECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_COMPANION_OBJECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_GET_METHOD
@@ -321,6 +341,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SPREAD_OF_NULLABL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPES_FOR_ANNOTATION_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_IS_EXTENSION_FUNCTION_TYPE
@@ -1134,10 +1155,30 @@ class FirDefaultErrorMessages {
// Multi-platform projects
map.put(EXPECTED_DECLARATION_WITH_BODY, "Expected declaration must not have a body")
map.put(EXPECTED_CLASS_CONSTRUCTOR_DELEGATION_CALL, "Explicit delegation call for constructor of an expected class is not allowed")
map.put(EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER, "Expected class constructor cannot have a property parameter")
map.put(EXPECTED_ENUM_CONSTRUCTOR, "Expected enum class cannot have a constructor")
map.put(EXPECTED_ENUM_ENTRY_WITH_BODY, "Expected enum entry cannot have a body")
map.put(EXPECTED_PROPERTY_INITIALIZER, "Expected property cannot have an initializer")
map.put(EXPECTED_DELEGATED_PROPERTY, "Expected property cannot be delegated")
map.put(EXPECTED_PRIVATE_DECLARATION, "Expected declaration cannot be private")
map.put(EXPECTED_LATEINIT_PROPERTY, "Expected property cannot be lateinit")
map.put(EXPECTED_PRIVATE_DECLARATION, "Expected declaration cannot be private")
map.put(SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS, "Expected classes cannot initialize supertypes")
map.put(EXPECTED_PRIVATE_DECLARATION, "Expected declaration cannot be private")
map.put(IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS, "Implementation by delegation in expected classes is prohibited")
map.put(ACTUAL_TYPE_ALIAS_NOT_TO_CLASS, "Right-hand side of actual type alias should be a class, not another type alias")
map.put(ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE, "Aliased class should not have type parameters with declaration-site variance")
map.put(ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE, "Right-hand side of actual type alias cannot contain use-site variance or star projections")
map.put(ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION, "Type arguments in the right-hand side of actual type alias should be its type parameters in the same order, e.g. 'actual typealias Foo<A, B> = Bar<A, B>'")
map.put(ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS, "Actual function cannot have default argument values, they should be declared in the expected function")
map.put(ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE, "Parameter ''{0}'' has conflicting values in the expected and actual annotation", SYMBOL)
map.put(EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND, "Expected function source is not found, therefore it's impossible to generate default argument values declared there. Please add the corresponding file to compilation sources")
map.put(NO_ACTUAL_FOR_EXPECT, "Expected {0} has no actual declaration in module {1}{2}", SYMBOL, MODULE_DATA, FirPlatformIncompatibilityDiagnosticRenderer.TEXT)
map.put(ACTUAL_WITHOUT_EXPECT, "{0} has no corresponding expected declaration{1}", SYMBOL, FirPlatformIncompatibilityDiagnosticRenderer.TEXT)
map.put(AMBIGUOUS_ACTUALS, "{0} has several compatible actual declarations in modules {1}", SYMBOL, SYMBOLS)
map.put(AMBIGUOUS_EXPECTS, "{0} has several compatible expect declarations in modules {1}", SYMBOL, COLLECTION(MODULE_DATA))
map.put(NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS, "Actual class ''{0}'' has no corresponding members for expected class members:{1}", SYMBOL, FirIncompatibleExpectedActualClassScopesRenderer.TEXT)
map.put(ACTUAL_MISSING, "Declaration must be marked with 'actual'")
// Destructuring declaration
map.put(INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION, "Initializer required for destructuring declaration")
@@ -8,8 +8,10 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.diagnostics.rendering.ContextIndependentParameterRenderer
import org.jetbrains.kotlin.diagnostics.rendering.Renderer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirRenderer
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
@@ -34,11 +36,7 @@ object FirDiagnosticRenderers {
}
}
val SYMBOLS = Renderer { symbols: Collection<FirBasedSymbol<*>> ->
symbols.joinToString(prefix = "[", postfix = "]", separator = ", ", limit = 3, truncated = "...") { symbol ->
SYMBOL.render(symbol)
}
}
val SYMBOLS = COLLECTION(SYMBOL)
val RENDER_COLLECTION_OF_TYPES = Renderer { types: Collection<ConeKotlinType> ->
types.joinToString(separator = ", ") { type ->
@@ -133,4 +131,17 @@ object FirDiagnosticRenderers {
}
val FUNCTION_PARAMETERS = Renderer { hasValueParameters: Boolean -> if (hasValueParameters) "..." else "" }
val MODULE_DATA = Renderer<FirModuleData> {
"Module ${it.name}"
}
@Suppress("FunctionName")
fun <T> COLLECTION(renderer: ContextIndependentParameterRenderer<T>): ContextIndependentParameterRenderer<Collection<T>> {
return Renderer { list ->
list.joinToString(prefix = "[", postfix = "]", separator = ", ", limit = 3, truncated = "...") {
renderer.render(it)
}
}
}
}
@@ -0,0 +1,128 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.ContextIndependentParameterRenderer
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
class FirPlatformIncompatibilityDiagnosticRenderer(
private val mode: MultiplatformDiagnosticRenderingMode
) : ContextIndependentParameterRenderer<Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> {
override fun render(
obj: Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
): String {
if (obj.isEmpty()) return ""
return buildString {
mode.newLine(this)
renderIncompatibilityInformation(obj, "", mode)
}
}
companion object {
@JvmField
val TEXT = FirPlatformIncompatibilityDiagnosticRenderer(MultiplatformDiagnosticRenderingMode())
}
}
class FirIncompatibleExpectedActualClassScopesRenderer(
private val mode: MultiplatformDiagnosticRenderingMode
) : ContextIndependentParameterRenderer<List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>> {
override fun render(
obj: List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>
): String {
if (obj.isEmpty()) return ""
return buildString {
mode.newLine(this)
renderIncompatibleClassScopes(obj, "", mode)
}
}
companion object {
@JvmField
val TEXT = FirIncompatibleExpectedActualClassScopesRenderer(MultiplatformDiagnosticRenderingMode())
}
}
open class MultiplatformDiagnosticRenderingMode {
open fun newLine(sb: StringBuilder) {
sb.appendLine()
}
open fun renderList(sb: StringBuilder, elements: List<() -> Unit>) {
sb.appendLine()
for (element in elements) {
element()
}
}
open fun renderSymbol(sb: StringBuilder, symbol: FirBasedSymbol<*>, indent: String) {
sb.append(indent)
sb.append(INDENTATION_UNIT)
sb.appendLine(FirDiagnosticRenderers.SYMBOL.render(symbol))
}
}
private fun StringBuilder.renderIncompatibilityInformation(
map: Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
indent: String,
mode: MultiplatformDiagnosticRenderingMode
) {
for ((incompatibility, descriptors) in map) {
append(indent)
append("The following declaration")
if (descriptors.size == 1) append(" is") else append("s are")
append(" incompatible")
incompatibility.reason?.let { append(" because $it") }
append(":")
mode.renderList(this, descriptors.map { descriptor ->
{ mode.renderSymbol(this, descriptor, indent) }
})
if (incompatibility is Incompatible.ClassScopes) {
append(indent)
append("No actual members are found for expected members listed below:")
mode.newLine(this)
renderIncompatibleClassScopes(incompatibility.unfulfilled, indent, mode)
}
}
}
private fun StringBuilder.renderIncompatibleClassScopes(
unfulfilled: List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>,
indent: String,
mode: MultiplatformDiagnosticRenderingMode
) {
mode.renderList(this, unfulfilled.indices.map { index ->
{
val (descriptor, mapping) = unfulfilled[index]
mode.renderSymbol(this, descriptor, indent)
if (mapping.isNotEmpty()) {
mode.newLine(this)
renderIncompatibilityInformation(mapping, indent + INDENTATION_UNIT, mode)
}
if (index != unfulfilled.lastIndex) {
mode.newLine(this)
}
}
})
}
private const val INDENTATION_UNIT = " "
@@ -272,4 +272,8 @@ object SourceElementPositioningStrategies {
LightTreePositioningStrategies.DECLARATION_WITH_BODY,
PositioningStrategies.DECLARATION_WITH_BODY
)
// TODO
val ACTUAL_DECLARATION_NAME = DEFAULT
val INCOMPATIBLE_DECLARATION = DEFAULT
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
import org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScope
import org.jetbrains.kotlin.fir.scopes.scopeForClass
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
@@ -95,6 +96,7 @@ private fun ConeKotlinType.scope(useSiteSession: FirSession, scopeSession: Scope
}
}
fun FirClassSymbol<*>.defaultType(): ConeClassLikeType = fir.defaultType()
fun FirClass.defaultType(): ConeClassLikeType =
when (this) {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedForCalls
@@ -19,8 +20,11 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
class FirPackageMemberScope(val fqName: FqName, val session: FirSession) : FirScope() {
private val symbolProvider = session.symbolProvider
class FirPackageMemberScope(
val fqName: FqName,
val session: FirSession,
private val symbolProvider: FirSymbolProvider = session.symbolProvider
) : FirScope() {
private val classifierCache: MutableMap<Name, FirClassifierSymbol<*>?> = mutableMapOf()
private val functionCache: MutableMap<Name, List<FirNamedFunctionSymbol>> = mutableMapOf()
private val propertyCache: MutableMap<Name, List<FirPropertySymbol>> = mutableMapOf()
@@ -107,6 +107,20 @@ inline val FirClassLikeSymbol<*>.isLocal: Boolean get() = classId.isLocal
inline val FirClassSymbol<*>.isLocalClassOrAnonymousObject: Boolean
get() = classId.isLocal || this is FirAnonymousObjectSymbol
inline val FirClassLikeSymbol<*>.isExpect: Boolean
get() = when (this) {
is FirRegularClassSymbol -> isExpect
is FirTypeAliasSymbol -> isExpect
else -> false
}
inline val FirClassLikeSymbol<*>.isActual: Boolean
get() = when (this) {
is FirRegularClassSymbol -> isActual
is FirTypeAliasSymbol -> isActual
else -> false
}
inline val FirClassSymbol<*>.isInterface: Boolean
get() = classKind.isInterface
@@ -568,4 +568,10 @@ abstract class AbstractTypeApproximator(
// Nothing or Nothing!
private fun KotlinTypeMarker.isTrivialSub() = lowerBoundIfFlexible().isNothing()
override fun CapturedTypeMarker.typeParameter(): TypeParameterMarker? {
with(ctx) {
return typeParameter()
}
}
}
@@ -30,7 +30,7 @@ class MyImpl2: My {
expect interface Outer {
interface Inner {
open fun bar()
open fun bas() {}
<!EXPECTED_DECLARATION_WITH_BODY!>open fun bas()<!> {}
<!REDUNDANT_MODIFIER!>open<!> abstract fun bat(): Int
fun foo()
}
@@ -5,7 +5,7 @@
expect enum class En {
E1,
E2 {
fun foo() = ""
<!EXPECTED_DECLARATION_WITH_BODY!>fun foo()<!> = ""
},
E3 { };
}
@@ -16,10 +16,10 @@ expect interface My {
// FILE: jvm.kt
actual interface My {
actual fun openFunPositive() = Unit
actual fun openFunNegative()
<!ACTUAL_WITHOUT_EXPECT!>actual fun openFunNegative()<!>
actual fun abstractFun()
actual val openValPositive: Int get() = 0
actual val openValNegative: Int
<!ACTUAL_WITHOUT_EXPECT!>actual val openValNegative: Int<!>
actual val abstractVal: Int
}
@@ -10,7 +10,7 @@ expect annotation class Foo5()
expect annotation class Foo6()
expect annotation class Foo7()
@Foo1
@<!UNRESOLVED_REFERENCE!>Foo1<!>
fun foo() {}
@Foo5
@@ -45,4 +45,4 @@ actual annotation class Foo5
actual annotation class Foo6(val s: String = "value")
actual typealias Foo7 = Bar2
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual typealias Foo7 = Bar2<!>
@@ -34,9 +34,9 @@ actual class Foo1(val s: String)
actual class Foo2(val p: String = "value", i: Int)
actual typealias Foo3 = JavaFoo
actual class Bar1(val s: String)
actual class Bar2(val p: String = "value", i: Int)
actual typealias Bar3 = JavaBar
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual class Bar1(val s: String)<!>
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual class Bar2(val p: String = "value", i: Int)<!>
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual typealias Bar3 = JavaBar<!>
actual class Bar4(val s: String) {
constructor() : this("")
}
@@ -4,7 +4,7 @@
expect open class A
expect class B : A
open class C : <!SUPERTYPE_NOT_INITIALIZED!>A<!>
<!UNRESOLVED_REFERENCE!>open class C : <!SUPERTYPE_NOT_INITIALIZED!>A<!><!>
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@@ -10,7 +10,7 @@ expect class FooBar {
}
fun test() {
Foo()
<!UNRESOLVED_REFERENCE!>Foo<!>()
Bar()
Baz()
FooBar()
@@ -23,11 +23,11 @@ fun foo1(x: String) {}
fun foo2(x: Int, y: Int) {}
fun foo2(x: String) {}
actual fun foo3(): String = ""
<!ACTUAL_WITHOUT_EXPECT!>actual fun foo3(): String = ""<!>
fun foo4(x: Int): String = ""
actual class NoArgConstructor {
actual constructor()
actual constructor(x: Int)
<!ACTUAL_WITHOUT_EXPECT!>actual constructor(x: Int)<!>
constructor(x: String)
}
@@ -17,12 +17,12 @@ expect open class Foo3
// FILE: jvm.kt
interface Foo1
actual interface Foo2
<!ACTUAL_WITHOUT_EXPECT!>actual interface Foo2<!>
actual var s: String = "value"
<!ACTUAL_WITHOUT_EXPECT!>actual var s: String = "value"<!>
fun foo2(): Int = 0
<!REDECLARATION!>actual class Foo3<!>
<!ACTUAL_WITHOUT_EXPECT, REDECLARATION!>actual class Foo3<!>
<!REDECLARATION!>class Foo3<!>
@@ -31,9 +31,9 @@ actual open class Container {
actual fun protectedFun1() {} // OK: protected -> public
actual protected fun protectedFun2() {} // OK: protected -> protected
actual internal fun protectedFun3() {} // BAD: protected -> internal
actual protected fun internalFun3() {} // BAD: internal -> protected
<!ACTUAL_WITHOUT_EXPECT!>actual internal fun protectedFun3() {}<!> // BAD: protected -> internal
<!ACTUAL_WITHOUT_EXPECT!>actual protected fun internalFun3() {}<!> // BAD: internal -> protected
actual open fun openInternalFun() {} // BAD: internal+open -> public
actual internal fun openPublicFun() {} // BAD: open+public -> internal
<!ACTUAL_WITHOUT_EXPECT!>actual open fun openInternalFun() {}<!> // BAD: internal+open -> public
<!ACTUAL_WITHOUT_EXPECT!>actual internal fun openPublicFun() {}<!> // BAD: open+public -> internal
}
@@ -4,7 +4,7 @@
expect class B {
class N {
fun body() {}
<!EXPECTED_DECLARATION_WITH_BODY!>fun body()<!> {}
expect fun extraHeader()
}
}
@@ -39,7 +39,7 @@ actual class C {
actual inner class I
}
actual class D
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual class D<!>
actual class E {
class N
@@ -8,13 +8,13 @@ interface J
expect class Foo : I, C, J
expect class Bar : C()
<!SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR!>expect class Bar : C()<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
actual class Foo : I, C(), J
actual class Bar
<!ACTUAL_WITHOUT_EXPECT!>actual class Bar<!>
// MODULE: m3-js()()(m1-common)
// FILE: js.kt
@@ -13,10 +13,10 @@ expect sealed class SealedWithPlatformActuals : SealedWithSharedActual
package foo
actual sealed class SealedWithSharedActual
class SimpleShared : SealedWithPlatformActuals()
class SimpleShared : <!UNRESOLVED_REFERENCE!>SealedWithPlatformActuals<!>()
// MODULE: main()()(intermediate)
// TARGET_PLATFORM: JVM
package foo
actual sealed class SealedWithPlatformActuals <!NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED!>actual constructor()<!>: SealedWithSharedActual()
actual sealed class SealedWithPlatformActuals <!ACTUAL_WITHOUT_EXPECT, NON_PRIVATE_OR_PROTECTED_CONSTRUCTOR_IN_SEALED!>actual constructor()<!>: SealedWithSharedActual()
@@ -0,0 +1,29 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNSUPPORTED
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
constructor(p: Any)
fun f1(s: String): Int
fun f2(s: List<String>?): MutableMap<Boolean?, Foo>
fun <T : Set<Number>> f3(t: T): T?
}
// MODULE: m2-js()()(m1-common)
// FILE: js.kt
// TODO: do not suppress UNSUPPORTED once JS files in multi-platform tests are analyzed with JS analyzer facade
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual class Foo {
<!ACTUAL_WITHOUT_EXPECT!>actual constructor(p: dynamic) {}<!>
<!ACTUAL_WITHOUT_EXPECT!>actual fun f1(s: dynamic): dynamic = null!!<!>
<!ACTUAL_WITHOUT_EXPECT!>actual fun f2(s: dynamic): MutableMap<Boolean?, Foo> = null!!<!>
<!ACTUAL_WITHOUT_EXPECT!>actual fun <T : Set<Number>> f3(t: T): dynamic = null!!<!>
}<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNSUPPORTED
// MODULE: m1-common
@@ -20,9 +20,9 @@ expect inline class NonInlineActual(val x: Int)
actual inline class Foo1(val x: Int) {
actual fun bar(): String = "Hello"
}
actual inline class Foo2(val x: String)
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual inline class Foo2(val x: String)<!>
actual <!ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS!>inline<!> class Foo3
actual inline class NonInlineExpect(val x: Int)
<!ACTUAL_WITHOUT_EXPECT!>actual inline class NonInlineExpect(val x: Int)<!>
actual class NonInlineActual actual constructor(actual val x: Int)
<!ACTUAL_WITHOUT_EXPECT!>actual class NonInlineActual actual constructor(actual val x: Int)<!>
@@ -22,7 +22,7 @@ fun foo() {
// MODULE: m2-jvm
// FILE: jvm.kt
class Outer actual constructor() {
class Outer <!ACTUAL_WITHOUT_EXPECT!>actual constructor()<!> {
actual class Nested
actual init {}
@@ -9,10 +9,10 @@ expect fun foo()
// FILE: jvm.kt
package jvm
actual fun foo() {}
<!ACTUAL_WITHOUT_EXPECT!>actual fun foo() {}<!>
// MODULE: m3-js()()(m1-common)
// FILE: js.kt
package js
actual fun foo() {}
<!ACTUAL_WITHOUT_EXPECT!>actual fun foo() {}<!>
@@ -9,4 +9,4 @@ expect fun foo()
<!NON_MEMBER_FUNCTION_NO_BODY!>actual fun foo()<!>
<!NON_MEMBER_FUNCTION_NO_BODY!>actual fun bar()<!>
<!ACTUAL_WITHOUT_EXPECT, NON_MEMBER_FUNCTION_NO_BODY!>actual fun bar()<!>
@@ -2,4 +2,4 @@
// MODULE: m1-jvm
// FILE: jvm.kt
actual fun foo() { }
<!ACTUAL_WITHOUT_EXPECT!>actual fun foo() { }<!>
@@ -8,7 +8,7 @@ expect fun nonInlineFun()
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
actual fun inlineFun() { }
<!ACTUAL_WITHOUT_EXPECT!>actual fun inlineFun() { }<!>
actual fun nonInlineFun() { }
// MODULE: m3-js()()(m1-common)
@@ -18,10 +18,10 @@ expect fun f8(vararg x: Any)
// FILE: jvm.kt
actual inline fun f1(noinline s: () -> String) {}
actual inline fun f2(noinline s: () -> String) {}
<!ACTUAL_WITHOUT_EXPECT!>actual inline fun f2(noinline s: () -> String) {}<!>
actual inline fun f3(s: () -> String) {}
actual inline fun f4(crossinline s: () -> String) {}
actual inline fun f5(crossinline s: () -> String) {}
<!ACTUAL_WITHOUT_EXPECT!>actual inline fun f5(crossinline s: () -> String) {}<!>
actual inline fun f6(s: () -> String) {}
actual fun f7(vararg x: Any) {}
actual fun f8(x: Any) {}
<!ACTUAL_WITHOUT_EXPECT!>actual fun f7(vararg x: Any) {}<!>
<!ACTUAL_WITHOUT_EXPECT!>actual fun f8(x: Any) {}<!>
@@ -5,6 +5,7 @@ plugins {
}
dependencies {
implementation(project(":compiler:resolution.common"))
implementation(project(":compiler:fir:tree"))
implementation(project(":compiler:fir:tree:tree-generator"))
implementation(project(":compiler:fir:checkers:checkers-component-generator"))
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticData
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticParameter
@@ -30,6 +31,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.Variance
import kotlin.reflect.KClass
import kotlin.reflect.KType
@@ -296,6 +298,8 @@ private object FirToKtConversionCreator {
LanguageVersionSettings::class,
Variance::class,
FqName::class,
FirModuleData::class,
ExpectActualCompatibility.Incompatible::class,
)
private val KType.kClass: KClass<*>
@@ -26,10 +26,14 @@ import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtIfExpression
@@ -2129,12 +2133,6 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.EXPECTED_PRIVATE_DECLARATION) { firDiagnostic ->
ExpectedPrivateDeclarationImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.VAL_WITH_SETTER) { firDiagnostic ->
ValWithSetterImpl(
firDiagnostic as FirPsiDiagnostic,
@@ -2232,6 +2230,30 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.EXPECTED_CLASS_CONSTRUCTOR_DELEGATION_CALL) { firDiagnostic ->
ExpectedClassConstructorDelegationCallImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER) { firDiagnostic ->
ExpectedClassConstructorPropertyParameterImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.EXPECTED_ENUM_CONSTRUCTOR) { firDiagnostic ->
ExpectedEnumConstructorImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.EXPECTED_ENUM_ENTRY_WITH_BODY) { firDiagnostic ->
ExpectedEnumEntryWithBodyImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.EXPECTED_PROPERTY_INITIALIZER) { firDiagnostic ->
ExpectedPropertyInitializerImpl(
firDiagnostic as FirPsiDiagnostic,
@@ -2250,6 +2272,138 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS) { firDiagnostic ->
SupertypeInitializedInExpectedClassImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.EXPECTED_PRIVATE_DECLARATION) { firDiagnostic ->
ExpectedPrivateDeclarationImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS) { firDiagnostic ->
ImplementationByDelegationInExpectClassImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_TYPE_ALIAS_NOT_TO_CLASS) { firDiagnostic ->
ActualTypeAliasNotToClassImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE) { firDiagnostic ->
ActualTypeAliasToClassWithDeclarationSiteVarianceImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE) { firDiagnostic ->
ActualTypeAliasWithUseSiteVarianceImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION) { firDiagnostic ->
ActualTypeAliasWithComplexSubstitutionImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS) { firDiagnostic ->
ActualFunctionWithDefaultArgumentsImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE) { firDiagnostic ->
ActualAnnotationConflictingDefaultArgumentValueImpl(
firSymbolBuilder.variableLikeBuilder.buildVariableLikeSymbol(firDiagnostic.a.fir),
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND) { firDiagnostic ->
ExpectedFunctionSourceWithDefaultArgumentsNotFoundImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.NO_ACTUAL_FOR_EXPECT) { firDiagnostic ->
NoActualForExpectImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir),
firDiagnostic.b,
firDiagnostic.c.mapKeys { (incompatible, _) ->
incompatible
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol.fir)
}
},
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_WITHOUT_EXPECT) { firDiagnostic ->
ActualWithoutExpectImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir),
firDiagnostic.b.mapKeys { (incompatible, _) ->
incompatible
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol.fir)
}
},
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.AMBIGUOUS_ACTUALS) { firDiagnostic ->
AmbiguousActualsImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir),
firDiagnostic.b.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol.fir)
},
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.AMBIGUOUS_EXPECTS) { firDiagnostic ->
AmbiguousExpectsImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir),
firDiagnostic.b.map { firModuleData ->
firModuleData
},
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS) { firDiagnostic ->
NoActualClassMemberForExpectedClassImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir),
firDiagnostic.b.map { pair ->
firSymbolBuilder.buildSymbol(pair.first.fir) to pair.second.mapKeys { (incompatible, _) ->
incompatible
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol.fir)
}
}
},
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.ACTUAL_MISSING) { firDiagnostic ->
ActualMissingImpl(
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION) { firDiagnostic ->
InitializerRequiredForDestructuringDeclarationImpl(
firDiagnostic as FirPsiDiagnostic,
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol
@@ -33,10 +35,14 @@ import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtIfExpression
@@ -59,6 +65,7 @@ import org.jetbrains.kotlin.psi.KtWhenCondition
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
/*
@@ -1504,10 +1511,6 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = PrivateSetterForOpenProperty::class
}
abstract class ExpectedPrivateDeclaration : KtFirDiagnostic<KtModifierListOwner>() {
override val diagnosticClass get() = ExpectedPrivateDeclaration::class
}
abstract class ValWithSetter : KtFirDiagnostic<KtPropertyAccessor>() {
override val diagnosticClass get() = ValWithSetter::class
}
@@ -1575,6 +1578,22 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ExpectedDeclarationWithBody::class
}
abstract class ExpectedClassConstructorDelegationCall : KtFirDiagnostic<KtConstructorDelegationCall>() {
override val diagnosticClass get() = ExpectedClassConstructorDelegationCall::class
}
abstract class ExpectedClassConstructorPropertyParameter : KtFirDiagnostic<KtParameter>() {
override val diagnosticClass get() = ExpectedClassConstructorPropertyParameter::class
}
abstract class ExpectedEnumConstructor : KtFirDiagnostic<KtConstructor<*>>() {
override val diagnosticClass get() = ExpectedEnumConstructor::class
}
abstract class ExpectedEnumEntryWithBody : KtFirDiagnostic<KtEnumEntry>() {
override val diagnosticClass get() = ExpectedEnumEntryWithBody::class
}
abstract class ExpectedPropertyInitializer : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ExpectedPropertyInitializer::class
}
@@ -1587,6 +1606,82 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ExpectedLateinitProperty::class
}
abstract class SupertypeInitializedInExpectedClass : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = SupertypeInitializedInExpectedClass::class
}
abstract class ExpectedPrivateDeclaration : KtFirDiagnostic<KtModifierListOwner>() {
override val diagnosticClass get() = ExpectedPrivateDeclaration::class
}
abstract class ImplementationByDelegationInExpectClass : KtFirDiagnostic<KtDelegatedSuperTypeEntry>() {
override val diagnosticClass get() = ImplementationByDelegationInExpectClass::class
}
abstract class ActualTypeAliasNotToClass : KtFirDiagnostic<KtTypeAlias>() {
override val diagnosticClass get() = ActualTypeAliasNotToClass::class
}
abstract class ActualTypeAliasToClassWithDeclarationSiteVariance : KtFirDiagnostic<KtTypeAlias>() {
override val diagnosticClass get() = ActualTypeAliasToClassWithDeclarationSiteVariance::class
}
abstract class ActualTypeAliasWithUseSiteVariance : KtFirDiagnostic<KtTypeAlias>() {
override val diagnosticClass get() = ActualTypeAliasWithUseSiteVariance::class
}
abstract class ActualTypeAliasWithComplexSubstitution : KtFirDiagnostic<KtTypeAlias>() {
override val diagnosticClass get() = ActualTypeAliasWithComplexSubstitution::class
}
abstract class ActualFunctionWithDefaultArguments : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ActualFunctionWithDefaultArguments::class
}
abstract class ActualAnnotationConflictingDefaultArgumentValue : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ActualAnnotationConflictingDefaultArgumentValue::class
abstract val parameter: KtVariableLikeSymbol
}
abstract class ExpectedFunctionSourceWithDefaultArgumentsNotFound : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ExpectedFunctionSourceWithDefaultArgumentsNotFound::class
}
abstract class NoActualForExpect : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = NoActualForExpect::class
abstract val declaration: KtSymbol
abstract val module: FirModuleData
abstract val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>
}
abstract class ActualWithoutExpect : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = ActualWithoutExpect::class
abstract val declaration: KtSymbol
abstract val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>
}
abstract class AmbiguousActuals : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = AmbiguousActuals::class
abstract val declaration: KtSymbol
abstract val candidates: List<KtSymbol>
}
abstract class AmbiguousExpects : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = AmbiguousExpects::class
abstract val declaration: KtSymbol
abstract val modules: List<FirModuleData>
}
abstract class NoActualClassMemberForExpectedClass : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = NoActualClassMemberForExpectedClass::class
abstract val declaration: KtSymbol
abstract val members: List<Pair<KtSymbol, Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>>>
}
abstract class ActualMissing : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = ActualMissing::class
}
abstract class InitializerRequiredForDestructuringDeclaration : KtFirDiagnostic<KtDestructuringDeclaration>() {
override val diagnosticClass get() = InitializerRequiredForDestructuringDeclaration::class
}
@@ -13,7 +13,9 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol
@@ -35,10 +37,14 @@ import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtIfExpression
@@ -61,6 +67,7 @@ import org.jetbrains.kotlin.psi.KtWhenCondition
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
/*
@@ -2423,13 +2430,6 @@ internal class PrivateSetterForOpenPropertyImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedPrivateDeclarationImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ExpectedPrivateDeclaration(), KtAbstractFirDiagnostic<KtModifierListOwner> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ValWithSetterImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
@@ -2542,6 +2542,34 @@ internal class ExpectedDeclarationWithBodyImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedClassConstructorDelegationCallImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ExpectedClassConstructorDelegationCall(), KtAbstractFirDiagnostic<KtConstructorDelegationCall> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedClassConstructorPropertyParameterImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ExpectedClassConstructorPropertyParameter(), KtAbstractFirDiagnostic<KtParameter> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedEnumConstructorImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ExpectedEnumConstructor(), KtAbstractFirDiagnostic<KtConstructor<*>> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedEnumEntryWithBodyImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ExpectedEnumEntryWithBody(), KtAbstractFirDiagnostic<KtEnumEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedPropertyInitializerImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
@@ -2563,6 +2591,130 @@ internal class ExpectedLateinitPropertyImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class SupertypeInitializedInExpectedClassImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.SupertypeInitializedInExpectedClass(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedPrivateDeclarationImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ExpectedPrivateDeclaration(), KtAbstractFirDiagnostic<KtModifierListOwner> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ImplementationByDelegationInExpectClassImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ImplementationByDelegationInExpectClass(), KtAbstractFirDiagnostic<KtDelegatedSuperTypeEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualTypeAliasNotToClassImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualTypeAliasNotToClass(), KtAbstractFirDiagnostic<KtTypeAlias> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualTypeAliasToClassWithDeclarationSiteVarianceImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualTypeAliasToClassWithDeclarationSiteVariance(), KtAbstractFirDiagnostic<KtTypeAlias> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualTypeAliasWithUseSiteVarianceImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualTypeAliasWithUseSiteVariance(), KtAbstractFirDiagnostic<KtTypeAlias> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualTypeAliasWithComplexSubstitutionImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualTypeAliasWithComplexSubstitution(), KtAbstractFirDiagnostic<KtTypeAlias> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualFunctionWithDefaultArgumentsImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualFunctionWithDefaultArguments(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualAnnotationConflictingDefaultArgumentValueImpl(
override val parameter: KtVariableLikeSymbol,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualAnnotationConflictingDefaultArgumentValue(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ExpectedFunctionSourceWithDefaultArgumentsNotFoundImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ExpectedFunctionSourceWithDefaultArgumentsNotFound(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class NoActualForExpectImpl(
override val declaration: KtSymbol,
override val module: FirModuleData,
override val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.NoActualForExpect(), KtAbstractFirDiagnostic<KtNamedDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualWithoutExpectImpl(
override val declaration: KtSymbol,
override val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualWithoutExpect(), KtAbstractFirDiagnostic<KtNamedDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class AmbiguousActualsImpl(
override val declaration: KtSymbol,
override val candidates: List<KtSymbol>,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.AmbiguousActuals(), KtAbstractFirDiagnostic<KtNamedDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class AmbiguousExpectsImpl(
override val declaration: KtSymbol,
override val modules: List<FirModuleData>,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.AmbiguousExpects(), KtAbstractFirDiagnostic<KtNamedDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class NoActualClassMemberForExpectedClassImpl(
override val declaration: KtSymbol,
override val members: List<Pair<KtSymbol, Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>>>,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.NoActualClassMemberForExpectedClass(), KtAbstractFirDiagnostic<KtNamedDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class ActualMissingImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.ActualMissing(), KtAbstractFirDiagnostic<KtNamedDeclaration> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class InitializerRequiredForDestructuringDeclarationImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,