[AA] KT-58580 Add KotlinDeclarationProviderMerger

- A proper merging strategy for declaration providers is required for
  cases where the main declaration provider created by
  `createDeclarationProvider` can't provide all declarations that the
  original declaration providers can provide. Then, only a sublist of
  the declaration providers should be merged, while keeping the
  unmergeable declaration providers intact.

^KT-58580 fixed
This commit is contained in:
Marco Pennekamp
2023-05-17 15:37:27 +02:00
committed by Space Team
parent fba0648005
commit 915c412929
12 changed files with 105 additions and 21 deletions
@@ -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)
}
/**
* 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>): 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>): KotlinDeclarationProvider =
KotlinDeclarationProviderMerger.getInstance(this).mergeDeclarationProviders(declarationProviders)
@@ -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>): 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<KotlinDeclarationProvider>): List<KotlinDeclarationProvider>
}
@@ -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<KotlinDeclarationProvider>): List<KotlinDeclarationProvider> =
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."
}
}
}
}
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
public class CompositeKotlinDeclarationProvider private constructor(
private val providers: List<KotlinDeclarationProvider>
public val providers: List<KotlinDeclarationProvider>
) : 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>): KotlinDeclarationProvider =
create(if (providers.size > 1) flatten(providers) else providers)
public fun flatten(providers: List<KotlinDeclarationProvider>): List<KotlinDeclarationProvider> =
providers.flatMap { provider ->
when (provider) {
is CompositeKotlinDeclarationProvider -> provider.providers
else -> listOf(provider)
}
}
}
}