[LL] [cls] remove search for sources for decompiled FIR

stub based deserializer provides sources during initialization,
no need to search afterward
This commit is contained in:
Anna Kozlova
2023-05-02 11:13:30 +02:00
committed by teamcity
parent 5ca052f87b
commit 92b6a77431
5 changed files with 7 additions and 573 deletions
@@ -1,229 +0,0 @@
/*
* Copyright 2010-2020 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.analysis.api.fir
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.KtDeclarationAndFirDeclarationEqualityChecker
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.project.structure.KtBuiltinsModule
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
import org.jetbrains.kotlin.analysis.project.structure.KtLibrarySourceModule
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.containingClassLookupTag
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isData
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.resolve.getContainingClass
import org.jetbrains.kotlin.fir.delegatedWrapperData
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.filterIsInstanceWithChecker
//todo introduce LibraryModificationTracker based cache?
internal object FirDeserializedDeclarationSourceProvider {
fun findPsi(fir: FirElement, project: Project): PsiElement? {
return when (fir) {
is FirSimpleFunction -> provideSourceForFunction(fir.delegatedWrapperData?.wrapped ?: fir, project)
is FirProperty -> provideSourceForProperty(fir, project)
is FirClass -> provideSourceForClass(fir, project)
is FirTypeAlias -> provideSourceForTypeAlias(fir, project)
is FirConstructor -> provideSourceForConstructor(fir, project)
is FirEnumEntry -> provideSourceForEnumEntry(fir, project)
else -> null
}
}
fun findClassPsiForGeneratedMembers(fir: FirElement, project: Project): PsiElement? {
if (fir !is FirCallableDeclaration) {
return null
}
val containingClass = fir.getContainingClass(fir.moduleData.session) ?: return null
if (isGeneratedMemberNotWrittenToMetadata(fir.symbol, containingClass)) {
return provideSourceForClass(containingClass, project)
}
return null
}
private fun isGeneratedMemberNotWrittenToMetadata(symbol: FirCallableSymbol<*>, containingClass: FirRegularClass): Boolean {
return when {
containingClass.isEnumClass -> symbol.name in setOf(
StandardNames.ENUM_VALUES,
StandardNames.ENUM_VALUE_OF,
StandardNames.ENUM_ENTRIES,
)
containingClass.isData -> symbol.name in setOf(
OperatorNameConventions.EQUALS,
OperatorNameConventions.TO_STRING,
StandardNames.HASHCODE_NAME,
StandardNames.DATA_CLASS_COPY,
)
else -> false
}
}
private fun provideSourceForFunction(function: FirSimpleFunction, project: Project): PsiElement? {
return provideSourceForCallable(
function,
project,
topLevelSearcher = { declarationProvider -> declarationProvider.getTopLevelFunctions(function.symbol.callableId) },
predicate = { it is KtFunction && it.name == function.name.asString() }
)
}
private fun provideSourceForProperty(property: FirProperty, project: Project): PsiElement? {
return provideSourceForCallable(
property,
project,
topLevelSearcher = { declarationProvider -> declarationProvider.getTopLevelProperties(property.symbol.callableId) },
predicate = { it is KtProperty && it.name == property.name.asString() },
)
}
private fun <T : KtCallableDeclaration> provideSourceForCallable(
callable: FirCallableDeclaration,
project: Project,
topLevelSearcher: (KotlinDeclarationProvider) -> Collection<T>,
predicate: (KtCallableDeclaration) -> Boolean
): PsiElement? {
fun chooseCandidate(candidates: Collection<KtCallableDeclaration>): PsiElement? {
return callable.unwrapFakeOverrides().chooseCorrespondingPsi(candidates)
}
if (callable.isTopLevel) {
return getTargetScopes(callable, project)
.asSequence()
.map { scope -> topLevelSearcher(project.createDeclarationProvider(scope)).filter(KtElement::isCompiled) }
.filter { it.isNotEmpty() }
.firstNotNullOfOrNull { chooseCandidate(it) }
}
val containingKtClass = getContainingKtClassOrObject(callable, getTargetScopes(callable, project), project)
val containingKtFile = containingKtClass?.containingKtFile
if (containingKtFile == null || !containingKtFile.isCompiled) {
return null
}
val candidates = containingKtClass.declarations.filterIsInstanceWithChecker<KtCallableDeclaration>(predicate)
return chooseCandidate(candidates)
}
private fun provideSourceForClass(klass: FirClass, project: Project): PsiElement? {
return getTargetScopes(klass, project)
.asSequence()
.mapNotNull { scope -> classByClassId(klass.symbol.classId, scope, project) }
.filter { it.isCompiled() }
.firstOrNull()
}
private fun provideSourceForTypeAlias(typeAlias: FirTypeAlias, project: Project): PsiElement? {
return getTargetScopes(typeAlias, project)
.asSequence()
.flatMap { scope -> project.createDeclarationProvider(scope).getAllTypeAliasesByClassId(typeAlias.symbol.classId) }
.firstOrNull { it.isCompiled() }
}
private fun provideSourceForConstructor(constructor: FirConstructor, project: Project): PsiElement? {
val scopes = getTargetScopes(constructor, project)
val containingKtClass = getContainingKtClassOrObject(constructor, scopes, project) ?: return null
return if (constructor.isPrimary) {
containingKtClass.primaryConstructor
} else {
constructor.unwrapFakeOverrides()
.chooseCorrespondingPsi(containingKtClass.secondaryConstructors)
}
}
private fun FirCallableDeclaration.chooseCorrespondingPsi(candidates: Collection<KtCallableDeclaration>): KtCallableDeclaration? {
if (candidates.isEmpty()) return null
for (candidate in candidates) {
assert(candidate.isCompiled()) {
"Candidate should be decompiled from metadata because it should have fqName types as we don't use resolve here"
}
if (KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(candidate, this)) {
return candidate
}
}
return null
}
private fun provideSourceForEnumEntry(enumEntry: FirEnumEntry, project: Project): PsiElement? {
val scopes = getTargetScopes(enumEntry, project)
val containingKtClass = getContainingKtClassOrObject(enumEntry, scopes, project) ?: return null
if (containingKtClass.isCompiled()) {
return containingKtClass.body?.enumEntries?.firstOrNull { it.name == enumEntry.name.asString() }
}
return null
}
private fun getTargetScopes(declaration: FirDeclaration, project: Project): List<GlobalSearchScope> {
val originalDeclaration = when (declaration) {
is FirCallableDeclaration -> declaration.unwrapFakeOverrides()
else -> declaration
}
val moduleData = originalDeclaration.llFirModuleData
val module = moduleData.ktModule
return buildList {
add(module.contentScope)
when (module) {
is KtBuiltinsModule -> {
add(ProjectScope.getLibrariesScope(project))
add(ProjectScope.getContentScope(project))
}
is KtLibrarySourceModule, is KtLibraryModule -> {
// Search in other libraries (that might be dependencies of this library)
add(ProjectScope.getLibrariesScope(project))
}
else -> {}
}
}
}
private fun getContainingKtClassOrObject(
firCallable: FirCallableDeclaration,
scopes: List<GlobalSearchScope>,
project: Project
): KtClassOrObject? {
val classId = firCallable.unwrapFakeOverrides().containingClassLookupTag()?.classId ?: return null
return scopes.firstNotNullOfOrNull { scope -> classByClassId(classId, scope, project) }
}
private fun classByClassId(classId: ClassId, scope: GlobalSearchScope, project: Project): KtClassOrObject? {
val correctedClassId = classIdMapping[classId] ?: classId
return project.createDeclarationProvider(scope)
.getAllClassesByClassId(correctedClassId)
.firstOrNull(KtElement::isCompiled)
}
private val FirCallableDeclaration.isTopLevel
get() = symbol.callableId.className == null
private val classIdMapping = (0..23).associate { i ->
StandardClassIds.FunctionN(i) to ClassId(FqName("kotlin.jvm.functions"), Name.identifier("Function$i"))
}
}
@@ -5,20 +5,14 @@
package org.jetbrains.kotlin.analysis.api.fir
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtFakeSourceElement
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtRealPsiSourceElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.psi.KtElement
internal fun KtElement.isCompiled(): Boolean = containingKtFile.isCompiled
private val allowedFakeElementKinds = setOf(
KtFakeSourceElementKind.FromUseSiteTarget,
@@ -36,15 +30,11 @@ internal fun FirElement.getAllowedPsi() = when (val source = source) {
else -> null
}
fun FirElement.findPsi(project: Project): PsiElement? =
fun FirElement.findPsi(): PsiElement? =
getAllowedPsi()
?: FirDeserializedDeclarationSourceProvider.findPsi(this, project)
fun FirBasedSymbol<*>.findPsi(): PsiElement? =
fir.findPsi(fir.moduleData.session)
fun FirElement.findPsi(session: FirSession): PsiElement? =
findPsi((session as LLFirSession).project)
fir.findPsi()
/**
* Finds [PsiElement] which will be used as go-to referenced element for [KtPsiReference]
@@ -52,8 +42,5 @@ fun FirElement.findPsi(session: FirSession): PsiElement? =
* Otherwise, behaves the same way as [findPsi] returns exact PSI declaration corresponding to passed [FirDeclaration]
*/
fun FirDeclaration.findReferencePsi(): PsiElement? {
psi?.let { return it }
val project = (moduleData.session as LLFirSession).project
return FirDeserializedDeclarationSourceProvider.findPsi(this, project)
?: FirDeserializedDeclarationSourceProvider.findClassPsiForGeneratedMembers(this, project)
return psi
}
@@ -1,281 +0,0 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.low.level.api.fir.api
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.createEmptySession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecificEntries
import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.BodyBuildingMode
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addIfNotNull
// TODO replace with structural type comparison?
object KtDeclarationAndFirDeclarationEqualityChecker {
fun representsTheSameDeclaration(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
if (fir.realPsi == psi) return true
if (!modifiersMatch(psi, fir)) return false
if (!receiverTypeMatch(psi, fir)) return false
if (!returnTypesMatch(psi, fir)) return false
if (!typeParametersMatch(psi, fir)) return false
if (fir is FirFunction && !valueParametersMatch(psi, fir)) return false
return true
}
private fun modifiersMatch(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
// According to asymmetric logic in 'RawFirBuilder'
if (psi.parentsOfType<KtDeclaration>().any { it.hasExpectModifier() } != fir.symbol.rawStatus.isExpect) return false
if (psi.hasActualModifier() != fir.symbol.rawStatus.isActual) return false
return true
}
private fun receiverTypeMatch(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
if ((fir.receiverParameter != null) != (psi.receiverTypeReference != null)) return false
if (fir.receiverParameter != null && !isTheSameTypes(
psi.receiverTypeReference!!,
fir.receiverParameter!!.typeRef,
isVararg = false,
)
) {
return false
}
return true
}
private fun returnTypesMatch(psi: KtCallableDeclaration, fir: FirCallableDeclaration): Boolean {
if (psi is KtConstructor<*>) return true
return isTheSameTypes(psi.typeReference!!, fir.returnTypeRef, isVararg = false)
}
private fun typeParametersMatch(psiFunction: KtCallableDeclaration, firFunction: FirCallableDeclaration): Boolean {
if (firFunction.typeParameters.size != psiFunction.typeParameters.size) return false
val boundsByName = psiFunction.typeConstraints.groupBy { it.subjectTypeParameterName?.getReferencedName() }
firFunction.typeParameters.zip(psiFunction.typeParameters) { expectedTypeParameter, candidateTypeParameter ->
if (expectedTypeParameter.symbol.name.toString() != candidateTypeParameter.name) return false
val candidateBounds = mutableListOf<KtTypeReference>()
candidateBounds.addIfNotNull(candidateTypeParameter.extendsBound)
boundsByName[candidateTypeParameter.name]?.forEach {
candidateBounds.addIfNotNull(it.boundTypeReference)
}
val expectedBounds = expectedTypeParameter.symbol.resolvedBounds.filter { it !is FirImplicitNullableAnyTypeRef }
if (candidateBounds.size != expectedBounds.size) return false
expectedBounds.zip(candidateBounds) { expectedBound, candidateBound ->
if (!isTheSameTypes(
candidateBound,
expectedBound,
isVararg = false
)
) {
return false
}
}
}
return true
}
private fun valueParametersMatch(psiFunction: KtCallableDeclaration, firFunction: FirFunction): Boolean {
if (firFunction.valueParameters.size != psiFunction.valueParameters.size) return false
firFunction.valueParameters.zip(psiFunction.valueParameters) { expectedParameter, candidateParameter ->
if (expectedParameter.name.toString() != candidateParameter.name) return false
if (expectedParameter.isVararg != candidateParameter.isVarArg) return false
val candidateParameterType = candidateParameter.typeReference ?: return false
if (!isTheSameTypes(
candidateParameterType,
expectedParameter.returnTypeRef,
isVararg = expectedParameter.isVararg
)
) {
return false
}
}
return true
}
private fun FirTypeRef.renderTypeAsKotlinType(isVararg: Boolean = false): String {
val rendered = when (this) {
is FirResolvedTypeRef -> type.renderTypeAsKotlinType()
is FirUserTypeRef -> {
val renderedQualifier = qualifier.joinToString(separator = ".") { part ->
buildString {
append(part.name)
if (part.typeArgumentList.typeArguments.isNotEmpty()) {
part.typeArgumentList.typeArguments.joinTo(this, prefix = "<", postfix = ">") { it.renderTypeAsKotlinType() }
}
}
}
if (isMarkedNullable) "$renderedQualifier?" else renderedQualifier
}
is FirFunctionTypeRef -> {
val classId = if (isSuspend) {
StandardNames.getSuspendFunctionClassId(parametersCount)
} else {
StandardNames.getFunctionClassId(parametersCount)
}
buildString {
append(classId.asSingleFqName().toString())
val parameters = buildList {
receiverTypeRef?.let(::add)
parameters.mapTo(this) { it.returnTypeRef }
returnTypeRef.let(::add)
}
if (parameters.isNotEmpty()) {
append(parameters.joinToString(prefix = "<", postfix = ">") { it.renderTypeAsKotlinType() })
}
if (isMarkedNullable) {
append("?")
}
}
}
else -> errorWithFirSpecificEntries("Invalid type reference", fir = this)
}
return if (isVararg) {
rendered.asArrayType()
} else {
rendered
}
}
private fun String.asArrayType(): String {
classIdToName[this]?.let { return it }
return "kotlin.Array<out $this>"
}
private val classIdToName: Map<String, String> = buildList<Pair<String, String>> {
StandardClassIds.primitiveArrayTypeByElementType.mapTo(this) { (classId, arrayClassId) ->
classId.asString().replace('/', '.') to arrayClassId.asString().replace('/', '.')
}
StandardClassIds.unsignedArrayTypeByElementType.mapTo(this) { (classId, arrayClassId) ->
classId.asString().replace('/', '.') to arrayClassId.asString().replace('/', '.')
}
}.toMap()
private fun FirTypeProjection.renderTypeAsKotlinType() = when (this) {
is FirStarProjection -> "*"
is FirTypeProjectionWithVariance -> buildString {
append(variance.label)
if (variance != Variance.INVARIANT) {
append(" ")
}
append(typeRef.renderTypeAsKotlinType())
}
else -> errorWithFirSpecificEntries("Invalid type reference", fir = this)
}
private fun isTheSameTypes(
psiTypeReference: KtTypeReference,
coneTypeReference: FirTypeRef,
isVararg: Boolean
): Boolean =
psiTypeReference.toKotlinTypReference().renderTypeAsKotlinType(isVararg) == coneTypeReference.renderTypeAsKotlinType()
@Suppress("DEPRECATION_ERROR")
private fun KtTypeReference.toKotlinTypReference(): FirTypeRef {
// Maybe resolve all types here to not to work with FirTypeRef directly
return RawFirBuilder(
createEmptySession(),
DummyScopeProvider,
bodyBuildingMode = BodyBuildingMode.NORMAL
).buildTypeReference(this)
}
private fun ConeKotlinType.renderTypeAsKotlinType(): String {
val rendered = when (this) {
is ConeClassLikeType -> buildString {
append(lookupTag.classId.asString())
if (typeArguments.isNotEmpty()) {
append(typeArguments.joinToString(prefix = "<", postfix = ">", separator = ", ") { it.renderTypeAsKotlinType() })
}
}
is ConeTypeVariableType -> lookupTag.name.asString()
is ConeLookupTagBasedType -> lookupTag.name.asString()
// NOTE: Flexible types can occur not only as implicit return types,
// but also as implicit parameter types, for example in setters with implicit types
is ConeFlexibleType -> {
// since Kotlin decompiler always "renders" flexible types as their lower bound, we can do the same here
lowerBound.renderTypeAsKotlinType()
}
else -> errorWithFirSpecificEntries("Type should not be present in Kotlin declaration", coneType = this)
}.replace('/', '.')
// UNKNOWN nullability occurs only on flexible types
val nullabilitySuffix = nullability.takeUnless { it == ConeNullability.UNKNOWN }?.suffix.orEmpty()
return rendered + nullabilitySuffix
}
private fun ConeTypeProjection.renderTypeAsKotlinType(): String = when (this) {
ConeStarProjection -> "*"
is ConeKotlinTypeProjectionIn -> "in ${type.renderTypeAsKotlinType()}"
is ConeKotlinTypeProjectionOut -> "out ${type.renderTypeAsKotlinType()}"
is ConeKotlinTypeConflictingProjection -> "CONFLICTING-PROJECTION ${type.renderTypeAsKotlinType()}"
is ConeKotlinType -> renderTypeAsKotlinType()
}
@TestOnly
fun renderPsi(ktFunction: KtFunction): String = buildString {
appendLine("receiver: ${ktFunction.receiverTypeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
ktFunction.valueParameters.forEach { parameter ->
appendLine("${parameter.name}: ${parameter.typeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
}
appendLine("return: ${ktFunction.typeReference?.toKotlinTypReference()?.renderTypeAsKotlinType()}")
}
@TestOnly
fun renderFir(firFunction: FirFunction): String = buildString {
appendLine("receiver: ${firFunction.receiverParameter?.typeRef?.renderTypeAsKotlinType()}")
firFunction.valueParameters.forEach { parameter ->
appendLine("${parameter.name}: ${parameter.returnTypeRef.renderTypeAsKotlinType()}")
}
appendLine("return: ${firFunction.returnTypeRef.renderTypeAsKotlinType()}")
}
private object DummyScopeProvider : FirScopeProvider() {
override fun getUseSiteMemberScope(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession,
memberRequiredPhase: FirResolvePhase?,
): FirTypeScope = shouldNotBeCalled()
override fun getStaticMemberScopeForCallables(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirContainingNamesAwareScope? = shouldNotBeCalled()
override fun getNestedClassifierScope(
klass: FirClass,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirContainingNamesAwareScope? = shouldNotBeCalled()
private fun shouldNotBeCalled(): Nothing = error("Should not be called in RawFirBuilder while converting KtTypeReference")
}
}
@@ -1,43 +0,0 @@
/*
* 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.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.createCache
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.caches.getValue
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirBuiltinSymbolProvider
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
class LLFirBuiltinSymbolProvider(
session: FirSession,
moduleData: FirModuleData,
kotlinScopeProvider: FirKotlinScopeProvider
) : FirBuiltinSymbolProvider(session, moduleData, kotlinScopeProvider) {
private val functionsCache = session.firCachesFactory.createCache { callableId: CallableId ->
buildList {
getTopLevelFunctionSymbolsToByPackageFragments(this, callableId.packageName, callableId.callableName)
}
}
@OptIn(FirSymbolProviderInternals::class)
override fun getTopLevelCallableSymbolsTo(destination: MutableList<FirCallableSymbol<*>>, packageFqName: FqName, name: Name) {
// valid until FirBuiltinSymbolProvider provide property symbols
destination += functionsCache.getValue(CallableId(packageFqName, name))
}
@OptIn(FirSymbolProviderInternals::class)
override fun getTopLevelFunctionSymbolsTo(destination: MutableList<FirNamedFunctionSymbol>, packageFqName: FqName, name: Name) {
destination += functionsCache.getValue(CallableId(packageFqName, name))
}
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.util
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.KtDeclarationAndFirDeclarationEqualityChecker
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirBuiltinsAndCloneableSession
@@ -13,6 +12,7 @@ import org.jetbrains.kotlin.analysis.project.structure.getKtModule
import org.jetbrains.kotlin.analysis.utils.errors.ExceptionAttachmentBuilder
import org.jetbrains.kotlin.analysis.utils.errors.withClassEntry
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.realPsi
import org.jetbrains.kotlin.fir.resolve.providers.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
@@ -110,7 +110,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid
val candidates = symbolProvider.findFunctionCandidates(declaration)
val functionCandidate =
candidates
.firstOrNull { KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(declaration, it.fir) }
.firstOrNull { it.fir.realPsi === declaration }
?: errorWithFirSpecificEntries("We should be able to find a symbol for function", psi = declaration) {
withCandidates(candidates)
}
@@ -124,7 +124,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid
val candidates = symbolProvider.findPropertyCandidates(declaration)
val propertyCandidate =
candidates.firstOrNull { KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(declaration, it.fir) }
candidates.firstOrNull { it.fir.realPsi === declaration }
?: errorWithFirSpecificEntries("We should be able to find a symbol for property", psi = declaration) {
withCandidates(candidates)
}
@@ -181,7 +181,7 @@ private fun representSameConstructor(psiConstructor: KtConstructor<*>, firConstr
return false
}
return KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(psiConstructor, firConstructor)
return firConstructor.realPsi === psiConstructor
}
private fun ExceptionAttachmentBuilder.withCandidates(candidates: List<FirBasedSymbol<*>>) {