[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
This commit is contained in:
Yan Zhulanow
2023-04-18 17:19:14 +09:00
committed by Space Team
parent 5e04c0d4f7
commit c90d094af6
4 changed files with 31 additions and 10 deletions
@@ -12,11 +12,13 @@ import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerial
class KtStaticModuleProvider( class KtStaticModuleProvider(
private val builtinsModule: KtBuiltinsModule, private val builtinsModule: KtBuiltinsModule,
val projectStructure: KtModuleProjectStructure, private val projectStructure: KtModuleProjectStructure,
) : ProjectStructureProvider() { ) : ProjectStructureProvider() {
val allModules: List<KtModule>
get() = projectStructure.allKtModules()
@OptIn(KtModuleStructureInternals::class) @OptIn(KtModuleStructureInternals::class)
override fun getKtModuleForKtElement(element: PsiElement): KtModule { override fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule {
val containingFileAsPsiFile = element.containingFile val containingFileAsPsiFile = element.containingFile
val containingFileAsVirtualFile = containingFileAsPsiFile.virtualFile val containingFileAsVirtualFile = containingFileAsPsiFile.virtualFile
if (containingFileAsVirtualFile.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) { if (containingFileAsVirtualFile.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) {
@@ -30,8 +32,7 @@ class KtStaticModuleProvider(
}?.let { return it } }?.let { return it }
return projectStructure.mainModules return projectStructure.mainModules
.first { module -> .first { module -> element in module.ktModule.contentScope }
element in module.ktModule.contentScope .ktModule
}.ktModule
} }
} }
@@ -36,7 +36,7 @@ internal class KtModuleProviderImpl(
LLFirBuiltinsSessionFactory.getInstance(project).getBuiltinsSession(platform).ktModule as KtBuiltinsModule 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 val containingFileAsPsiFile = element.containingFile
?: return ktNotUnderContentRootModuleWithoutPsiFile ?: return ktNotUnderContentRootModuleWithoutPsiFile
// If an [element] is created on the fly, e.g., via [KtPsiFactory], // If an [element] is created on the fly, e.g., via [KtPsiFactory],
@@ -31,7 +31,7 @@ public object StandaloneModeTestServiceRegistrar : AnalysisApiTestServiceRegistr
override fun registerProjectModelServices(project: MockProject, testServices: TestServices) { override fun registerProjectModelServices(project: MockProject, testServices: TestServices) {
val projectStructureProvider = project.getService(ProjectStructureProvider::class.java) val projectStructureProvider = project.getService(ProjectStructureProvider::class.java)
val binaryModules = val binaryModules =
(projectStructureProvider as? KtStaticModuleProvider)?.projectStructure?.allKtModules()?.filterIsInstance<KtBinaryModule>() (projectStructureProvider as? KtStaticModuleProvider)?.allModules?.filterIsInstance<KtBinaryModule>()
?: emptyList() ?: emptyList()
val projectEnvironment = testServices.environmentManager.getProjectEnvironment() val projectEnvironment = testServices.environmentManager.getProjectEnvironment()
project.apply { project.apply {
@@ -10,9 +10,29 @@ import com.intellij.psi.PsiElement
public abstract class ProjectStructureProvider { 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 = public fun PsiElement.getKtModule(project: Project = this.project): KtModule =
project.getService(ProjectStructureProvider::class.java) project.getService(ProjectStructureProvider::class.java)
.getKtModuleForKtElement(this) .getModule(this, null)
/** /**
* For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs. * For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.