From 96b7abc6cfb9c7194ae46c53ead2dae5d7b7fa6c Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Wed, 26 Apr 2023 00:19:01 +0200 Subject: [PATCH] [Analysis API] add API to get KtScope for resolve extensions ^KT-58262 --- .../api/descriptors/KtFe10AnalysisSession.kt | 1 + ...KtFirSymbolFromResolveExtensionProvider.kt | 25 ++++ .../analysis/api/fir/KtFirAnalysisSession.kt | 7 +- .../analysis/api/fir/KtSymbolByFirBuilder.kt | 5 +- ...KtFirSymbolFromResolveExtensionProvider.kt | 90 ++++++++++++++ .../kotlin/analysis/api/KtAnalysisSession.kt | 6 +- .../KtSymbolFromResolveExtensionProvider.kt | 25 ++++ .../extensions/LLFirResolveExtensionTool.kt | 111 +++++++++++++----- 8 files changed, 234 insertions(+), 36 deletions(-) create mode 100644 analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFirSymbolFromResolveExtensionProvider.kt create mode 100644 analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolFromResolveExtensionProvider.kt create mode 100644 analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolFromResolveExtensionProvider.kt diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10AnalysisSession.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10AnalysisSession.kt index be9400c5b56..d0a15796745 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10AnalysisSession.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/KtFe10AnalysisSession.kt @@ -65,6 +65,7 @@ class KtFe10AnalysisSession( override val scopeSubstitutionImpl: KtScopeSubstitution = KtFe10ScopeSubstitution(this) override val substitutorFactoryImpl: KtSubstitutorFactory = KtFe10SubstitutorFactory(this) override val symbolProviderByJavaPsiImpl: KtSymbolProviderByJavaPsi = KtFe10SymbolProviderByJavaPsi(this) + override val resolveExtensionProviderImpl: KtSymbolFromResolveExtensionProvider = KtFe10SymbolFromResolveExtensionProvider(this) override fun createContextDependentCopy(originalKtFile: KtFile, elementToReanalyze: KtElement): KtAnalysisSession = withValidityAssertion { diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFirSymbolFromResolveExtensionProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFirSymbolFromResolveExtensionProvider.kt new file mode 100644 index 00000000000..97d103f5b1d --- /dev/null +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KtFirSymbolFromResolveExtensionProvider.kt @@ -0,0 +1,25 @@ +/* + * 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.api.descriptors.components + +import org.jetbrains.kotlin.analysis.api.components.KtSymbolFromResolveExtensionProvider +import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession +import org.jetbrains.kotlin.analysis.api.descriptors.components.base.Fe10KtAnalysisSessionComponent +import org.jetbrains.kotlin.analysis.api.impl.base.scopes.KtEmptyScope +import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken +import org.jetbrains.kotlin.analysis.api.scopes.KtScope + +internal class KtFe10SymbolFromResolveExtensionProvider( + override val analysisSession: KtFe10AnalysisSession, +) : KtSymbolFromResolveExtensionProvider(), Fe10KtAnalysisSessionComponent { + override val token: KtLifetimeToken + get() = analysisSession.token + + override fun getResolveExtensionScopeWithTopLevelDeclarations(): KtScope { + // Not supported for FE1.0 + return KtEmptyScope(token) + } +} \ No newline at end of file diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSession.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSession.kt index 43287f68280..54bc736b123 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSession.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSession.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LowLevelFirApiFacadeForResolveOnAir import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.CompositeKotlinDeclarationProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.CompositeKotlinPackageProvider +import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.LLFirResolveExtensionTool import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.llResolveExtensionTool import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.allDirectDependencies @@ -123,6 +124,8 @@ private constructor( override val symbolProviderByJavaPsiImpl = KtFirSymbolProviderByJavaPsi(this) + override val resolveExtensionProviderImpl: KtSymbolFromResolveExtensionProvider = KtFirSymbolFromResolveExtensionProvider(this) + @Suppress("AnalysisApiMissingLifetimeCheck") override fun createContextDependentCopy(originalKtFile: KtFile, elementToReanalyze: KtElement): KtAnalysisSession { check(mode == AnalysisSessionMode.REGULAR) { @@ -150,11 +153,13 @@ private constructor( val useSiteAnalysisScope: GlobalSearchScope = analysisScopeProviderImpl.getAnalysisScope() + val extensionTools: List val useSiteScopeDeclarationProvider: KotlinDeclarationProvider val useSitePackageProvider: KotlinPackageProvider + init { - val extensionTools = buildList { + extensionTools = buildList { addIfNotNull(useSiteSession.llResolveExtensionTool) useSiteModule.allDirectDependencies().mapNotNullTo(this) { dependency -> firResolveSession.getSessionFor(dependency).llResolveExtensionTool diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt index 5f1ea33b2cd..41545510e3a 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.analysis.api.fir import com.intellij.openapi.project.Project -import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.analysis.api.KtStarTypeProjection import org.jetbrains.kotlin.analysis.api.KtTypeArgumentWithVariance @@ -25,7 +24,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.util.errorWithFirSpecific import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withConeTypeEntry import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirEntry import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirSymbolEntry -import org.jetbrains.kotlin.analysis.providers.createPackageProvider +import org.jetbrains.kotlin.analysis.providers.KotlinPackageProvider import org.jetbrains.kotlin.analysis.utils.errors.buildErrorWithAttachment import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession @@ -106,7 +105,7 @@ internal class KtSymbolByFirBuilder constructor( fun buildScriptSymbol(firSymbol: FirScriptSymbol) = KtFirScriptSymbol(firSymbol, analysisSession) - private val packageProvider = project.createPackageProvider(GlobalSearchScope.allScope(project))//todo scope + private val packageProvider: KotlinPackageProvider get() = analysisSession.useSitePackageProvider fun createPackageSymbolIfOneExists(packageFqName: FqName): KtFirPackageSymbol? { val exists = packageProvider.doesPackageExist(packageFqName, analysisSession.targetPlatform) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolFromResolveExtensionProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolFromResolveExtensionProvider.kt new file mode 100644 index 00000000000..a1a5845c176 --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolFromResolveExtensionProvider.kt @@ -0,0 +1,90 @@ +/* + * 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.api.fir.components + +import org.jetbrains.kotlin.analysis.api.components.KtSymbolFromResolveExtensionProvider +import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSession +import org.jetbrains.kotlin.analysis.api.impl.base.scopes.KtEmptyScope +import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken +import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion +import org.jetbrains.kotlin.analysis.api.scopes.KtScope +import org.jetbrains.kotlin.analysis.api.scopes.KtScopeNameFilter +import org.jetbrains.kotlin.analysis.api.symbols.* +import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.LLFirResolveExtensionTool +import org.jetbrains.kotlin.analysis.low.level.api.fir.resolve.extensions.LLFirResolveExtensionToolDeclarationProvider +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedDeclaration + +internal class KtFirSymbolFromResolveExtensionProvider( + override val analysisSession: KtFirAnalysisSession, +) : KtSymbolFromResolveExtensionProvider(), KtFirAnalysisSessionComponent { + override val token: KtLifetimeToken + get() = analysisSession.token + + override fun getResolveExtensionScopeWithTopLevelDeclarations(): KtScope { + val tools = analysisSession.extensionTools + if (tools.isEmpty()) return KtEmptyScope(token) + return KtFirResolveExtensionScope(analysisSession, tools) + } +} + +private class KtFirResolveExtensionScope( + private val analysisSession: KtFirAnalysisSession, + private val tools: List, +) : KtScope { + init { + require(tools.isNotEmpty()) + } + + override val token: KtLifetimeToken get() = analysisSession.token + + override fun getCallableSymbols(nameFilter: KtScopeNameFilter): Sequence = withValidityAssertion { + gelTopLevelDeclarations(nameFilter) { it.getTopLevelCallables() } + } + + override fun getClassifierSymbols(nameFilter: KtScopeNameFilter): Sequence = withValidityAssertion { + gelTopLevelDeclarations(nameFilter) { it.getTopLevelClassifiers() } + } + + private inline fun gelTopLevelDeclarations( + crossinline nameFilter: KtScopeNameFilter, + crossinline getDeclarationsByProvider: (LLFirResolveExtensionToolDeclarationProvider) -> Sequence, + ): Sequence = sequence { + for (tool in tools) { + for (declaration in getDeclarationsByProvider(tool.declarationProvider)) { + val declarationName = declaration.nameAsName ?: continue + if (!nameFilter(declarationName)) continue + with(analysisSession) { + yield(declaration.getSymbolOfType()) + } + } + } + } + + override fun getConstructors(): Sequence = withValidityAssertion { + emptySequence() + } + + override fun getPackageSymbols(nameFilter: KtScopeNameFilter): Sequence = withValidityAssertion { + sequence { + for (tool in tools) { + for (packageName in tool.packageFilter.getAllPackages()) { + if (!nameFilter(packageName.shortName())) continue + analysisSession.firSymbolBuilder.createPackageSymbol(packageName) + } + } + } + } + + override fun getPossibleCallableNames(): Set = withValidityAssertion { + tools.flatMapTo(mutableSetOf()) { it.declarationProvider.getTopLevelCallableNames() } + } + + override fun getPossibleClassifierNames(): Set = withValidityAssertion { + tools.flatMapTo(mutableSetOf()) { it.declarationProvider.getTopLevelClassifierNames() } + } +} \ No newline at end of file diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/KtAnalysisSession.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/KtAnalysisSession.kt index 420ad68fa48..b365c8eb585 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/KtAnalysisSession.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/KtAnalysisSession.kt @@ -60,7 +60,8 @@ public abstract class KtAnalysisSession(final override val token: KtLifetimeToke KtAnalysisScopeProviderMixIn, KtSignatureSubstitutorMixIn, KtScopeSubstitutionMixIn, - KtSymbolProviderByJavaPsiMixIn { + KtSymbolProviderByJavaPsiMixIn, + KtSymbolFromResolveExtensionProviderMixIn { public abstract val useSiteModule: KtModule @@ -155,6 +156,9 @@ public abstract class KtAnalysisSession(final override val token: KtLifetimeToke internal val scopeSubstitution: KtScopeSubstitution get() = scopeSubstitutionImpl protected abstract val scopeSubstitutionImpl: KtScopeSubstitution + internal val resolveExtensionProvider: KtSymbolFromResolveExtensionProvider get() = resolveExtensionProviderImpl + protected abstract val resolveExtensionProviderImpl: KtSymbolFromResolveExtensionProvider + @KtAnalysisApiInternals public val substitutorFactory: KtSubstitutorFactory get() = substitutorFactoryImpl protected abstract val substitutorFactoryImpl: KtSubstitutorFactory diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolFromResolveExtensionProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolFromResolveExtensionProvider.kt new file mode 100644 index 00000000000..73108b072d7 --- /dev/null +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/components/KtSymbolFromResolveExtensionProvider.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2020 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.api.components + +import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion +import org.jetbrains.kotlin.analysis.api.scopes.KtScope + +public abstract class KtSymbolFromResolveExtensionProvider : KtAnalysisSessionComponent() { + abstract fun getResolveExtensionScopeWithTopLevelDeclarations(): KtScope +} + +public interface KtSymbolFromResolveExtensionProviderMixIn : KtAnalysisSessionMixIn { + /** + * Returns [KtScope] which contains all top-level callable declarations which are generated by [org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension] + * + * @see org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtension + * @see org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider + */ + fun getResolveExtensionScopeWithTopLevelDeclarations(): KtScope = withValidityAssertion { + analysisSession.resolveExtensionProvider.getResolveExtensionScopeWithTopLevelDeclarations() + } +} diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt index 562fa4bd674..cdf839b72cf 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/resolve/extensions/LLFirResolveExtensionTool.kt @@ -36,8 +36,9 @@ import java.util.concurrent.ConcurrentHashMap */ abstract class LLFirResolveExtensionTool : FirSessionComponent { abstract val modificationTrackers: List - abstract val declarationProvider: KotlinDeclarationProvider + abstract val declarationProvider: LLFirResolveExtensionToolDeclarationProvider abstract val packageProvider: KotlinPackageProvider + abstract val packageFilter: LLFirResolveExtensionToolPackageFilter internal abstract val symbolNameCache: LLFirSymbolProviderNameCache } @@ -53,11 +54,11 @@ internal class LLFirNonEmptyResolveExtensionTool( private val fileProvider = LLFirResolveExtensionsFileProvider(extensions) - private val packageFilter = LLFirResolveExtensionToolPackageFilter(extensions) + override val packageFilter = LLFirResolveExtensionToolPackageFilter(extensions) override val modificationTrackers by lazy { extensions.map { it.getModificationTracker() } } - override val declarationProvider: KotlinDeclarationProvider = + override val declarationProvider: LLFirResolveExtensionToolDeclarationProvider = LLFirResolveExtensionToolDeclarationProvider(fileProvider, session.ktModule) override val packageProvider: KotlinPackageProvider = LLFirResolveExtensionToolPackageProvider(packageFilter) @@ -97,21 +98,47 @@ private class LLFirResolveExtensionToolNameCache( } } -private class LLFirResolveExtensionToolPackageFilter( +class LLFirResolveExtensionToolPackageFilter( private val extensions: List ) { - val allPackages: Set by lazy { - forbidAnalysis { + private val packageSubPackages: Map> by lazy { + val packagesFromExtensions = forbidAnalysis { extensions.flatMapTo(mutableSetOf()) { it.getContainedPackages() } } + createSubPackagesMapping(packagesFromExtensions) + } + + fun getAllPackages(): Set { + return packageSubPackages.keys + } + + fun getAllSubPackages(packageFqName: FqName): Set { + return packageSubPackages[packageFqName].orEmpty() } fun packageExists(packageFqName: FqName): Boolean { - return packageFqName in allPackages + return packageFqName in packageSubPackages + } + + private fun createSubPackagesMapping(packages: Set): Map> { + return buildMap> { + for (packageName in packages) { + collectAllSubPackages(packageName) + } + } + } + + private fun MutableMap>.collectAllSubPackages(packageName: FqName) { + var currentPackage = FqName.ROOT + for (packagePart in packageName.pathSegments()) { + getOrPut(currentPackage) { mutableSetOf() }.add(packagePart) + currentPackage = currentPackage.child(packagePart) + } + putIfAbsent(currentPackage, mutableSetOf()) } } -private class LLFirResolveExtensionToolDeclarationProvider( +class LLFirResolveExtensionToolDeclarationProvider internal constructor( private val extensionProvider: LLFirResolveExtensionsFileProvider, private val ktModule: KtModule, ) : KotlinDeclarationProvider() { @@ -119,6 +146,30 @@ private class LLFirResolveExtensionToolDeclarationProvider( private val extensionFileToDeclarationProvider: ConcurrentHashMap = ConcurrentHashMap() + fun getTopLevelCallables(): Sequence = sequence { + forEachDeclarationOfType { callable -> + yield(callable) + } + } + + fun getTopLevelClassifiers(): Sequence = sequence { + forEachDeclarationOfType { classLike -> + yield(classLike) + } + } + + fun getTopLevelCallableNames(): Sequence = sequence { + forEachDeclarationOfType { callable -> + callable.nameAsName?.let { yield(it) } + } + } + + fun getTopLevelClassifierNames(): Sequence = sequence { + forEachDeclarationOfType { classLike -> + classLike.nameAsName?.let { yield(it) } + } + } + override fun getClassLikeDeclarationByClassId(classId: ClassId): KtClassLikeDeclaration? = forbidAnalysis { return getDeclarationProvidersByPackage(classId.packageFqName) { it.mayHaveTopLevelClassifier(classId.getTopLevelShortClassName()) } .firstNotNullOfOrNull { it.getClassLikeDeclarationByClassId(classId) } @@ -224,9 +275,22 @@ private class LLFirResolveExtensionToolDeclarationProvider( virtualFile.psiTargetsProvider = psiTargetsProvider return ktFile } + + + private inline fun forEachDeclarationOfType(action: (D) -> Unit) { + for (file in extensionProvider.getAllFiles()) { + val provider = createDeclarationProviderByFile(file) + val ktFile = provider.kotlinFile + for (declaration in ktFile.declarations) { + if (declaration is D) { + action(declaration) + } + } + } + } } -private class LLFirResolveExtensionsFileProvider( +internal class LLFirResolveExtensionsFileProvider( val extensions: List, ) { fun getFilesByPackage(packageFqName: FqName): Sequence = forbidAnalysis { @@ -236,16 +300,17 @@ private class LLFirResolveExtensionsFileProvider( .flatMap { it.getKtFiles() } .filter { it.getFilePackageName() == packageFqName } } + + fun getAllFiles(): Sequence { + return extensions + .asSequence() + .flatMap { it.getKtFiles() } + } } private class LLFirResolveExtensionToolPackageProvider( private val packageFilter: LLFirResolveExtensionToolPackageFilter, ) : KotlinPackageProvider() { - - private val packageSubPackages: Map> by lazy { - createSubPackagesMapping(packageFilter.allPackages) - } - override fun doesPackageExist(packageFqName: FqName, platform: TargetPlatform): Boolean = doesKotlinOnlyPackageExist(packageFqName) @@ -261,26 +326,10 @@ private class LLFirResolveExtensionToolPackageProvider( packageFilter.packageExists(packageFqName) override fun getKotlinOnlySubPackagesFqNames(packageFqName: FqName, nameFilter: (Name) -> Boolean): Set { - val subPackageNames = packageSubPackages[packageFqName] ?: return emptySet() + val subPackageNames = packageFilter.getAllSubPackages(packageFqName) if (subPackageNames.isEmpty()) return emptySet() return subPackageNames.filterTo(mutableSetOf()) { nameFilter(it) } } - - private fun createSubPackagesMapping(packages: Set): Map> { - return buildMap> { - for (packageName in packages) { - collectAllSubPackages(packageName) - } - } - } - - private fun MutableMap>.collectAllSubPackages(packageName: FqName) { - var currentPackage = FqName.ROOT - for (packagePart in packageName.pathSegments()) { - getOrPut(currentPackage) { mutableSetOf() }.add(packagePart) - currentPackage = currentPackage.child(packagePart) - } - } } private fun ClassId.getTopLevelShortClassName(): Name {