diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirCombinedKotlinSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirCombinedKotlinSymbolProvider.kt index 987c98aeb23..fb0aafcb801 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirCombinedKotlinSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirCombinedKotlinSymbolProvider.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals +import org.jetbrains.kotlin.fir.resolve.providers.flatMapToNullableSet import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol @@ -47,13 +48,14 @@ internal class LLFirCombinedKotlinSymbolProvider private constructor( private val declarationProvider: KotlinDeclarationProvider, ) : LLFirSelectingCombinedSymbolProvider(session, project, providers) { private val symbolNameCache = object : LLFirSymbolProviderNameCacheBase(session) { - override fun computeClassifierNames(packageFqName: FqName): Set? = buildSet { - providers.forEach { addAll(it.knownTopLevelClassifiersInPackage(packageFqName) ?: return null) } - } + override fun computeClassifierNames(packageFqName: FqName): Set? = + providers.flatMapToNullableSet { it.knownTopLevelClassifiersInPackage(packageFqName) } - override fun computeCallableNames(packageFqName: FqName): Set? = buildSet { - providers.forEach { addAll(it.computeCallableNamesInPackage(packageFqName) ?: return null) } - } + override fun computeCallableNames(packageFqName: FqName): Set? = + providers.flatMapToNullableSet { it.computeCallableNamesInPackage(packageFqName) } + + override fun computePackageNamesWithTopLevelCallables(): Set? = + providers.flatMapToNullableSet { it.computePackageSetWithTopLevelCallables() } } private val classifierCache = NullableCaffeineCache> { it.maximumSize(500) } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProvider.kt index 02746f27787..95f7aca91f8 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProvider.kt @@ -195,8 +195,8 @@ internal class LLFirProvider( override fun getPackage(fqName: FqName): FqName? = providerHelper.getPackage(fqName) - // Computing the set of such package names is expensive and would require a new index. For now, it is not worth the marginal gains. - override fun computePackageSetWithTopLevelCallables(): Set? = null + override fun computePackageSetWithTopLevelCallables(): Set? = + providerHelper.symbolNameCache.getPackageNamesWithTopLevelCallables() override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? = providerHelper.symbolNameCache.getTopLevelClassifierNamesInPackage(packageFqName) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt index 6a56bad83ef..6a3386717c9 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt @@ -70,30 +70,34 @@ private class LLFirResolveExtensionToolNameCache( private val packageFilter: LLFirResolveExtensionToolPackageFilter, private val fileProvider: LLFirResolveExtensionsFileProvider, ) : LLFirSymbolProviderNameCache() { - override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set? = forbidAnalysis { + override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set = forbidAnalysis { if (!packageFilter.packageExists(packageFqName)) return emptySet() - return fileProvider.getFilesByPackage(packageFqName) + fileProvider.getFilesByPackage(packageFqName) .flatMap { it.getTopLevelClassifierNames() } .mapTo(mutableSetOf()) { it.asString() } } + override fun getPackageNamesWithTopLevelCallables(): Set = forbidAnalysis { + packageFilter.getAllPackages().mapTo(mutableSetOf()) { it.asString() } + } + override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set = forbidAnalysis { if (!packageFilter.packageExists(packageFqName)) return emptySet() - return fileProvider.getFilesByPackage(packageFqName) + fileProvider.getFilesByPackage(packageFqName) .flatMapTo(mutableSetOf()) { it.getTopLevelCallableNames() } } override fun mayHaveTopLevelClassifier(classId: ClassId, mayHaveFunctionClass: Boolean): Boolean = forbidAnalysis { if (!packageFilter.packageExists(classId.packageFqName)) return false - return fileProvider.getFilesByPackage(classId.packageFqName) + fileProvider.getFilesByPackage(classId.packageFqName) .any { it.mayHaveTopLevelClassifier(classId.getTopLevelShortClassName()) } } override fun mayHaveTopLevelCallable(packageFqName: FqName, name: Name): Boolean = forbidAnalysis { if (!packageFilter.packageExists(packageFqName)) return false - return fileProvider.getFilesByPackage(packageFqName) + fileProvider.getFilesByPackage(packageFqName) .any { it.mayHaveTopLevelCallable(name) } } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt index ea5d5d8afa3..a0e21e62d30 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/stubBased/deserialization/JvmStubBasedFirDeserializedSymbolProvider.kt @@ -52,9 +52,6 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( ) : FirSymbolProvider(session) { private val declarationProvider by lazy(LazyThreadSafetyMode.PUBLICATION) { project.createDeclarationProvider(scope, module = null) } private val moduleData = moduleDataProvider.getModuleData(null) - private val packageSetWithTopLevelCallableDeclarations: Set by lazy(LazyThreadSafetyMode.PUBLICATION) { - declarationProvider.computePackageSetWithTopLevelCallableDeclarations() - } private val namesByPackageCache by lazy(LazyThreadSafetyMode.PUBLICATION) { LLFirKotlinSymbolProviderNameCache( @@ -80,8 +77,8 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( private val functionCache = session.firCachesFactory.createCache(::loadFunctionsByCallableId) private val propertyCache = session.firCachesFactory.createCache(::loadPropertiesByCallableId) - override fun computePackageSetWithTopLevelCallables(): Set { - return packageSetWithTopLevelCallableDeclarations + override fun computePackageSetWithTopLevelCallables(): Set? { + return namesByPackageCache.getPackageNamesWithTopLevelCallables() } override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = @@ -203,7 +200,6 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( } private fun > FirCache, Nothing?>.getCallables(id: CallableId): List { - if (id.packageName.asString() !in packageSetWithTopLevelCallableDeclarations) return emptyList() if (!namesByPackageCache.mayHaveTopLevelCallable(id.packageName, id.callableName)) return emptyList() return getValue(id) } @@ -218,13 +214,11 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( destination += propertyCache.getCallables(CallableId(packageFqName, name)) } - override fun getPackage(fqName: FqName): FqName? { - return if (!namesByPackageCache.getTopLevelClassifierNamesInPackage(fqName) - .isNullOrEmpty() || packageSetWithTopLevelCallableDeclarations.contains(fqName.asString()) - ) { - fqName - } else null - } + override fun getPackage(fqName: FqName): FqName? = + fqName.takeIf { + namesByPackageCache.getTopLevelClassifierNamesInPackage(fqName)?.isNotEmpty() == true || + namesByPackageCache.getPackageNamesWithTopLevelCallables()?.contains(fqName.asString()) == true + } override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { if (!namesByPackageCache.mayHaveTopLevelClassifier(classId, mayHaveFunctionClass = false)) return null diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolProviderNameCache.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolProviderNameCache.kt index 87b9555473d..85f98aba65a 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolProviderNameCache.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolProviderNameCache.kt @@ -22,6 +22,9 @@ internal class LLFirKotlinSymbolProviderNameCache( .getTopLevelKotlinClassLikeDeclarationNamesInPackage(packageFqName) .mapTo(mutableSetOf()) { it.asString() } + override fun computePackageNamesWithTopLevelCallables(): Set = + declarationProvider.computePackageSetWithTopLevelCallableDeclarations() + override fun computeCallableNames(packageFqName: FqName): Set = declarationProvider.getTopLevelCallableNamesInPackage(packageFqName) } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirSymbolProviderNameCache.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirSymbolProviderNameCache.kt index 1566a0ef0e0..98f1522bceb 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirSymbolProviderNameCache.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirSymbolProviderNameCache.kt @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.name.Name * Caches the names of classifiers and callables contained in a package. [LLFirSymbolProviderNameCache] is used by symbol providers to abort * symbol finding early if the symbol name isn't contained in the symbol provider's domain. * - * For the [getTopLevelClassifierNamesInPackage] and [getTopLevelCallableNamesInPackage] functions, + * For the [getTopLevelClassifierNamesInPackage], [getTopLevelCallableNamesInPackage], and [getPackageNamesWithTopLevelCallables] functions, * the result may have false-positive entries but cannot have false-negative entries. * It should contain all the names in the package but may have some additional names that are not there. * Also, the `null` value might be returned when it's too heavyweight to calculate the results. @@ -35,6 +35,10 @@ internal abstract class LLFirSymbolProviderNameCache { */ abstract fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set? + /** + * Returns the set of package names which contain a top-level callable declaration within the cache's scope. + */ + abstract fun getPackageNamesWithTopLevelCallables(): Set? /** * Returns the set of top-level callable (functions and properties) names in a given scope inside the [packageFqName]. @@ -59,24 +63,34 @@ internal abstract class LLFirSymbolProviderNameCache { abstract fun mayHaveTopLevelCallable(packageFqName: FqName, name: Name): Boolean } - internal abstract class LLFirSymbolProviderNameCacheBase( private val firSession: FirSession ) : LLFirSymbolProviderNameCache() { abstract fun computeClassifierNames(packageFqName: FqName): Set? + abstract fun computePackageNamesWithTopLevelCallables(): Set? abstract fun computeCallableNames(packageFqName: FqName): Set? private val topLevelClassifierNamesByPackage = - firSession.firCachesFactory.createCache?>(::computeClassifierNames) + firSession.firCachesFactory.createCache(::computeClassifierNames) + + private val topLevelCallablePackageNames by lazy(LazyThreadSafetyMode.PUBLICATION) { + computePackageNamesWithTopLevelCallables() + } private val topLevelCallableNamesByPackage = - firSession.firCachesFactory.createCache?>(::computeCallableNames) + firSession.firCachesFactory.createCache(::computeCallableNames) final override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set? = topLevelClassifierNamesByPackage.getValue(packageFqName) - final override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set? = - topLevelCallableNamesByPackage.getValue(packageFqName) + final override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set? { + val packageNames = getPackageNamesWithTopLevelCallables() + if (packageNames != null && packageFqName.asString() !in packageNames) return null + + return topLevelCallableNamesByPackage.getValue(packageFqName) + } + + final override fun getPackageNamesWithTopLevelCallables(): Set? = topLevelCallablePackageNames final override fun mayHaveTopLevelClassifier(classId: ClassId, mayHaveFunctionClass: Boolean): Boolean { val names = getTopLevelClassifierNamesInPackage(classId.packageFqName) ?: return true @@ -90,10 +104,10 @@ internal abstract class LLFirSymbolProviderNameCacheBase( } } - internal object LLFirEmptySymbolProviderNameCache : LLFirSymbolProviderNameCache() { - override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set? = emptySet() - override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set? = emptySet() + override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set = emptySet() + override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set = emptySet() + override fun getPackageNamesWithTopLevelCallables(): Set = emptySet() override fun mayHaveTopLevelClassifier(classId: ClassId, mayHaveFunctionClass: Boolean): Boolean = false override fun mayHaveTopLevelCallable(packageFqName: FqName, name: Name): Boolean = false } @@ -106,6 +120,10 @@ private constructor( return caches.flatMapToNullableSet { it.getTopLevelClassifierNamesInPackage(packageFqName) } } + override fun getPackageNamesWithTopLevelCallables(): Set? { + return caches.flatMapToNullableSet { it.getPackageNamesWithTopLevelCallables() } + } + override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set? { return caches.flatMapToNullableSet { it.getTopLevelCallableNamesInPackage(packageFqName) } } @@ -127,4 +145,4 @@ private constructor( } } } -} \ No newline at end of file +}