[Analysis API] Extract dangling file module computation
Use the same code for creating dangling file modules inside tests, the stand-alone API, and in the IDE.
This commit is contained in:
+23
-23
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirBu
|
||||
import org.jetbrains.kotlin.analysis.project.structure.*
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.analysisContext
|
||||
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
||||
|
||||
@@ -40,38 +39,39 @@ internal class KtStandaloneProjectStructureProvider(
|
||||
LLFirBuiltinsSessionFactory.getInstance(project).getBuiltinsSession(platform).ktModule as KtBuiltinsModule
|
||||
}
|
||||
|
||||
override fun getNotUnderContentRootModule(project: Project, file: PsiFile?): KtNotUnderContentRootModule {
|
||||
if (file == null) {
|
||||
return ktNotUnderContentRootModuleWithoutPsiFile
|
||||
}
|
||||
|
||||
return notUnderContentRootModuleCache.getOrPut(file) {
|
||||
KtNotUnderContentRootModuleImpl(
|
||||
name = file.name,
|
||||
moduleDescription = "Standalone-not-under-content-root-module-for-$file",
|
||||
file = file,
|
||||
project = project,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KtModuleStructureInternals::class)
|
||||
override fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule {
|
||||
val containingFileAsPsiFile = element.containingFile
|
||||
val containingFile = element.containingFile
|
||||
?: return ktNotUnderContentRootModuleWithoutPsiFile
|
||||
// If an [element] is created on the fly, e.g., via [KtPsiFactory],
|
||||
// its containing [PsiFile] may not have [VirtualFile].
|
||||
// We can attempt to use an associated [analysisContext] (from [KtPsiFactory]) if any.
|
||||
// If both fail, the [element] is not bound to any [KtModule], and we bail out early
|
||||
// by returning a [KtNotUnderContentRootModule] (for that specific unbound file).
|
||||
val containingFileAsVirtualFile = containingFileAsPsiFile.virtualFile
|
||||
?: (containingFileAsPsiFile as? KtFile)?.analysisContext?.containingFile?.virtualFile
|
||||
?: return notUnderContentRootModuleCache.getOrPut(containingFileAsPsiFile) {
|
||||
KtNotUnderContentRootModuleImpl(
|
||||
name = containingFileAsPsiFile.name,
|
||||
moduleDescription = "Standalone-not-under-content-root-module-for-$containingFileAsPsiFile",
|
||||
file = containingFileAsPsiFile,
|
||||
project = project,
|
||||
)
|
||||
}
|
||||
|
||||
if (containingFileAsVirtualFile.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) {
|
||||
val virtualFile = containingFile.virtualFile
|
||||
?: error("${containingFile.name} is not a physical file")
|
||||
|
||||
if (virtualFile.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) {
|
||||
return builtinsModule
|
||||
}
|
||||
|
||||
containingFileAsVirtualFile.analysisExtensionFileContextModule?.let { return it }
|
||||
computeSpecialModule(containingFile)?.let { return it }
|
||||
|
||||
return allKtModules.firstOrNull { module ->
|
||||
containingFileAsVirtualFile in module.contentScope
|
||||
}
|
||||
return allKtModules.firstOrNull { module -> virtualFile in module.contentScope }
|
||||
?: throw KotlinExceptionWithAttachments("Cannot find KtModule; see the attachment for more details.")
|
||||
.withAttachment(
|
||||
containingFileAsVirtualFile.path,
|
||||
virtualFile.path,
|
||||
allKtModules.joinToString(separator = System.lineSeparator()) { it.asDebugString() }
|
||||
)
|
||||
}
|
||||
|
||||
+19
-20
@@ -5,14 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.analysis.test.framework.services
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiFileSystemItem
|
||||
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure
|
||||
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtStaticProjectStructureProvider
|
||||
import org.jetbrains.kotlin.analysis.project.structure.*
|
||||
import org.jetbrains.kotlin.analysis.project.structure.impl.KtDanglingFileModuleImpl
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
|
||||
@@ -22,37 +23,35 @@ class KtTestProjectStructureProvider(
|
||||
private val builtinsModule: KtBuiltinsModule,
|
||||
private val projectStructure: KtModuleProjectStructure,
|
||||
) : KtStaticProjectStructureProvider() {
|
||||
override fun getNotUnderContentRootModule(project: Project, file: PsiFile?): KtNotUnderContentRootModule {
|
||||
error("Not-under content root modules most be initialized explicitly in tests")
|
||||
}
|
||||
|
||||
@OptIn(KtModuleStructureInternals::class)
|
||||
override fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule {
|
||||
// Unwrap context-dependent file copies coming from dependent sessions
|
||||
val containingPsiFile = element.containingFile.originalFile
|
||||
val containingFile = element.containingFile.originalFile
|
||||
|
||||
if (containingPsiFile is KtCodeFragment) {
|
||||
val contextElement = containingPsiFile.context
|
||||
?: throw KotlinExceptionWithAttachments("Context not found for a code fragment")
|
||||
.withAttachment("codeFragment.kt", containingPsiFile.text)
|
||||
val virtualFile = containingFile.virtualFile
|
||||
|
||||
val contextModule = getModule(contextElement, contextualModule)
|
||||
return KtDanglingFileModuleImpl(containingPsiFile, contextModule)
|
||||
if (virtualFile != null) {
|
||||
if (virtualFile.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) {
|
||||
return builtinsModule
|
||||
}
|
||||
|
||||
projectStructure.binaryModules
|
||||
.firstOrNull { binaryModule -> virtualFile in binaryModule.contentScope }
|
||||
?.let { return it }
|
||||
}
|
||||
|
||||
val containingVirtualFile = containingPsiFile.virtualFile
|
||||
if (containingVirtualFile.extension == BuiltInSerializerProtocol.BUILTINS_FILE_EXTENSION) {
|
||||
return builtinsModule
|
||||
}
|
||||
|
||||
containingVirtualFile.analysisExtensionFileContextModule?.let { return it }
|
||||
|
||||
projectStructure.binaryModules.firstOrNull { binaryModule ->
|
||||
containingVirtualFile in binaryModule.contentScope
|
||||
}?.let { return it }
|
||||
computeSpecialModule(containingFile)?.let { return it }
|
||||
|
||||
return projectStructure.mainModules.firstOrNull { module ->
|
||||
element in module.ktModule.contentScope
|
||||
}?.ktModule
|
||||
?: throw KotlinExceptionWithAttachments("Cannot find KtModule; see the attachment for more details.")
|
||||
.withAttachment(
|
||||
containingVirtualFile.path,
|
||||
virtualFile?.path ?: containingFile.name,
|
||||
allKtModules.joinToString(separator = System.lineSeparator()) { it.asDebugString() }
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ private object KtOutOfContentRootModuleFactory : KtModuleFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private class KtNotUnderContentRootModuleForTest(
|
||||
internal class KtNotUnderContentRootModuleForTest(
|
||||
override val name: String,
|
||||
override val file: PsiFile,
|
||||
override val platform: TargetPlatform
|
||||
|
||||
+47
-1
@@ -7,8 +7,14 @@ package org.jetbrains.kotlin.analysis.project.structure
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.analysis.project.structure.impl.KtDanglingFileModuleImpl
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.analysisContext
|
||||
import org.jetbrains.kotlin.psi.doNotAnalyze
|
||||
|
||||
public abstract class ProjectStructureProvider {
|
||||
/**
|
||||
@@ -34,6 +40,39 @@ public abstract class ProjectStructureProvider {
|
||||
*/
|
||||
public abstract fun getModule(element: PsiElement, contextualModule: KtModule?): KtModule
|
||||
|
||||
protected abstract fun getNotUnderContentRootModule(project: Project, file: PsiFile?): KtNotUnderContentRootModule
|
||||
|
||||
@OptIn(KtModuleStructureInternals::class)
|
||||
protected fun computeSpecialModule(file: PsiFile): KtModule? {
|
||||
val virtualFile = file.virtualFile
|
||||
if (virtualFile != null) {
|
||||
val contextModule = virtualFile.analysisExtensionFileContextModule
|
||||
if (contextModule != null) {
|
||||
return contextModule
|
||||
}
|
||||
}
|
||||
|
||||
if (file is KtFile && file.isDangling) {
|
||||
val contextModule = computeContextModule(file)
|
||||
return KtDanglingFileModuleImpl(file, contextModule)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@OptIn(KtModuleStructureInternals::class)
|
||||
private fun computeContextModule(file: KtFile): KtModule {
|
||||
val contextElement = file.context
|
||||
?: file.analysisContext
|
||||
?: file.originalFile.takeIf { it !== file }
|
||||
|
||||
if (contextElement != null) {
|
||||
return getModule(contextElement, contextualModule = null)
|
||||
}
|
||||
|
||||
return getNotUnderContentRootModule(file.project, file = null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Project-global [LanguageVersionSettings] for source modules lacking explicit settings (such as [KtNotUnderContentRootModule]).
|
||||
*/
|
||||
@@ -55,4 +94,11 @@ public abstract class ProjectStructureProvider {
|
||||
return getInstance(project).getModule(element, contextualModule)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KtModuleStructureInternals::class)
|
||||
private val KtFile.isDangling: Boolean
|
||||
get() = this is KtCodeFragment
|
||||
|| !isPhysical
|
||||
|| analysisContext != null
|
||||
|| doNotAnalyze != null
|
||||
Reference in New Issue
Block a user