From a62ac940c4f73763f4131406d96c1d46be7a83ac Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Wed, 11 Oct 2023 19:02:00 +0200 Subject: [PATCH] [AA] Add abstraction for composable Kotlin providers This further improves the `KotlinCompositeProvider` abstraction: - Pulling the abstraction's interfaces outside the `impl` package allows us to write consolidated documentation on composable Kotlin providers. - The addition of `KotlinComposableProvider` allows more specific bounds for the type parameters of `KotlinCompositeProvider` and `KotlinCompositeProviderFactory`. It also clarifies to Analysis API implementors which providers can be composed at all, as providers like `KotlinDeclarationProvider` extend this interface. - `KotlinComposableProviderMerger` provides a unified interface for provider mergers. ^KT-61791 --- .../providers/KotlinComposableProvider.kt | 38 +++++++++++++++++++ .../providers/KotlinDeclarationProvider.kt | 19 +++------- .../providers/KotlinPackageProvider.kt | 12 ++---- .../impl/KotlinCompositeProviderFactory.kt | 16 +++----- .../impl/KotlinStaticDeclarationProvider.kt | 6 +-- .../impl/KotlinStaticPackageProvider.kt | 6 +-- .../CompositeKotlinDeclarationProvider.kt | 2 +- .../CompositeKotlinPackageProvider.kt | 2 +- 8 files changed, 60 insertions(+), 41 deletions(-) create mode 100644 analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinComposableProvider.kt diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinComposableProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinComposableProvider.kt new file mode 100644 index 00000000000..968b34f52c6 --- /dev/null +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinComposableProvider.kt @@ -0,0 +1,38 @@ +/* + * 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 + +/** + * A marker interface for a provider that can be composed, i.e. multiple instances of the same provider can be composed into a single + * provider. + * + * Composable providers share certain traits: There is a notion of a sequentially composed [KotlinCompositeProvider] of that kind, and there + * is usually a merge function which allows to create a single provider from a list of providers. Mergers of composable providers may + * produce a merged provider which is more efficient than the naive sequential composite provider. + * + * @see KotlinDeclarationProvider + * @see KotlinPackageProvider + */ +public interface KotlinComposableProvider + +/** + * A [KotlinCompositeProvider] is the sequential composition of a specific kind of composable provider [P]. + * + * 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]. (This is not enforceable in the Kotlin type system.) + */ +public interface KotlinCompositeProvider

: KotlinComposableProvider { + public val providers: List

+} + +public interface KotlinComposableProviderMerger

