[FIR JS] Implement FirJsExportDeclarationChecker

This commit is contained in:
Nikolay Lunyak
2023-01-27 15:22:17 +02:00
parent 624162dc07
commit f644eb7988
38 changed files with 450 additions and 115 deletions
@@ -98,6 +98,16 @@ object JS_DIAGNOSTICS_LIST : DiagnosticList("FirJsErrors") {
val EXPORT by object : DiagnosticGroup("Export") {
val NESTED_JS_EXPORT by error<KtElement>()
val WRONG_EXPORTED_DECLARATION by error<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<String>("kind")
}
val NON_EXPORTABLE_TYPE by warning<KtElement>(PositioningStrategy.DECLARATION_SIGNATURE_OR_DEFAULT) {
parameter<String>("kind")
parameter<ConeKotlinType>("type")
}
val NON_CONSUMABLE_EXPORTED_IDENTIFIER by warning<KtElement>(PositioningStrategy.DEFAULT) {
parameter<String>("name")
}
}
val DYNAMICS by object : DiagnosticGroup("Dynamics") {
@@ -7,6 +7,7 @@ plugins {
dependencies {
api(project(":core:compiler.common.js"))
api(project(":js:js.ast"))
api(project(":compiler:fir:checkers"))
/*
@@ -81,6 +81,9 @@ object FirJsErrors {
// Export
val NESTED_JS_EXPORT by error0<KtElement>()
val WRONG_EXPORTED_DECLARATION by error1<KtElement, String>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NON_EXPORTABLE_TYPE by warning2<KtElement, String, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val NON_CONSUMABLE_EXPORTED_IDENTIFIER by warning1<KtElement, String>()
// Dynamics
val DELEGATION_BY_DYNAMIC by error0<KtElement>()
@@ -46,6 +46,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NESTED_JS_EXPORT
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_ABSTRACT_MEMBER_OF_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_CONSUMABLE_EXPORTED_IDENTIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_EXPORTABLE_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.NON_EXTERNAL_DECLARATION_IN_INAPPROPRIATE_FILE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE
@@ -54,6 +56,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.RUNTIME_ANNO
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.UNCHECKED_CAST_TO_EXTERNAL_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_BODY_OF_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_DEFAULT_VALUE_FOR_EXTERNAL_FUN_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_EXPORTED_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_INITIALIZER_OF_EXTERNAL_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors.WRONG_JS_QUALIFIER
@@ -169,6 +172,18 @@ object FirJsErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
)
map.put(EXTERNAL_INTERFACE_AS_CLASS_LITERAL, "Can't refer to external interface from class literal")
map.put(NESTED_JS_EXPORT, "@JsExport is only allowed on files and top-level declarations")
map.put(WRONG_EXPORTED_DECLARATION, "Declaration of such kind ({0}) can''t be exported to JS", CommonRenderers.STRING)
map.put(
NON_EXPORTABLE_TYPE,
"Exported declaration uses non-exportable {0} type: {1}",
CommonRenderers.STRING,
FirDiagnosticRenderers.RENDER_TYPE,
)
map.put(
NON_CONSUMABLE_EXPORTED_IDENTIFIER,
"Exported declaration contains non-consumable identifier '${0}', that can't be represented inside TS definitions and ESM",
CommonRenderers.STRING,
)
map.checkMissingMessages(FirJsErrors)
}
@@ -8,22 +8,22 @@
package org.jetbrains.kotlin.fir.analysis.js.checkers
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.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenFunctions
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationStringParameter
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.hasAnnotationOrInsideAnnotatedClass
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.js.PredefinedAnnotation
import org.jetbrains.kotlin.js.common.isES5IdentifierPart
import org.jetbrains.kotlin.js.common.isES5IdentifierStart
import org.jetbrains.kotlin.name.JsStandardClassIds
private val FirBasedSymbol<*>.isExternal
@@ -72,6 +72,13 @@ fun FirBasedSymbol<*>.getJsName(session: FirSession): String? {
return getAnnotationStringParameter(JsStandardClassIds.Annotations.JsName, session)
}
fun sanitizeName(name: String): String {
if (name.isEmpty()) return "_"
val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' }
return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("")
}
fun FirBasedSymbol<*>.isNativeObject(session: FirSession): Boolean {
if (hasAnnotationOrInsideAnnotatedClass(JsStandardClassIds.Annotations.JsNative, session) || isEffectivelyExternal(session)) {
return true
@@ -109,8 +116,35 @@ fun FirBasedSymbol<*>.isPredefinedObject(session: FirSession): Boolean {
return false
}
fun FirBasedSymbol<*>.isExportedObject(session: FirSession): Boolean {
val declaration = fir
if (declaration is FirMemberDeclaration) {
val visibility = declaration.visibility
if (visibility != Visibilities.Public && visibility != Visibilities.Protected) {
return false
}
}
return when {
hasAnnotationOrInsideAnnotatedClass(JsStandardClassIds.Annotations.JsExportIgnore, session) -> false
hasAnnotationOrInsideAnnotatedClass(JsStandardClassIds.Annotations.JsExport, session) -> true
else -> getContainingFile(session)?.hasAnnotation(JsStandardClassIds.Annotations.JsExport, session) == true
}
}
private fun FirBasedSymbol<*>.getContainingFile(session: FirSession): FirFile? {
return when (this) {
is FirCallableSymbol<*> -> session.firProvider.getFirCallableContainerFile(this)
is FirClassLikeSymbol<*> -> session.firProvider.getFirClassifierContainerFileIfAny(this)
else -> return null
}
}
fun FirBasedSymbol<*>.isNativeObject(context: CheckerContext) = isNativeObject(context.session)
fun FirBasedSymbol<*>.isNativeInterface(context: CheckerContext) = isNativeInterface(context.session)
fun FirBasedSymbol<*>.isPredefinedObject(context: CheckerContext) = isPredefinedObject(context.session)
fun FirBasedSymbol<*>.isExportedObject(context: CheckerContext) = isExportedObject(context.session)
@@ -28,6 +28,7 @@ object JsDeclarationCheckers : DeclarationCheckers() {
FirJsExternalFileChecker,
FirJsNameChecker,
FirJsExportAnnotationChecker,
FirJsExportDeclarationChecker,
)
override val classCheckers: Set<FirClassChecker>
@@ -0,0 +1,224 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.js.checkers.declaration
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationFirstArgument
import org.jetbrains.kotlin.fir.analysis.checkers.isTopLevel
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
import org.jetbrains.kotlin.fir.analysis.js.checkers.isExportedObject
import org.jetbrains.kotlin.fir.analysis.js.checkers.sanitizeName
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.js.common.RESERVED_KEYWORDS
import org.jetbrains.kotlin.js.common.SPECIAL_KEYWORDS
import org.jetbrains.kotlin.name.JsStandardClassIds
object FirJsExportDeclarationChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (!declaration.symbol.isExportedObject(context) || declaration !is FirMemberDeclaration) {
return
}
fun checkTypeParameter(typeParameter: FirTypeParameterRef) {
for (upperBound in typeParameter.symbol.resolvedBounds) {
if (!upperBound.type.isExportable(context.session)) {
reporter.reportOn(typeParameter.source, FirJsErrors.NON_EXPORTABLE_TYPE, "upper bound", upperBound.type, context)
}
}
}
fun checkValueParameter(valueParameter: FirValueParameter) {
val type = valueParameter.returnTypeRef.coneType
if (!type.isExportable(context.session)) {
reporter.reportOn(valueParameter.source, FirJsErrors.NON_EXPORTABLE_TYPE, "parameter", type, context)
}
}
val hasJsName = declaration.hasAnnotation(JsStandardClassIds.Annotations.JsName, context.session)
fun reportWrongExportedDeclaration(kind: String) {
reporter.reportOn(declaration.source, FirJsErrors.WRONG_EXPORTED_DECLARATION, kind, context)
}
if (declaration.isExpect) {
reportWrongExportedDeclaration("expect")
}
validateDeclarationOnConsumableName(declaration, context, reporter)
when (declaration) {
is FirFunction -> {
for (typeParameter in declaration.typeParameters) {
checkTypeParameter(typeParameter)
}
if (declaration.isInlineWithReified) {
reportWrongExportedDeclaration("inline function with reified type parameters")
return
}
if (declaration.isSuspend) {
reportWrongExportedDeclaration("suspend function")
return
}
if (declaration is FirConstructor && !declaration.isPrimary && !hasJsName) {
reportWrongExportedDeclaration("secondary constructor without @JsName")
}
// Properties are checked instead of property accessors
if (declaration is FirPropertyAccessor) {
return
}
for (parameter in declaration.valueParameters) {
checkValueParameter(parameter)
}
val returnType = declaration.returnTypeRef.coneType
if (!returnType.isExportable(context.session)) {
reporter.reportOn(declaration.source, FirJsErrors.NON_EXPORTABLE_TYPE, "return type", returnType, context)
}
}
is FirProperty -> {
if (declaration.source?.kind == KtFakeSourceElementKind.PropertyFromParameter) {
return
}
if (declaration.isExtension) {
reportWrongExportedDeclaration("extension property")
return
}
val returnType = declaration.returnTypeRef.coneType
if (!returnType.isExportable(context.session)) {
reporter.reportOn(declaration.source, FirJsErrors.NON_EXPORTABLE_TYPE, "return type", returnType, context)
}
}
is FirClass -> {
for (typeParameter in declaration.typeParameters) {
checkTypeParameter(typeParameter)
}
val wrongDeclaration: String? = when (declaration.classKind) {
ClassKind.ANNOTATION_CLASS -> "annotation class"
ClassKind.CLASS -> when {
context.isInsideInterface -> "nested class inside exported interface"
declaration.isInline -> "value class"
else -> null
}
else -> if (context.isInsideInterface) {
"${if (declaration.status.isCompanion) "companion object" else "nested/inner declaration"} inside exported interface"
} else null
}
if (wrongDeclaration != null) {
reportWrongExportedDeclaration(wrongDeclaration)
}
}
else -> {}
}
}
private val CheckerContext.isInsideInterface
get(): Boolean {
val parent = containingDeclarations.lastOrNull() as? FirClass
return parent != null && parent.isInterface
}
private val FirCallableDeclaration.isInlineWithReified: Boolean
get() = when (this) {
is FirPropertyAccessor -> {
@OptIn(SymbolInternals::class)
this.propertySymbol.fir.isInlineWithReified
}
else -> typeParameters.any { it.symbol.isReified }
}
private fun ConeKotlinType.isExportableReturn(session: FirSession, currentlyProcessed: MutableSet<ConeKotlinType> = mutableSetOf()) =
isUnit || isExportable(session, currentlyProcessed)
private fun ConeKotlinType.isExportable(
session: FirSession,
currentlyProcessed: MutableSet<ConeKotlinType> = mutableSetOf(),
): Boolean {
if (!currentlyProcessed.add(this)) {
return true
}
currentlyProcessed.add(this)
val hasNonExportableArgument = typeArguments.any { it.type?.isExportable(session, currentlyProcessed) != true }
if (hasNonExportableArgument) {
currentlyProcessed.remove(this)
return false
}
currentlyProcessed.remove(this)
if (isBasicFunctionType(session)) {
typeArguments.lastOrNull()?.type?.isExportableReturn(session, currentlyProcessed)
}
val nonNullable = withNullability(ConeNullability.NOT_NULL, session.typeContext)
val isPrimitiveExportableType = nonNullable.isAny || nonNullable.isNullableAny
|| nonNullable is ConeDynamicType || isPrimitiveExportableConeKotlinType
val symbol = toSymbol(session)
return when {
isPrimitiveExportableType -> true
@OptIn(SymbolInternals::class)
symbol?.fir is FirMemberDeclaration -> false
isEnum -> true
else -> symbol?.isEffectivelyExternal(session) == true || symbol?.isExportedObject(session) == true
}
}
private val ConeKotlinType.isPrimitiveExportableConeKotlinType: Boolean
get() = this is ConeTypeParameterType
|| isBoolean
|| isThrowableOrNullableThrowable
|| isString
|| isPrimitiveNumberOrNullableType && !isLong
|| isNothingOrNullableNothing
|| isArrayType
private fun validateDeclarationOnConsumableName(
declaration: FirMemberDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
) {
if (!context.isTopLevel || declaration.nameOrSpecialName.isSpecial) {
return
}
val jsNameArgument = declaration.symbol.getAnnotationFirstArgument(JsStandardClassIds.Annotations.JsName, context.session)
val reportTarget = jsNameArgument?.source ?: declaration.source
val name = (jsNameArgument as? FirConstExpression<*>)?.value as? String ?: declaration.nameOrSpecialName.asString()
if (name in SPECIAL_KEYWORDS || (name !in RESERVED_KEYWORDS && sanitizeName(name) == name)) {
return
}
reporter.reportOn(reportTarget, FirJsErrors.NON_CONSUMABLE_EXPORTED_IDENTIFIER, name, context)
}
}
@@ -736,9 +736,13 @@ fun FirBasedSymbol<*>.hasAnnotationOrInsideAnnotatedClass(classId: ClassId, sess
fun FirDeclaration.hasAnnotationOrInsideAnnotatedClass(classId: ClassId, session: FirSession) =
symbol.hasAnnotationOrInsideAnnotatedClass(classId, session)
fun FirBasedSymbol<*>.getAnnotationStringParameter(classId: ClassId, session: FirSession): String? {
fun FirBasedSymbol<*>.getAnnotationFirstArgument(classId: ClassId, session: FirSession): FirExpression? {
val annotation = getAnnotationByClassId(classId, session) as? FirAnnotationCall
val expression = annotation?.argumentMapping?.mapping?.values?.firstOrNull() as? FirConstExpression<*>
return annotation?.argumentMapping?.mapping?.values?.firstOrNull()
}
fun FirBasedSymbol<*>.getAnnotationStringParameter(classId: ClassId, session: FirSession): String? {
val expression = getAnnotationFirstArgument(classId, session) as? FirConstExpression<*>
return expression?.value as? String
}
@@ -26,7 +26,10 @@ val ConeKotlinType.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boo
val ConeKotlinType.isNullableBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, true)
val ConeKotlinType.isBooleanOrNullableBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, null)
val ConeKotlinType.isThrowableOrNullableThrowable: Boolean get() = isAnyOfBuiltinType(setOf(StandardClassIds.Throwable))
val ConeKotlinType.isChar: Boolean get() = isBuiltinType(StandardClassIds.Char, false)
val ConeKotlinType.isCharOrNullableChar: Boolean get() = isAnyOfBuiltinType(setOf(StandardClassIds.Char))
val ConeKotlinType.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false)
val ConeKotlinType.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false)
@@ -37,6 +40,8 @@ val ConeKotlinType.isUInt: Boolean get() = isBuiltinType(StandardClassIds.UInt,
val ConeKotlinType.isULong: Boolean get() = isBuiltinType(StandardClassIds.ULong, false)
val ConeKotlinType.isPrimitiveOrNullablePrimitive: Boolean get() = isAnyOfBuiltinType(StandardClassIds.primitiveTypes)
val ConeKotlinType.isPrimitive: Boolean get() = isPrimitiveOrNullablePrimitive && nullability == ConeNullability.NOT_NULL
val ConeKotlinType.isPrimitiveNumberOrNullableType: Boolean
get() = isPrimitiveOrNullablePrimitive && !isBooleanOrNullableBoolean && !isCharOrNullableChar
val ConeKotlinType.isArrayType: Boolean
get() {
return isBuiltinType(StandardClassIds.Array, false) ||
@@ -540,15 +540,6 @@ fun FirBasedSymbol<*>.getOwnerLookupTag(): ConeClassLikeLookupTag? {
}
}
fun FirClassLikeSymbol<*>.getContainingClassLookupTag(): ConeClassLikeLookupTag? {
return if (classId.isLocal) {
(fir as? FirRegularClass)?.containingClassForLocal()
} else {
val ownerId = classId.outerClassId
ownerId?.let { it.toLookupTag() }
}
}
fun FirBasedSymbol<*>.isVariableOrNamedFunction(): Boolean {
return this is FirVariableSymbol || this is FirNamedFunctionSymbol || this is FirPropertyAccessorSymbol
}
@@ -233,7 +233,10 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
fun constructorTypeParametersFromConstructedClass(ownerTypeParameters: List<FirTypeParameterRef>): List<FirTypeParameterRef> {
return ownerTypeParameters.mapNotNull {
val declaredTypeParameter = (it as? FirTypeParameter) ?: return@mapNotNull null
buildConstructedClassTypeParameterRef { symbol = declaredTypeParameter.symbol }
buildConstructedClassTypeParameterRef {
source = declaredTypeParameter.symbol.source?.fakeElement(KtFakeSourceElementKind.ConstructorTypeParameter)
symbol = declaredTypeParameter.symbol
}
}
}
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.builder.buildOuterClassTypeParameterRef
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.symbols.impl.FirFileSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.name.ClassId
@@ -48,6 +49,7 @@ class Context<T> {
var forcedElementSourceKind: KtSourceElementKind? = null
val dispatchReceiverTypesStack = mutableListOf<ConeClassLikeType>()
var containerIsExpect: Boolean = false
var containingFileSymbol: FirFileSymbol? = null
fun pushFirTypeParameters(isInnerOrLocal: Boolean, parameters: List<FirTypeParameterRef>) {
capturedTypeParameters.add(StatusFirTypeParameterSymbolList(isInnerOrLocal, parameters.map { it.symbol }))
@@ -10,9 +10,11 @@ import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.declarations.utils.isSynthetic
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeIntersectionType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.toLookupTag
fun FirCallableSymbol<*>.dispatchReceiverClassTypeOrNull(): ConeClassLikeType? =
fir.dispatchReceiverClassTypeOrNull()
@@ -41,6 +43,15 @@ fun FirRegularClass.containingClassForLocal(): ConeClassLikeLookupTag? =
fun FirDanglingModifierList.containingClass(): ConeClassLikeLookupTag? =
containingClassAttr
fun FirClassLikeSymbol<*>.getContainingClassLookupTag(): ConeClassLikeLookupTag? {
return if (classId.isLocal) {
(fir as? FirRegularClass)?.containingClassForLocal()
} else {
val ownerId = classId.outerClassId
ownerId?.toLookupTag()
}
}
private object ContainingClassKey : FirDeclarationDataKey()
var FirCallableDeclaration.containingClassForStaticMemberAttr: ConeClassLikeLookupTag? by FirDeclarationDataRegistry.data(ContainingClassKey)
var FirRegularClass.containingClassForLocalAttr: ConeClassLikeLookupTag? by FirDeclarationDataRegistry.data(ContainingClassKey)
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.fir.declarations.comparators
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.types.FirTypeRefComparator
import org.jetbrains.kotlin.name.Name
object FirMemberDeclarationComparator : Comparator<FirMemberDeclaration> {
// Comparing different kinds of callable members by assigning distinct priorities to those members.
@@ -28,16 +28,6 @@ object FirMemberDeclarationComparator : Comparator<FirMemberDeclaration> {
is FirBackingField -> 0
}
private val FirMemberDeclaration.name: Name
get() = when (this) {
is FirCallableDeclaration ->
this.symbol.callableId.callableName
is FirClass ->
this.classId.shortClassName
is FirTypeAlias ->
this.name
}
override fun compare(a: FirMemberDeclaration, b: FirMemberDeclaration): Int {
val priorityDiff = a.priority - b.priority
if (priorityDiff != 0) {
@@ -51,7 +41,7 @@ object FirMemberDeclarationComparator : Comparator<FirMemberDeclaration> {
return 0
}
return a.name.compareTo(b.name)
return a.nameOrSpecialName.compareTo(b.nameOrSpecialName)
}
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
val FirTypeAlias.expandedConeType: ConeClassLikeType? get() = expandedTypeRef.coneTypeSafe()
@@ -45,3 +46,13 @@ val FirDeclaration.isNonLocal
}
val FirCallableDeclaration.isExtension get() = receiverParameter != null
val FirMemberDeclaration.nameOrSpecialName: Name
get() = when (this) {
is FirCallableDeclaration ->
this.symbol.callableId.callableName
is FirClass ->
this.classId.shortClassName
is FirTypeAlias ->
this.name
}