diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinCompositeProviderFactory.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinCompositeProviderFactory.kt new file mode 100644 index 00000000000..4e204e81bf0 --- /dev/null +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinCompositeProviderFactory.kt @@ -0,0 +1,58 @@ +/* + * 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.impl.util.mergeOnly + +/** + * A composite provider should only contain providers of the same base type as the composite provider itself, so implementations of + * [KotlinCompositeProvider] should always be a subtype of their type argument [P]. + */ +public interface KotlinCompositeProvider

{ + public val providers: List

+} + +/** + * [KotlinCompositeProviderFactory] is used by various composite providers to share code related to provider creation and flattening. + */ +public class KotlinCompositeProviderFactory

( + private val emptyProvider: P, + private val composeProviders: (List

) -> P, +) { + public fun create(providers: List

): P = when (providers.size) { + 0 -> emptyProvider + 1 -> providers.single() + else -> composeProviders(providers) + } + + public fun createFlattened(providers: List

): P = + create(if (providers.size > 1) flatten(providers) else providers) + + public fun flatten(providers: List

): List

= + providers.flatMap { provider -> + // `KotlinCompositeProvider

` should always be a provider of type `P` itself, so the cast is legal. Still, suppressing the + // error is unfortunate, but to properly type this, we'd need to be able to restrict `KotlinCompositeProvider

` to be a + // subtype of `P`, which is not possible in Kotlin. + @Suppress("UNCHECKED_CAST") + when (provider) { + is KotlinCompositeProvider<*> -> (provider as KotlinCompositeProvider

).providers + else -> listOf(provider) + } + } +} + +/** + * Uses the given [factory] to merge all providers of type [T] with the given [mergeTargets] strategy. Other providers (not of type [T]) are + * added to the resulting composite provider unmerged. + */ +public inline fun

List

.mergeSpecificProviders( + factory: KotlinCompositeProviderFactory

, + crossinline mergeTargets: (List) -> P, +): P { + // We should flatten providers before merging so that the merger has access to all individual providers, and also after merging because + // composite providers may be created by the merger. + return factory.createFlattened(factory.flatten(this).mergeOnly<_, T> { mergeTargets(it) }) +} 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 deleted file mode 100644 index a52550ed03e..00000000000 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinDeclarationProviderMergerBase.kt +++ /dev/null @@ -1,24 +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.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 8998dc638a0..dbd62eb3fe8 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 @@ -20,7 +20,6 @@ import com.intellij.psi.stubs.StubElement import com.intellij.util.indexing.FileContent import com.intellij.util.indexing.FileContentImpl import org.jetbrains.kotlin.analysis.decompiler.psi.BuiltInsVirtualFileProvider -import java.util.concurrent.ConcurrentHashMap import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInFileType import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache @@ -28,8 +27,9 @@ 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.KotlinDeclarationProviderMerger import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider -import org.jetbrains.kotlin.analysis.providers.impl.util.mergeOnly +import org.jetbrains.kotlin.analysis.providers.impl.declarationProviders.CompositeKotlinDeclarationProvider import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.name.* @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.impl.* import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol +import java.util.concurrent.ConcurrentHashMap public class KotlinStaticDeclarationProvider internal constructor( private val index: KotlinStaticDeclarationIndex, @@ -373,9 +374,9 @@ public class KotlinFakeClsStubsCache { } } -public class KotlinStaticDeclarationProviderMerger(private val project: Project) : KotlinDeclarationProviderMergerBase() { - override fun mergeToList(declarationProviders: List): List = - declarationProviders.mergeOnly<_, KotlinStaticDeclarationProvider> { providers -> +public class KotlinStaticDeclarationProviderMerger(private val project: Project) : KotlinDeclarationProviderMerger() { + override fun mergeDeclarationProviders(declarationProviders: List): KotlinDeclarationProvider = + declarationProviders.mergeSpecificProviders<_, KotlinStaticDeclarationProvider>(CompositeKotlinDeclarationProvider.factory) { providers -> val combinedScope = GlobalSearchScope.union(providers.map { it.scope }) project.createDeclarationProvider(combinedScope, module = null).apply { check(this is KotlinStaticDeclarationProvider) { 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 298b319b79a..8472763b370 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 @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.analysis.providers.impl.declarationProviders import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider +import org.jetbrains.kotlin.analysis.providers.impl.KotlinCompositeProvider +import org.jetbrains.kotlin.analysis.providers.impl.KotlinCompositeProviderFactory import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName @@ -13,8 +15,8 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* public class CompositeKotlinDeclarationProvider private constructor( - public val providers: List -) : KotlinDeclarationProvider() { + override val providers: List +) : KotlinDeclarationProvider(), KotlinCompositeProvider { override fun getClassLikeDeclarationByClassId(classId: ClassId): KtClassLikeDeclaration? { return providers.firstNotNullOfOrNull { it.getClassLikeDeclarationByClassId(classId) } } @@ -68,23 +70,11 @@ public class CompositeKotlinDeclarationProvider private constructor( } public companion object { - public fun create(providers: List): KotlinDeclarationProvider { - return when (providers.size) { - 0 -> EmptyKotlinDeclarationProvider - 1 -> providers.single() - else -> CompositeKotlinDeclarationProvider(providers) - } - } + public val factory: KotlinCompositeProviderFactory = KotlinCompositeProviderFactory( + EmptyKotlinDeclarationProvider, + ::CompositeKotlinDeclarationProvider, + ) - 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) - } - } + public fun create(providers: List): KotlinDeclarationProvider = factory.create(providers) } }