[LL FIR] KT-57314 Allow passing index results to Kotlin symbol provider

- If a `KtClassLikeDeclaration` or all `KtFile`s which contain a
  callable are already known, they can now be passed to
  `LLFirProvider$SymbolProvider` directly. This avoids index accesses in
  `providerHelper`.
This commit is contained in:
Marco Pennekamp
2023-03-22 18:24:37 +01:00
committed by Space Team
parent bf1ee6ebed
commit 16af67e99c
2 changed files with 72 additions and 8 deletions
@@ -21,10 +21,12 @@ import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassLikeDeclaration
import org.jetbrains.kotlin.psi.KtFile
@ThreadSafeMutableState
internal class LLFirProvider(
@@ -113,6 +115,14 @@ internal class LLFirProvider(
return getFirClassifierByFqName(classId)?.symbol
}
/**
* This function is optimized for a known [classLikeDeclaration].
*/
@FirSymbolProviderInternals
fun getClassLikeSymbolByClassId(classId: ClassId, classLikeDeclaration: KtClassLikeDeclaration): FirClassLikeSymbol<*>? {
return getFirClassifierByFqNameAndDeclaration(classId, classLikeDeclaration)?.symbol
}
override fun getTopLevelCallableSymbols(packageFqName: FqName, name: Name): List<FirCallableSymbol<*>> {
if (!providerHelper.symbolNameCache.mayHaveTopLevelCallable(packageFqName, name)) return emptyList()
return providerHelper.getTopLevelCallableSymbols(packageFqName, name)
@@ -124,6 +134,18 @@ internal class LLFirProvider(
destination += providerHelper.getTopLevelCallableSymbols(packageFqName, name)
}
/**
* This function is optimized for known [callableFiles], which should be the list of all [KtFile]s that contain the callables.
*/
@FirSymbolProviderInternals
fun getTopLevelCallableSymbolsTo(
destination: MutableList<FirCallableSymbol<*>>,
callableId: CallableId,
callableFiles: Collection<KtFile>,
) {
destination += providerHelper.getTopLevelCallableSymbols(callableId, callableFiles)
}
override fun getTopLevelFunctionSymbols(packageFqName: FqName, name: Name): List<FirNamedFunctionSymbol> {
if (!providerHelper.symbolNameCache.mayHaveTopLevelCallable(packageFqName, name)) return emptyList()
return providerHelper.getTopLevelFunctionSymbols(packageFqName, name)
@@ -135,6 +157,18 @@ internal class LLFirProvider(
destination += providerHelper.getTopLevelFunctionSymbols(packageFqName, name)
}
/**
* This function is optimized for known [callableFiles], which should be the list of all [KtFile]s that contain the functions.
*/
@FirSymbolProviderInternals
fun getTopLevelFunctionSymbolsTo(
destination: MutableList<FirNamedFunctionSymbol>,
callableId: CallableId,
callableFiles: Collection<KtFile>,
) {
destination += providerHelper.getTopLevelFunctionSymbols(callableId, callableFiles)
}
override fun getTopLevelPropertySymbols(packageFqName: FqName, name: Name): List<FirPropertySymbol> {
if (!providerHelper.symbolNameCache.mayHaveTopLevelCallable(packageFqName, name)) return emptyList()
return providerHelper.getTopLevelPropertySymbols(packageFqName, name)
@@ -146,6 +180,18 @@ internal class LLFirProvider(
destination += providerHelper.getTopLevelPropertySymbols(packageFqName, name)
}
/**
* This function is optimized for known [callableFiles], which should be the list of all [KtFile]s that contain the properties.
*/
@FirSymbolProviderInternals
fun getTopLevelPropertySymbolsTo(
destination: MutableList<FirPropertySymbol>,
callableId: CallableId,
callableFiles: Collection<KtFile>,
) {
destination += providerHelper.getTopLevelPropertySymbols(callableId, callableFiles)
}
override fun getPackage(fqName: FqName): FqName? =
providerHelper.getPackage(fqName)
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.AnalysisFlags
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.declarations.FirCallableDeclaration
@@ -28,6 +27,7 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassLikeDeclaration
import org.jetbrains.kotlin.psi.KtFile
internal class LLFirProviderHelper(
firSession: FirSession,
@@ -49,15 +49,16 @@ internal class LLFirProviderHelper(
?: error("Classifier $classId was found in file ${ktClass.containingKtFile.virtualFilePath} but was not found in FirFile")
}
private val callablesByCallableId = firSession.firCachesFactory.createCache<CallableId, List<FirCallableSymbol<*>>> { callableId ->
val files = declarationProvider.getTopLevelCallableFiles(callableId).ifEmpty { return@createCache emptyList() }
buildList {
files.forEach { ktFile ->
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile)
firFile.collectCallableDeclarationsTo(this, callableId.callableName)
private val callablesByCallableId =
firSession.firCachesFactory.createCache<CallableId, List<FirCallableSymbol<*>>, Collection<KtFile>?> { callableId, context ->
val files = context ?: declarationProvider.getTopLevelCallableFiles(callableId).ifEmpty { return@createCache emptyList() }
buildList {
files.forEach { ktFile ->
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile)
firFile.collectCallableDeclarationsTo(this, callableId.callableName)
}
}
}
}
val symbolNameCache = object : LLFirSymbolProviderNameCache(firSession) {
override fun computeClassifierNames(packageFqName: FqName): Set<String>? =
@@ -84,14 +85,31 @@ internal class LLFirProviderHelper(
return callablesByCallableId.getValue(callableId)
}
/**
* [callableFiles] are the [KtFile]s which contain callables of the given package and name. If already known, they can be provided to
* avoid index accesses.
*/
fun getTopLevelCallableSymbols(callableId: CallableId, callableFiles: Collection<KtFile>?): List<FirCallableSymbol<*>> {
if (!allowKotlinPackage && callableId.packageName.isKotlinPackage()) return emptyList()
return callablesByCallableId.getValue(callableId, callableFiles)
}
fun getTopLevelFunctionSymbols(packageFqName: FqName, name: Name): List<FirNamedFunctionSymbol> {
return getTopLevelCallableSymbols(packageFqName, name).filterIsInstance<FirNamedFunctionSymbol>()
}
fun getTopLevelFunctionSymbols(callableId: CallableId, callableFiles: Collection<KtFile>): List<FirNamedFunctionSymbol> {
return getTopLevelCallableSymbols(callableId, callableFiles).filterIsInstance<FirNamedFunctionSymbol>()
}
fun getTopLevelPropertySymbols(packageFqName: FqName, name: Name): List<FirPropertySymbol> {
return getTopLevelCallableSymbols(packageFqName, name).filterIsInstance<FirPropertySymbol>()
}
fun getTopLevelPropertySymbols(callableId: CallableId, callableFiles: Collection<KtFile>): List<FirPropertySymbol> {
return getTopLevelCallableSymbols(callableId, callableFiles).filterIsInstance<FirPropertySymbol>()
}
private fun FirFile.collectCallableDeclarationsTo(list: MutableList<FirCallableSymbol<*>>, name: Name) {
declarations.mapNotNullTo(list) { declaration ->
if (declaration is FirCallableDeclaration && declaration.symbol.callableId.callableName == name) {