[AA] Add abstraction for composite provider creation and merging

- Composite declaration providers and declaration provider mergers are
  extremely similar to the composite package providers and (newly to be
  implemented) package provider mergers. This commit extracts the common
  parts into a `KotlinCompositeProviderFactory`.

^KT-61791
This commit is contained in:
Marco Pennekamp
2023-10-10 13:07:47 +02:00
committed by Space Team
parent 0e6cc92958
commit c63dde4f7e
4 changed files with 73 additions and 48 deletions
@@ -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<P> {
public val providers: List<P>
}
/**
* [KotlinCompositeProviderFactory] is used by various composite providers to share code related to provider creation and flattening.
*/
public class KotlinCompositeProviderFactory<P>(
private val emptyProvider: P,
private val composeProviders: (List<P>) -> P,
) {
public fun create(providers: List<P>): P = when (providers.size) {
0 -> emptyProvider
1 -> providers.single()
else -> composeProviders(providers)
}
public fun createFlattened(providers: List<P>): P =
create(if (providers.size > 1) flatten(providers) else providers)
public fun flatten(providers: List<P>): List<P> =
providers.flatMap { provider ->
// `KotlinCompositeProvider<P>` 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<P>` to be a
// subtype of `P`, which is not possible in Kotlin.
@Suppress("UNCHECKED_CAST")
when (provider) {
is KotlinCompositeProvider<*> -> (provider as KotlinCompositeProvider<P>).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 <P : Any, reified T : P> List<P>.mergeSpecificProviders(
factory: KotlinCompositeProviderFactory<P>,
crossinline mergeTargets: (List<T>) -> 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) })
}
@@ -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>): 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>
}
@@ -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<KotlinDeclarationProvider>): List<KotlinDeclarationProvider> =
declarationProviders.mergeOnly<_, KotlinStaticDeclarationProvider> { providers ->
public class KotlinStaticDeclarationProviderMerger(private val project: Project) : KotlinDeclarationProviderMerger() {
override fun mergeDeclarationProviders(declarationProviders: List<KotlinDeclarationProvider>): 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) {
@@ -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>
) : KotlinDeclarationProvider() {
override val providers: List<KotlinDeclarationProvider>
) : KotlinDeclarationProvider(), KotlinCompositeProvider<KotlinDeclarationProvider> {
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>): KotlinDeclarationProvider {
return when (providers.size) {
0 -> EmptyKotlinDeclarationProvider
1 -> providers.single()
else -> CompositeKotlinDeclarationProvider(providers)
}
}
public val factory: KotlinCompositeProviderFactory<KotlinDeclarationProvider> = KotlinCompositeProviderFactory(
EmptyKotlinDeclarationProvider,
::CompositeKotlinDeclarationProvider,
)
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)
}
}
public fun create(providers: List<KotlinDeclarationProvider>): KotlinDeclarationProvider = factory.create(providers)
}
}