diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirCombinedSyntheticFunctionSymbolProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirCombinedSyntheticFunctionSymbolProvider.kt new file mode 100644 index 00000000000..86891eca878 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirCombinedSyntheticFunctionSymbolProvider.kt @@ -0,0 +1,61 @@ +/* + * 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.low.level.api.fir.providers + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals +import org.jetbrains.kotlin.fir.resolve.providers.impl.FirSyntheticFunctionInterfaceProviderBase +import org.jetbrains.kotlin.fir.resolve.providers.impl.FirSyntheticFunctionInterfaceProviderBase.Companion.mayBeSyntheticFunctionClassName +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +/** + * [LLFirCombinedSyntheticFunctionSymbolProvider] combines multiple synthetic function symbol providers with the advantage that [ClassId] + * heuristics are checked only once. + */ +@OptIn(FirSymbolProviderInternals::class) +internal class LLFirCombinedSyntheticFunctionSymbolProvider( + session: FirSession, + private val providers: List, +) : FirSymbolProvider(session) { + private val combinedPackageNames: Set = providers.flatMapTo(mutableSetOf()) { it.getFunctionKindPackageNames() } + + override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { + if (!classId.mayBeSyntheticFunctionClassName()) return null + if (classId.packageFqName !in combinedPackageNames) return null + + return providers.firstNotNullOfOrNull { it.getClassLikeSymbolByClassIdWithoutClassIdChecks(classId) } + } + + @FirSymbolProviderInternals + override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { + } + + @FirSymbolProviderInternals + override fun getTopLevelFunctionSymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + } + + @FirSymbolProviderInternals + override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + } + + override fun getPackage(fqName: FqName): FqName? = fqName.takeIf { it in combinedPackageNames } + + override fun computePackageSetWithTopLevelCallables(): Set? = null + override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set? = null + override fun computeCallableNamesInPackage(packageFqName: FqName): Set? = null + + companion object { + fun merge(session: FirSession, providers: List): FirSymbolProvider = + providers.singleOrNull() ?: LLFirCombinedSyntheticFunctionSymbolProvider(session, providers) + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt index adf79a647d8..7c55736ccf7 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/sessions/LLFirSessionCache.kt @@ -38,7 +38,6 @@ import org.jetbrains.kotlin.fir.extensions.* import org.jetbrains.kotlin.fir.java.JavaSymbolProvider import org.jetbrains.kotlin.fir.resolve.providers.* import org.jetbrains.kotlin.fir.resolve.providers.impl.FirExtensionSyntheticFunctionInterfaceProvider -import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.scopes.wrapScopeWithJvmMapped import org.jetbrains.kotlin.fir.resolve.transformers.FirDummyCompilerLazyDeclarationResolver import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider @@ -49,6 +48,7 @@ import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices +import org.jetbrains.kotlin.utils.addToStdlib.partitionIsInstance import java.util.concurrent.ConcurrentMap @OptIn(PrivateSessionConstructor::class, SessionConfiguration::class) @@ -164,7 +164,13 @@ internal class LLFirSessionCache(private val project: Project) { }) val javaSymbolProvider = createJavaSymbolProvider(this, moduleData, project, contentScope) - val syntheticFunctionalInterfaceProvider = FirExtensionSyntheticFunctionInterfaceProvider(this, moduleData, scopeProvider) + + // We only need to add an extension synthetic function provider if the session's function type service even has extension kinds. + // Otherwise, the provider will be completely useless. + val syntheticFunctionalInterfaceProvider = if (this.functionTypeService.hasExtensionKinds()) { + FirExtensionSyntheticFunctionInterfaceProvider(this, moduleData, scopeProvider) + } else null + register( FirSymbolProvider::class, LLFirModuleWithDependenciesSymbolProvider( @@ -530,12 +536,19 @@ internal class LLFirSessionCache(private val project: Project) { * [session] should be the session of the dependent module. Because all symbol providers are tied to a session, we need a session to * create a combined symbol provider. */ - @Suppress("UNUSED_PARAMETER") private fun List.mergeDependencySymbolProvidersInto( session: FirSession, destination: MutableList, ) { - destination.addAll(this) + val (syntheticFunctionSymbolProviders, remainingSymbolProviders1) = + partitionIsInstance<_, FirExtensionSyntheticFunctionInterfaceProvider>() + + destination.addAll(remainingSymbolProviders1) + + // Unfortunately, the functions that an extension synthetic function symbol provider might provide differ between sessions because + // they depend on compiler plugins. However, only extension providers that are affected by compiler plugins are added in + // `createSourcesSession`. We can still combine these, because the `ClassId` heuristics only need to be checked once. + destination.add(LLFirCombinedSyntheticFunctionSymbolProvider.merge(session, syntheticFunctionSymbolProviders)) } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirExtensionSyntheticFunctionInterfaceProvider.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirExtensionSyntheticFunctionInterfaceProvider.kt index 962612db7ae..94c5782d84a 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirExtensionSyntheticFunctionInterfaceProvider.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/providers/impl/FirExtensionSyntheticFunctionInterfaceProvider.kt @@ -69,11 +69,15 @@ abstract class FirSyntheticFunctionInterfaceProviderBase( val moduleData: FirModuleData, val kotlinScopeProvider: FirKotlinScopeProvider ) : FirSymbolProvider(session) { + @OptIn(FirSymbolProviderInternals::class) override fun getClassLikeSymbolByClassId(classId: ClassId): FirRegularClassSymbol? { if (!classId.mayBeSyntheticFunctionClassName()) return null - return cache.getValue(classId) + return getClassLikeSymbolByClassIdWithoutClassIdChecks(classId) } + @FirSymbolProviderInternals + fun getClassLikeSymbolByClassIdWithoutClassIdChecks(classId: ClassId): FirRegularClassSymbol? = cache.getValue(classId) + @FirSymbolProviderInternals override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { } @@ -86,6 +90,9 @@ abstract class FirSyntheticFunctionInterfaceProviderBase( override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { } + @FirSymbolProviderInternals + fun getFunctionKindPackageNames(): Set = session.functionTypeService.getFunctionKindPackageNames() + override fun getPackage(fqName: FqName): FqName? { return fqName.takeIf { session.functionTypeService.hasKindWithSpecificPackage(it) } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirFunctionTypeKindService.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirFunctionTypeKindService.kt index cd0bee83d72..082b103120f 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirFunctionTypeKindService.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/FirFunctionTypeKindService.kt @@ -23,6 +23,16 @@ abstract class FirFunctionTypeKindService : FirSessionComponent { return extractor.hasKindWithSpecificPackage(packageFqName) } + /** + * Returns all package names for which [getKindByClassNamePrefix] may return a [FunctionTypeKind]. + */ + fun getFunctionKindPackageNames(): Set = extractor.getFunctionKindPackageNames() + + /** + * Whether [getKindByClassNamePrefix] may return a [FunctionTypeKind] added by a compiler plugin. + */ + fun hasExtensionKinds(): Boolean = extractor.hasExtensionKinds() + abstract fun extractSingleSpecialKindForFunction(functionSymbol: FirFunctionSymbol<*>): FunctionTypeKind? abstract fun extractAllSpecialKindsForFunction(functionSymbol: FirFunctionSymbol<*>): List abstract fun extractAllSpecialKindsForFunctionTypeRef(typeRef: FirFunctionTypeRef): List diff --git a/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKindExtractor.kt b/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKindExtractor.kt index a07514a5b68..e3a49605deb 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKindExtractor.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/builtins/functions/FunctionTypeKindExtractor.kt @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.name.FqName @RequiresOptIn annotation class AllowedToUsedOnlyInK1 -class FunctionTypeKindExtractor(kinds: List) { +class FunctionTypeKindExtractor(private val kinds: List) { companion object { /** * This instance should be used only in: @@ -49,6 +49,10 @@ class FunctionTypeKindExtractor(kinds: List) { return packageFqName in knownKindsByPackageFqName } + fun getFunctionKindPackageNames(): Set = knownKindsByPackageFqName.keys + + fun hasExtensionKinds(): Boolean = kinds.any { !it.isBuiltin } + data class KindWithArity(val kind: FunctionTypeKind, val arity: Int) private fun toInt(s: String): Int? {