diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt index 5ea32fc61c5..06130480e31 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/configurators/AnalysisApiBaseTestServiceRegistrar.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.analysis.api.lifetime.KtReadActionConfinementDefault import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory import org.jetbrains.kotlin.analysis.decompiler.psi.BuiltInDefinitionFile -import org.jetbrains.kotlin.analysis.project.structure.KtBuiltinsModule import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl import org.jetbrains.kotlin.analysis.providers.* @@ -69,6 +68,7 @@ object AnalysisApiBaseTestServiceRegistrar: AnalysisApiTestServiceRegistrar() { } finally { BuiltInDefinitionFile.FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES = filter } + registerService(KotlinDeclarationProviderMerger::class.java, KotlinStaticDeclarationProviderMerger(project)) registerService(KotlinPackageProviderFactory::class.java, KotlinStaticPackageProviderFactory(project, allKtFiles)) registerService(KotlinReferenceProvidersService::class.java, HLApiReferenceProviderService::class.java) registerService(KotlinResolutionScopeProvider::class.java, KotlinByModulesResolutionScopeProvider::class.java) diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt index ce54e73d44b..e847e7ecd49 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinDeclarationProvider.kt @@ -49,9 +49,40 @@ public abstract class KotlinDeclarationProvider { public abstract class KotlinDeclarationProviderFactory { public abstract fun createDeclarationProvider(scope: GlobalSearchScope, contextualModule: KtModule?): KotlinDeclarationProvider + + public companion object { + public fun getInstance(project: Project): KotlinDeclarationProviderFactory = + project.getService(KotlinDeclarationProviderFactory::class.java) + } } -public fun Project.createDeclarationProvider(scope: GlobalSearchScope, module: KtModule?): KotlinDeclarationProvider { - return getService(KotlinDeclarationProviderFactory::class.java) - .createDeclarationProvider(scope, module) -} \ No newline at end of file +/** + * While the main declaration provider created via [createDeclarationProvider] is scope-based, there are other declaration providers which + * are not, such as file-based declaration providers. Not all declarations provided by such declaration providers can be provided by the + * main declaration provider, even if the correct scope is provided (such as a file-based scope). For example, the main declaration provider + * may be based on an index which doesn't contain the declarations provided by file-based declaration providers. + * + * Hence, [KotlinDeclarationProvider]s cannot just be combined by combining the scopes of all declaration providers and calling + * [createDeclarationProvider]. [KotlinDeclarationProviderMerger] should implement proper merging logic that takes these concerns into + * account. + */ +public abstract class KotlinDeclarationProviderMerger { + /** + * Merges [declarationProviders] if possible, creating a combined declaration provider that should be more efficient compared to calling + * separate declaration providers. Not all given declaration providers might be mergeable, or there might be multiple separate sets of + * declaration providers which can be merged individually, so the resulting declaration provider may be a composite Kotlin declaration + * provider. + */ + public abstract fun mergeDeclarationProviders(declarationProviders: List): KotlinDeclarationProvider + + public companion object { + public fun getInstance(project: Project): KotlinDeclarationProviderMerger = + project.getService(KotlinDeclarationProviderMerger::class.java) + } +} + +public fun Project.createDeclarationProvider(scope: GlobalSearchScope, module: KtModule?): KotlinDeclarationProvider = + KotlinDeclarationProviderFactory.getInstance(this).createDeclarationProvider(scope, module) + +public fun Project.mergeDeclarationProviders(declarationProviders: List): KotlinDeclarationProvider = + KotlinDeclarationProviderMerger.getInstance(this).mergeDeclarationProviders(declarationProviders) diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinDeclarationProviderMergerBase.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinDeclarationProviderMergerBase.kt new file mode 100644 index 00000000000..a52550ed03e --- /dev/null +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinDeclarationProviderMergerBase.kt @@ -0,0 +1,24 @@ +/* + * 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.providers.impl + +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderMerger +import org.jetbrains.kotlin.analysis.providers.impl.declarationProviders.CompositeKotlinDeclarationProvider + +public abstract class KotlinDeclarationProviderMergerBase : KotlinDeclarationProviderMerger() { + override fun mergeDeclarationProviders(declarationProviders: List): KotlinDeclarationProvider { + // We should flatten declaration providers before merging so that the merger has access to all declaration providers, and also after + // merging because composite declaration providers may be created by the merger. + return CompositeKotlinDeclarationProvider.createFlattened( + mergeToList( + CompositeKotlinDeclarationProvider.flatten(declarationProviders) + ) + ) + } + + protected abstract fun mergeToList(declarationProviders: List): List +} diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt index 315d07058e8..54b6872715f 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticDeclarationProvider.kt @@ -21,13 +21,14 @@ import com.intellij.util.containers.CollectionFactory import com.intellij.util.indexing.FileContent import com.intellij.util.indexing.FileContentImpl import com.intellij.util.io.URLUtil -import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinClsStubBuilder import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider +import org.jetbrains.kotlin.analysis.providers.impl.util.mergeOnly import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.name.* @@ -39,7 +40,7 @@ import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerial public class KotlinStaticDeclarationProvider internal constructor( private val index: KotlinStaticDeclarationIndex, - private val scope: GlobalSearchScope, + public val scope: GlobalSearchScope, ) : KotlinDeclarationProvider() { private val KtElement.inScope: Boolean @@ -118,7 +119,6 @@ public class KotlinStaticDeclarationProvider internal constructor( getTopLevelProperties(callableId).mapTo(this) { it.containingKtFile } getTopLevelFunctions(callableId).mapTo(this) { it.containingKtFile } } - } public class KotlinStaticDeclarationProviderFactory( @@ -368,3 +368,15 @@ public class KotlinFakeClsStubsCache { } } } + +public class KotlinStaticDeclarationProviderMerger(private val project: Project) : KotlinDeclarationProviderMergerBase() { + override fun mergeToList(declarationProviders: List): List = + declarationProviders.mergeOnly<_, KotlinStaticDeclarationProvider> { providers -> + val combinedScope = GlobalSearchScope.union(providers.map { it.scope }) + project.createDeclarationProvider(combinedScope, module = null).apply { + check(this is KotlinStaticDeclarationProvider) { + "`KotlinStaticDeclarationProvider` can only be merged into a combined declaration provider of the same type." + } + } + } +} diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/declarationProviders/CompositeKotlinDeclarationProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/declarationProviders/CompositeKotlinDeclarationProvider.kt index e43e9f3eb5f..298b319b79a 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/declarationProviders/CompositeKotlinDeclarationProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/declarationProviders/CompositeKotlinDeclarationProvider.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* public class CompositeKotlinDeclarationProvider private constructor( - private val providers: List + public val providers: List ) : KotlinDeclarationProvider() { override fun getClassLikeDeclarationByClassId(classId: ClassId): KtClassLikeDeclaration? { return providers.firstNotNullOfOrNull { it.getClassLikeDeclarationByClassId(classId) } @@ -75,5 +75,16 @@ public class CompositeKotlinDeclarationProvider private constructor( else -> CompositeKotlinDeclarationProvider(providers) } } + + public fun createFlattened(providers: List): KotlinDeclarationProvider = + create(if (providers.size > 1) flatten(providers) else providers) + + public fun flatten(providers: List): List = + providers.flatMap { provider -> + when (provider) { + is CompositeKotlinDeclarationProvider -> provider.providers + else -> listOf(provider) + } + } } } diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt index 91cff126bbd..b19cbd354c9 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneAnalysisAPISessionBuilder.kt @@ -141,6 +141,7 @@ public class StandaloneAnalysisAPISessionBuilder( kotlinCoreProjectEnvironment.environment.jarFileSystem as CoreJarFileSystem ) ) + registerService(KotlinDeclarationProviderMerger::class.java, KotlinStaticDeclarationProviderMerger(this)) registerService(KotlinPackageProviderFactory::class.java, KotlinStaticPackageProviderFactory(project, ktFiles)) registerService(KtAnalysisSessionProvider::class.java, KtFirAnalysisSessionProvider(this)) diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt index 0f0a93b9fbf..a95ffab6007 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneUtils.kt @@ -159,6 +159,10 @@ internal fun configureProjectEnvironment( KotlinDeclarationProviderFactory::class.java, KotlinStaticDeclarationProviderFactory(project, ktFiles) ) + project.registerService( + KotlinDeclarationProviderMerger::class.java, + KotlinStaticDeclarationProviderMerger(project) + ) project.registerService( KotlinPackageProviderFactory::class.java, KotlinStaticPackageProviderFactory(project, ktFiles) 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 587738f58d9..ae5085eb4dd 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 @@ -6,13 +6,11 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.providers import com.intellij.openapi.project.Project -import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.low.level.api.fir.caches.NullableCaffeineCache -import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirModuleData import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider -import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider +import org.jetbrains.kotlin.analysis.providers.mergeDeclarationProviders import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.resolve.providers.FirCompositeCachedSymbolNamesProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolNamesProvider @@ -118,8 +116,7 @@ internal class LLFirCombinedKotlinSymbolProvider private constructor( companion object { fun merge(session: LLFirSession, project: Project, providers: List): FirSymbolProvider? = if (providers.size > 1) { - val combinedScope = GlobalSearchScope.union(providers.map { it.session.llFirModuleData.ktModule.contentScope }) - val declarationProvider = project.createDeclarationProvider(combinedScope, session.ktModule) + val declarationProvider = project.mergeDeclarationProviders(providers.map { it.declarationProvider }) LLFirCombinedKotlinSymbolProvider(session, project, providers, declarationProvider) } else providers.singleOrNull() } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirKotlinSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirKotlinSymbolProvider.kt index a1564bbb51c..afe0be2c812 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirKotlinSymbolProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirKotlinSymbolProvider.kt @@ -26,6 +26,8 @@ import org.jetbrains.kotlin.psi.KtProperty * @see LLFirCombinedKotlinSymbolProvider */ internal abstract class LLFirKotlinSymbolProvider(session: FirSession) : FirSymbolProvider(session) { + abstract val declarationProvider: KotlinDeclarationProvider + /** * This function is optimized for a known [classLikeDeclaration]. */ 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 db3c8950aca..22f8a8b1918 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 @@ -109,6 +109,8 @@ internal class LLFirProvider( @NoMutableState internal inner class SymbolProvider : LLFirKotlinSymbolProvider(session) { + override val declarationProvider: KotlinDeclarationProvider get() = providerHelper.declarationProvider + override val symbolNamesProvider: FirSymbolNamesProvider get() = providerHelper.symbolNameCache override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProviderHelper.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProviderHelper.kt index 9edefb6c55b..c47e00687fa 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProviderHelper.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirProviderHelper.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.providers import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirFileBuilder -import org.jetbrains.kotlin.analysis.providers.impl.declarationProviders.CompositeKotlinDeclarationProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.CompositeKotlinPackageProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.LLFirResolveExtensionTool import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.llResolveExtensionTool @@ -14,6 +13,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirElementFinder import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirKotlinSymbolNamesProvider import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider +import org.jetbrains.kotlin.analysis.providers.impl.declarationProviders.CompositeKotlinDeclarationProvider import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.config.AnalysisFlags import org.jetbrains.kotlin.fir.FirSession @@ -43,7 +43,7 @@ internal class LLFirProviderHelper( ) { private val extensionTool: LLFirResolveExtensionTool? = firSession.llResolveExtensionTool - private val declarationProvider = CompositeKotlinDeclarationProvider.create( + val declarationProvider = CompositeKotlinDeclarationProvider.create( listOfNotNull( mainDeclarationProvider, extensionTool?.declarationProvider, 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 f4562a31a6d..8e0fc40a31b 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 @@ -5,10 +5,10 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.stubBased.deserialization -import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirKotlinSymbolProvider -import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirKotlinSymbolNamesProvider import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirKotlinSymbolProvider +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.LLFirKotlinSymbolNamesProvider import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider import org.jetbrains.kotlin.fir.FirSession @@ -19,8 +19,8 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.deserialization.SingleModuleDataProvider import org.jetbrains.kotlin.fir.java.deserialization.JvmClassFileBasedSymbolProvider import org.jetbrains.kotlin.fir.java.deserialization.KotlinBuiltins -import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolNamesProvider import org.jetbrains.kotlin.fir.realPsi +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolNamesProvider import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.symbols.impl.* @@ -52,10 +52,10 @@ internal open class JvmStubBasedFirDeserializedSymbolProvider( scope: GlobalSearchScope, private val initialOrigin: FirDeclarationOrigin ) : LLFirKotlinSymbolProvider(session) { - private val declarationProvider = project.createDeclarationProvider(scope, module = null) - private val moduleData = moduleDataProvider.getModuleData(null) + final override val declarationProvider = project.createDeclarationProvider(scope, module = null) + override val symbolNamesProvider: FirSymbolNamesProvider = LLFirKotlinSymbolNamesProvider.cached(session, declarationProvider) private val typeAliasCache: FirCache =