From c90d094af6572afb5cea6b5b71e1bd2649c2723c Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Tue, 18 Apr 2023 17:19:14 +0900 Subject: [PATCH] [LL API] Pass a contextual module to ProjectStructureProvider In certain cases, it's impossible to determine which module owns a particular file without knowing the analysis context. For instance, the file might be a part of a physical module, and be also included into a virtual ad-hoc module (to be analyzed in separate, e.g. a VCS diff). The new API allows to pass a contextual module. Basically it means "give me a module for this element, implying that we are now analyzing a contextual module". ^KT-57559 Fixed --- .../structure/KtStaticModuleProvider.kt | 11 ++++---- .../structure/impl/KtModuleProviderImpl.kt | 2 +- .../StandaloneModeTestServiceRegistrar.kt | 2 +- .../structure/ProjectStructureProvider.kt | 26 ++++++++++++++++--- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt index da7e39924f8..485a44a9d8b 100644 --- a/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt +++ b/analysis/analysis-api-standalone/analysis-api-standalone-base/src/org/jetbrains/kotlin/analysis/api/standalone/base/project/structure/KtStaticModuleProvider.kt @@ -12,11 +12,13 @@ import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerial class KtStaticModuleProvider( private val builtinsModule: KtBuiltinsModule, - val projectStructure: KtModuleProjectStructure, + private val projectStructure: KtModuleProjectStructure, ) : ProjectStructureProvider() { + val allModules: List + get() = projectStructure.allKtModules() @OptIn(KtModuleStructureInternals::class) - override fun getKtModuleForKtElement(element: PsiElement): KtModule { + override fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule { val containingFileAsPsiFile = element.containingFile val containingFileAsVirtualFile = containingFileAsPsiFile.virtualFile if (containingFileAsVirtualFile.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) { @@ -30,8 +32,7 @@ class KtStaticModuleProvider( }?.let { return it } return projectStructure.mainModules - .first { module -> - element in module.ktModule.contentScope - }.ktModule + .first { module -> element in module.ktModule.contentScope } + .ktModule } } \ No newline at end of file diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt index a530b258fd0..8da4e57a825 100644 --- a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleProviderImpl.kt @@ -36,7 +36,7 @@ internal class KtModuleProviderImpl( LLFirBuiltinsSessionFactory.getInstance(project).getBuiltinsSession(platform).ktModule as KtBuiltinsModule } - override fun getKtModuleForKtElement(element: PsiElement): KtModule { + override fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule { val containingFileAsPsiFile = element.containingFile ?: return ktNotUnderContentRootModuleWithoutPsiFile // If an [element] is created on the fly, e.g., via [KtPsiFactory], diff --git a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt index 9c0df62efa3..1149bc6e031 100644 --- a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt +++ b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/fir/test/StandaloneModeTestServiceRegistrar.kt @@ -31,7 +31,7 @@ public object StandaloneModeTestServiceRegistrar : AnalysisApiTestServiceRegistr override fun registerProjectModelServices(project: MockProject, testServices: TestServices) { val projectStructureProvider = project.getService(ProjectStructureProvider::class.java) val binaryModules = - (projectStructureProvider as? KtStaticModuleProvider)?.projectStructure?.allKtModules()?.filterIsInstance() + (projectStructureProvider as? KtStaticModuleProvider)?.allModules?.filterIsInstance() ?: emptyList() val projectEnvironment = testServices.environmentManager.getProjectEnvironment() project.apply { diff --git a/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt index f7b52108a50..9ae66bd4e1b 100644 --- a/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt +++ b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/ProjectStructureProvider.kt @@ -10,9 +10,29 @@ import com.intellij.psi.PsiElement public abstract class ProjectStructureProvider { /** - * For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs. + * Returns a [KtModule] for a given [element] in context of the [contextualModule]. + * + * Normally, every Kotlin source file either belongs to some module (e.g. a source module, or a library module), or is self-contained + * (a script file, or a file outside content roots). However, in certain cases there might be special modules that include both + * existing source files, and also some additional files. + * + * An example of such a module is one that owns an 'outsider' source file. Outsiders are used in IntelliJ for displaying files that + * technically belong to some module, but are not included in the module's content roots (e.g. a file from previous VCS revision). + * As there might be cross-references between the outsider file and other files in the module, they need to be analyzed as a single + * synthetic module. Inside an analysis session for such a module (that would become there a 'contextualModule'), + * sources that originally belong to a source module should be treated rather as a part of the synthetic one. */ - public abstract fun getKtModuleForKtElement(element: PsiElement): KtModule + public abstract fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule + + public companion object { + public fun getInstance(project: Project): ProjectStructureProvider { + return project.getService(ProjectStructureProvider::class.java) + } + + public fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule { + return getInstance(element.project).getModule(element, contextualModule) + } + } } /** @@ -21,7 +41,7 @@ public abstract class ProjectStructureProvider { */ public fun PsiElement.getKtModule(project: Project = this.project): KtModule = project.getService(ProjectStructureProvider::class.java) - .getKtModuleForKtElement(this) + .getModule(this, null) /** * For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.