{ + /** + * Merges the given [providers] into a single provider. When possible, mergers will try to create a provider that is more efficient + * compared to the naive sequential composite provider. Not all providers might be mergeable, or there might be multiple separate sets + * of providers that can be merged individually, so the resulting provider may be a composite provider. + */ + public fun merge(providers: List

): P +} 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 e847e7ecd49..8fd660dc5f8 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 @@ -15,10 +15,11 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* /** - * A declaration provider for a given scope. Can be created via [KotlinDeclarationProviderFactory]. - * May be called frequently, so for implementations it is better to cache results. + * A declaration provider for a given scope. It can be created via [KotlinDeclarationProviderFactory]. + * + * It may be called frequently, so implementations should cache the results. */ -public abstract class KotlinDeclarationProvider { +public abstract class KotlinDeclarationProvider : KotlinComposableProvider { public abstract fun getClassLikeDeclarationByClassId(classId: ClassId): KtClassLikeDeclaration? public abstract fun getAllClassesByClassId(classId: ClassId): Collection @@ -66,15 +67,7 @@ public abstract class KotlinDeclarationProviderFactory { * [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 abstract class KotlinDeclarationProviderMerger : KotlinComposableProviderMerger { public companion object { public fun getInstance(project: Project): KotlinDeclarationProviderMerger = project.getService(KotlinDeclarationProviderMerger::class.java) @@ -85,4 +78,4 @@ public fun Project.createDeclarationProvider(scope: GlobalSearchScope, module: K KotlinDeclarationProviderFactory.getInstance(this).createDeclarationProvider(scope, module) public fun Project.mergeDeclarationProviders(declarationProviders: List): KotlinDeclarationProvider = - KotlinDeclarationProviderMerger.getInstance(this).mergeDeclarationProviders(declarationProviders) + KotlinDeclarationProviderMerger.getInstance(this).merge(declarationProviders) diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinPackageProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinPackageProvider.kt index 61c46d00cce..63f8766db9e 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinPackageProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/KotlinPackageProvider.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.platform.TargetPlatform * Provides information about packages that are visible to Kotlin in the given scope. Can be constructed via [KotlinPackageProviderFactory]. * The FIR compiler calls [doesKotlinOnlyPackageExist] very often, so the implementations should consider caching the results. */ -public abstract class KotlinPackageProvider { +public abstract class KotlinPackageProvider : KotlinComposableProvider { /** * Checks if a package with given [FqName] exists in current [GlobalSearchScope] with a view from a given [platform]. * @@ -82,13 +82,7 @@ public abstract class KotlinPackageProviderFactory { * Package providers should not be naively merged by combining scopes and calling [createPackageProvider], because there may be additional * package providers which do not operate based on scopes (e.g. resolve extension package providers). */ -public abstract class KotlinPackageProviderMerger { - /** - * Merges [packageProviders] if possible, creating a combined package provider that should be more efficient compared to invoking - * separate package providers. - */ - public abstract fun mergePackageProviders(packageProviders: List): KotlinPackageProvider - +public abstract class KotlinPackageProviderMerger : KotlinComposableProviderMerger { public companion object { public fun getInstance(project: Project): KotlinPackageProviderMerger = project.getService(KotlinPackageProviderMerger::class.java) } @@ -99,4 +93,4 @@ public fun Project.createPackageProvider(searchScope: GlobalSearchScope): Kotlin .createPackageProvider(searchScope) public fun Project.mergePackageProviders(packageProviders: List): KotlinPackageProvider = - KotlinPackageProviderMerger.getInstance(this).mergePackageProviders(packageProviders) + KotlinPackageProviderMerger.getInstance(this).merge(packageProviders) 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 index 4e204e81bf0..8763135247d 100644 --- 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 @@ -5,20 +5,14 @@ package org.jetbrains.kotlin.analysis.providers.impl +import org.jetbrains.kotlin.analysis.providers.KotlinComposableProvider +import org.jetbrains.kotlin.analysis.providers.KotlinCompositeProvider 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]. + * [KotlinCompositeProviderFactory] is used by various [KotlinCompositeProvider]s to share code related to provider creation and flattening. */ -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

( +public class KotlinCompositeProviderFactory

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

) -> P, ) { @@ -48,7 +42,7 @@ public class KotlinCompositeProviderFactory

( * 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( +public inline fun

List

.mergeSpecificProviders( factory: KotlinCompositeProviderFactory

, crossinline mergeTargets: (List) -> P, ): P { 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 dbd62eb3fe8..f8efbfa2c57 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 @@ -375,9 +375,9 @@ public class KotlinFakeClsStubsCache { } 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 }) + override fun merge(providers: List): KotlinDeclarationProvider = + providers.mergeSpecificProviders<_, KotlinStaticDeclarationProvider>(CompositeKotlinDeclarationProvider.factory) { targetProviders -> + val combinedScope = GlobalSearchScope.union(targetProviders.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/KotlinStaticPackageProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticPackageProvider.kt index 275bb6abdc2..0597b772222 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticPackageProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/KotlinStaticPackageProvider.kt @@ -54,9 +54,9 @@ public class KotlinStaticPackageProviderFactory( } public class KotlinStaticPackageProviderMerger(private val project: Project) : KotlinPackageProviderMerger() { - override fun mergePackageProviders(packageProviders: List): KotlinPackageProvider = - packageProviders.mergeSpecificProviders<_, KotlinStaticPackageProvider>(CompositeKotlinPackageProvider.factory) { providers -> - val combinedScope = GlobalSearchScope.union(providers.map { it.scope }) + override fun merge(providers: List): KotlinPackageProvider = + providers.mergeSpecificProviders<_, KotlinStaticPackageProvider>(CompositeKotlinPackageProvider.factory) { targetProviders -> + val combinedScope = GlobalSearchScope.union(targetProviders.map { it.scope }) project.createPackageProvider(combinedScope).apply { check(this is KotlinStaticPackageProvider) { "`${KotlinStaticPackageProvider::class.simpleName}` can only be merged into a combined package 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 8472763b370..de023ba7773 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 @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.analysis.providers.impl.declarationProviders +import org.jetbrains.kotlin.analysis.providers.KotlinCompositeProvider 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 diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/packageProviders/CompositeKotlinPackageProvider.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/packageProviders/CompositeKotlinPackageProvider.kt index ea511ee506d..9e17fba8e8c 100644 --- a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/packageProviders/CompositeKotlinPackageProvider.kt +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/impl/packageProviders/CompositeKotlinPackageProvider.kt @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.analysis.providers.impl.packageProviders +import org.jetbrains.kotlin.analysis.providers.KotlinCompositeProvider import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider -import org.jetbrains.kotlin.analysis.providers.impl.KotlinCompositeProvider import org.jetbrains.kotlin.analysis.providers.impl.KotlinCompositeProviderFactory import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name