[Analysis API] Generify 'KtCodeFragmentModule' to support ordinary files

This commit introduces dangling file modules, which may be either
code fragments or ordinary Kotlin files. As before, code fragments are
analyzed against some context element, however ordinary files only
have a context module.

Code fragments can also have a dangling file module as a contextual one,
including other code fragment. This is done to support potential usages
in completion, intentions and refactorings.
This commit is contained in:
Yan Zhulanow
2023-11-08 19:47:27 +09:00
committed by Space Team
parent 916e46debe
commit b7c774b9e5
16 changed files with 111 additions and 92 deletions
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.llFirMo
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.codeFragment import org.jetbrains.kotlin.analysis.low.level.api.fir.util.codeFragment
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
@@ -248,7 +248,7 @@ internal class KtFirCompilerFacility(
private fun computeTargetModules(module: KtModule): List<KtModule> { private fun computeTargetModules(module: KtModule): List<KtModule> {
return when (module) { return when (module) {
is KtCodeFragmentModule -> listOf(module.contextModule, module) is KtDanglingFileModule -> listOf(module.contextModule, module)
else -> listOf(module) else -> listOf(module)
} }
} }
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.analysis.api.standalone.base.project.structure
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFileSystemItem import com.intellij.psi.PsiFileSystemItem
import org.jetbrains.kotlin.analysis.project.structure.* import org.jetbrains.kotlin.analysis.project.structure.*
import org.jetbrains.kotlin.analysis.project.structure.impl.KtCodeFragmentModuleImpl import org.jetbrains.kotlin.analysis.project.structure.impl.KtDanglingFileModuleImpl
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.psiUtil.contains import org.jetbrains.kotlin.psi.psiUtil.contains
@@ -31,7 +31,7 @@ class KtStaticModuleProvider(
.withAttachment("codeFragment.kt", containingPsiFile.text) .withAttachment("codeFragment.kt", containingPsiFile.text)
val contextModule = getModule(contextElement, contextualModule) val contextModule = getModule(contextElement, contextualModule)
return KtCodeFragmentModuleImpl(containingPsiFile, contextModule) return KtDanglingFileModuleImpl(containingPsiFile, contextModule)
} }
val containingVirtualFile = containingPsiFile.virtualFile val containingVirtualFile = containingPsiFile.virtualFile
@@ -54,15 +54,10 @@ class LLFirResolveSessionService(project: Project) {
is KtSourceModule -> LLSourceModuleResolutionStrategyProvider is KtSourceModule -> LLSourceModuleResolutionStrategyProvider
is KtLibraryModule, is KtLibrarySourceModule -> LLLibraryModuleResolutionStrategyProvider(module) is KtLibraryModule, is KtLibrarySourceModule -> LLLibraryModuleResolutionStrategyProvider(module)
is KtScriptModule -> LLScriptModuleResolutionStrategyProvider(module) is KtScriptModule -> LLScriptModuleResolutionStrategyProvider(module)
is KtCodeFragmentModule -> { is KtDanglingFileModule -> {
val contextElement = module.codeFragment.context val contextModule = module.contextModule
if (contextElement != null) { val contextResolutionStrategyProvider = createResolutionStrategyProvider(contextModule, moduleProvider)
val contextModule = moduleProvider.getModule(contextElement) LLDanglingFileResolutionStrategyProvider(contextResolutionStrategyProvider)
val contextResolutionStrategyProvider = createResolutionStrategyProvider(contextModule, moduleProvider)
LLCodeFragmentResolutionStrategyProvider(contextResolutionStrategyProvider)
} else {
LLSimpleResolutionStrategyProvider(module)
}
} }
is KtNotUnderContentRootModule -> LLSimpleResolutionStrategyProvider(module) is KtNotUnderContentRootModule -> LLSimpleResolutionStrategyProvider(module)
else -> { else -> {
@@ -77,7 +72,7 @@ class LLFirResolveSessionService(project: Project) {
return when (moduleProvider.useSiteModule) { return when (moduleProvider.useSiteModule) {
is KtSourceModule, is KtSourceModule,
is KtScriptModule, is KtScriptModule,
is KtCodeFragmentModule -> LLSourceDiagnosticProvider(moduleProvider, sessionProvider) is KtDanglingFileModule -> LLSourceDiagnosticProvider(moduleProvider, sessionProvider)
else -> LLEmptyDiagnosticProvider else -> LLEmptyDiagnosticProvider
} }
} }
@@ -115,10 +110,10 @@ private class LLScriptModuleResolutionStrategyProvider(private val useSiteModule
} }
} }
private class LLCodeFragmentResolutionStrategyProvider(private val delegate: LLModuleResolutionStrategyProvider) : LLModuleResolutionStrategyProvider { private class LLDanglingFileResolutionStrategyProvider(private val delegate: LLModuleResolutionStrategyProvider) : LLModuleResolutionStrategyProvider {
override fun getKind(module: KtModule): LLModuleResolutionStrategy { override fun getKind(module: KtModule): LLModuleResolutionStrategy {
return when (module) { return when (module) {
is KtCodeFragmentModule -> LLModuleResolutionStrategy.LAZY is KtDanglingFileModule -> LLModuleResolutionStrategy.LAZY
else -> delegate.getKind(module) else -> delegate.getKind(module)
} }
} }
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
class LLFirCodeFragmentDependenciesSymbolProvider(private val delegate: FirSymbolProvider) : FirSymbolProvider(delegate.session) { class LLFirDanglingFileDependenciesSymbolProvider(private val delegate: FirSymbolProvider) : FirSymbolProvider(delegate.session) {
override val symbolNamesProvider: FirSymbolNamesProvider override val symbolNamesProvider: FirSymbolNamesProvider
get() = delegate.symbolNamesProvider get() = delegate.symbolNamesProvider
@@ -44,8 +44,6 @@ import org.jetbrains.kotlin.fir.resolve.transformers.FirDummyCompilerLazyDeclara
import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.session.* import org.jetbrains.kotlin.fir.session.*
import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver
import org.jetbrains.kotlin.platform.has
import org.jetbrains.kotlin.platform.jvm.JvmPlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
@@ -461,12 +459,12 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
} }
} }
abstract fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession abstract fun createDanglingFileSession(module: KtDanglingFileModule, contextSession: LLFirSession): LLFirSession
protected fun doCreateCodeFragmentSession( protected fun doCreateDanglingFileSession(
module: KtCodeFragmentModule, module: KtDanglingFileModule,
contextSession: LLFirSession, contextSession: LLFirSession,
additionalSessionConfiguration: context(CodeFragmentSessionCreationContext) LLFirCodeFragmentSession.() -> Unit, additionalSessionConfiguration: context(DanglingFileSessionCreationContext) LLFirDanglingFileSession.() -> Unit,
): LLFirSession { ): LLFirSession {
val platform = module.platform val platform = module.platform
val builtinsSession = LLFirBuiltinsSessionFactory.getInstance(project).getBuiltinsSession(platform) val builtinsSession = LLFirBuiltinsSessionFactory.getInstance(project).getBuiltinsSession(platform)
@@ -475,7 +473,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
val components = LLFirModuleResolveComponents(module, globalResolveComponents, scopeProvider) val components = LLFirModuleResolveComponents(module, globalResolveComponents, scopeProvider)
val session = LLFirCodeFragmentSession(module, components, builtinsSession.builtinTypes) val session = LLFirDanglingFileSession(module, components, builtinsSession.builtinTypes)
components.session = session components.session = session
val moduleData = createModuleData(session) val moduleData = createModuleData(session)
@@ -492,9 +490,8 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
this, this,
components, components,
canContainKotlinPackage = true, canContainKotlinPackage = true,
) { scope -> declarationProviderFactory = { scope -> scope.createScopedDeclarationProviderForFile(module.file) }
scope.createScopedDeclarationProviderForFile(module.codeFragment) )
}
register(FirProvider::class, firProvider) register(FirProvider::class, firProvider)
register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver()) register(FirLazyDeclarationResolver::class, LLFirLazyDeclarationResolver())
@@ -518,7 +515,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
} }
// Wrap dependencies into a single classpath-filtering provider // Wrap dependencies into a single classpath-filtering provider
listOf(LLFirCodeFragmentDependenciesSymbolProvider(FirCompositeSymbolProvider(session, providers))) listOf(LLFirDanglingFileDependenciesSymbolProvider(FirCompositeSymbolProvider(session, providers)))
} }
register(DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY, dependencyProvider) register(DEPENDENCIES_SYMBOL_PROVIDER_QUALIFIED_KEY, dependencyProvider)
@@ -535,7 +532,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
.createIfNeeded(this) .createIfNeeded(this)
?.also { register(FirSwitchableExtensionDeclarationsSymbolProvider::class, it) } ?.also { register(FirSwitchableExtensionDeclarationsSymbolProvider::class, it) }
val context = CodeFragmentSessionCreationContext( val context = DanglingFileSessionCreationContext(
moduleData, moduleData,
firProvider, firProvider,
dependencyProvider, dependencyProvider,
@@ -547,7 +544,7 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
} }
} }
protected class CodeFragmentSessionCreationContext( protected class DanglingFileSessionCreationContext(
val moduleData: LLFirModuleData, val moduleData: LLFirModuleData,
val firProvider: LLFirProvider, val firProvider: LLFirProvider,
val dependencyProvider: LLFirDependenciesSymbolProvider, val dependencyProvider: LLFirDependenciesSymbolProvider,
@@ -579,11 +576,10 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
fun getOrCreateSessionForDependency(dependency: KtModule): LLFirSession? = when (dependency) { fun getOrCreateSessionForDependency(dependency: KtModule): LLFirSession? = when (dependency) {
is KtBuiltinsModule -> null // Built-ins are already added is KtBuiltinsModule -> null // Built-ins are already added
is KtBinaryModule -> llFirSessionCache.getSession(dependency, preferBinary = true) is KtBinaryModule -> llFirSessionCache.getSession(dependency, preferBinary = true)
is KtSourceModule -> llFirSessionCache.getSession(dependency) is KtSourceModule, is KtDanglingFileModule -> llFirSessionCache.getSession(dependency)
is KtScriptModule, is KtScriptModule,
is KtScriptDependencyModule, is KtScriptDependencyModule,
is KtCodeFragmentModule,
is KtNotUnderContentRootModule, is KtNotUnderContentRootModule,
is KtLibrarySourceModule, is KtLibrarySourceModule,
-> { -> {
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirLi
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider
@@ -74,8 +74,8 @@ internal class LLFirCommonSessionFactory(project: Project) : LLFirAbstractSessio
} }
} }
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession { override fun createDanglingFileSession(module: KtDanglingFileModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) { return doCreateDanglingFileSession(module, contextSession) {
registerDefaultComponents() registerDefaultComponents()
register( register(
@@ -6,12 +6,12 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveComponents
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
import org.jetbrains.kotlin.fir.BuiltinTypes import org.jetbrains.kotlin.fir.BuiltinTypes
import org.jetbrains.kotlin.fir.PrivateSessionConstructor import org.jetbrains.kotlin.fir.PrivateSessionConstructor
internal class LLFirCodeFragmentSession @PrivateSessionConstructor constructor( internal class LLFirDanglingFileSession @PrivateSessionConstructor constructor(
ktModule: KtCodeFragmentModule, ktModule: KtDanglingFileModule,
override val moduleComponents: LLFirModuleResolveComponents, override val moduleComponents: LLFirModuleResolveComponents,
builtinTypes: BuiltinTypes, builtinTypes: BuiltinTypes,
) : LLFirResolvableModuleSession(ktModule, builtinTypes) ) : LLFirResolvableModuleSession(ktModule, builtinTypes)
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirLi
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.fir.BuiltinTypes import org.jetbrains.kotlin.fir.BuiltinTypes
@@ -75,8 +75,8 @@ internal class LLFirJsSessionFactory(project: Project) : LLFirAbstractSessionFac
} }
} }
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession { override fun createDanglingFileSession(module: KtDanglingFileModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) { return doCreateDanglingFileSession(module, contextSession) {
registerDefaultComponents() registerDefaultComponents()
registerModuleIndependentJsComponents() registerModuleIndependentJsComponents()
@@ -78,12 +78,14 @@ internal class LLFirJvmSessionFactory(project: Project) : LLFirAbstractSessionFa
} }
} }
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession { override fun createDanglingFileSession(module: KtDanglingFileModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) { return doCreateDanglingFileSession(module, contextSession) {
registerJavaComponents(JavaModuleResolver.getInstance(project)) registerJavaComponents(JavaModuleResolver.getInstance(project))
val javaSymbolProvider = LLFirJavaSymbolProvider(this, moduleData, project, firProvider.searchScope) val contextJavaSymbolProvider = contextSession.nullableJavaSymbolProvider
register(JavaSymbolProvider::class, javaSymbolProvider) if (contextJavaSymbolProvider != null) {
register(JavaSymbolProvider::class, contextJavaSymbolProvider)
}
register( register(
FirSymbolProvider::class, FirSymbolProvider::class,
@@ -93,7 +95,7 @@ internal class LLFirJvmSessionFactory(project: Project) : LLFirAbstractSessionFa
firProvider.symbolProvider, firProvider.symbolProvider,
switchableExtensionDeclarationsSymbolProvider, switchableExtensionDeclarationsSymbolProvider,
syntheticFunctionInterfaceProvider, syntheticFunctionInterfaceProvider,
javaSymbolProvider contextJavaSymbolProvider
), ),
dependencyProvider dependencyProvider
) )
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirLi
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider
@@ -72,8 +72,8 @@ internal class LLFirNativeSessionFactory(project: Project) : LLFirAbstractSessio
} }
} }
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession { override fun createDanglingFileSession(module: KtDanglingFileModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) { return doCreateDanglingFileSession(module, contextSession) {
registerModuleIndependentNativeComponents() registerModuleIndependentNativeComponents()
register( register(
@@ -35,7 +35,7 @@ class LLFirSessionCache(private val project: Project) {
private val sourceCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap() private val sourceCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap()
private val binaryCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap() private val binaryCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap()
private val codeFragmentSessionCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap() private val danglingFileSessionCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap()
/** /**
* Returns the existing session if found, or creates a new session and caches it. * Returns the existing session if found, or creates a new session and caches it.
@@ -49,7 +49,7 @@ class LLFirSessionCache(private val project: Project) {
} }
val targetCache = when (module) { val targetCache = when (module) {
is KtCodeFragmentModule -> codeFragmentSessionCache is KtDanglingFileModule -> danglingFileSessionCache
else -> sourceCache else -> sourceCache
} }
@@ -86,9 +86,9 @@ class LLFirSessionCache(private val project: Project) {
val didSourceSessionExist = removeSessionFrom(module, sourceCache) val didSourceSessionExist = removeSessionFrom(module, sourceCache)
val didBinarySessionExist = module is KtBinaryModule && removeSessionFrom(module, binaryCache) val didBinarySessionExist = module is KtBinaryModule && removeSessionFrom(module, binaryCache)
val didCodeFragmentSessionExist = module is KtCodeFragmentModule && removeSessionFrom(module, codeFragmentSessionCache) val didDanglingFileSessionExist = module is KtDanglingFileModule && removeSessionFrom(module, danglingFileSessionCache)
return didSourceSessionExist || didBinarySessionExist || didCodeFragmentSessionExist return didSourceSessionExist || didBinarySessionExist || didDanglingFileSessionExist
} }
private fun removeSessionFrom(module: KtModule, storage: SessionStorage): Boolean { private fun removeSessionFrom(module: KtModule, storage: SessionStorage): Boolean {
@@ -114,11 +114,11 @@ class LLFirSessionCache(private val project: Project) {
removeAllMatchingSessionsFrom(sourceCache) { it !is KtBinaryModule && it !is KtLibrarySourceModule } removeAllMatchingSessionsFrom(sourceCache) { it !is KtBinaryModule && it !is KtLibrarySourceModule }
} }
removeAllCodeFragmentSessions() removeAllDanglingFileSessions()
} }
fun removeAllCodeFragmentSessions() { fun removeAllDanglingFileSessions() {
removeAllSessionsFrom(codeFragmentSessionCache) removeAllSessionsFrom(danglingFileSessionCache)
} }
// Removing script sessions is only needed temporarily until KTIJ-25620 has been implemented. // Removing script sessions is only needed temporarily until KTIJ-25620 has been implemented.
@@ -159,11 +159,11 @@ class LLFirSessionCache(private val project: Project) {
is KtLibraryModule, is KtLibrarySourceModule -> sessionFactory.createLibrarySession(module) is KtLibraryModule, is KtLibrarySourceModule -> sessionFactory.createLibrarySession(module)
is KtSdkModule -> sessionFactory.createBinaryLibrarySession(module) is KtSdkModule -> sessionFactory.createBinaryLibrarySession(module)
is KtScriptModule -> sessionFactory.createScriptSession(module) is KtScriptModule -> sessionFactory.createScriptSession(module)
is KtCodeFragmentModule -> { is KtDanglingFileModule -> {
// 'KtCodeFragment' context must have an analyzable session, so we can properly compile code against it. // Dangling file context must have an analyzable session, so we can properly compile code against it.
// 'KtCodeFragmentModule' is always a leaf module, there might not be a circular reference. // 'KtDanglingFileModule' is always a leaf module, so there might not be a circular reference.
val contextSession = getSession(module.contextModule, preferBinary = false) val contextSession = getSession(module.contextModule, preferBinary = false)
sessionFactory.createCodeFragmentSession(module, contextSession) sessionFactory.createDanglingFileSession(module, contextSession)
} }
is KtNotUnderContentRootModule -> sessionFactory.createNotUnderContentRootResolvableSession(module) is KtNotUnderContentRootModule -> sessionFactory.createNotUnderContentRootResolvableSession(module)
else -> error("Unexpected module kind: ${module::class.simpleName}") else -> error("Unexpected module kind: ${module::class.simpleName}")
@@ -54,7 +54,7 @@ class LLFirSessionInvalidationService(private val project: Project) : Disposable
) )
busConnection.subscribe( busConnection.subscribe(
PsiModificationTracker.TOPIC, PsiModificationTracker.TOPIC,
PsiModificationTracker.Listener { invalidateAllCodeFragments() } PsiModificationTracker.Listener { invalidateAllDanglingFiles() }
) )
} }
@@ -85,7 +85,7 @@ class LLFirSessionInvalidationService(private val project: Project) : Disposable
sessionCache.removeAllScriptSessions() sessionCache.removeAllScriptSessions()
} }
sessionCache.removeAllCodeFragmentSessions() sessionCache.removeAllDanglingFileSessions()
} }
private fun invalidateAll(includeLibraryModules: Boolean) { private fun invalidateAll(includeLibraryModules: Boolean) {
@@ -105,8 +105,8 @@ class LLFirSessionInvalidationService(private val project: Project) : Disposable
LLFirSessionCache.getInstance(project).removeAllSessions(includeLibraryModules) LLFirSessionCache.getInstance(project).removeAllSessions(includeLibraryModules)
} }
private fun invalidateAllCodeFragments() { private fun invalidateAllDanglingFiles() {
LLFirSessionCache.getInstance(project).removeAllCodeFragmentSessions() LLFirSessionCache.getInstance(project).removeAllDanglingFileSessions()
} }
override fun dispose() { override fun dispose() {
@@ -63,9 +63,10 @@ internal class LLFirResolvableResolveSession(
phase: FirResolvePhase, phase: FirResolvePhase,
): FirBasedSymbol<*> { ): FirBasedSymbol<*> {
val containingKtFile = ktDeclaration.containingKtFile val containingKtFile = ktDeclaration.containingKtFile
val module = getModule(containingKtFile.originalKtFile ?: containingKtFile) val module = getModule(containingKtFile)
return when (getModuleResolutionStrategy(module)) { return when (getModuleResolutionStrategy(module)) {
LLModuleResolutionStrategy.LAZY -> findSourceFirSymbol(ktDeclaration, module).also { resolveFirToPhase(it.fir, phase) } LLModuleResolutionStrategy.LAZY -> findSourceFirSymbol(ktDeclaration).also { resolveFirToPhase(it.fir, phase) }
LLModuleResolutionStrategy.STATIC -> findFirCompiledSymbol(ktDeclaration, module) LLModuleResolutionStrategy.STATIC -> findFirCompiledSymbol(ktDeclaration, module)
} }
} }
@@ -81,8 +82,10 @@ internal class LLFirResolvableResolveSession(
return firDeclaration.symbol return firDeclaration.symbol
} }
private fun findSourceFirSymbol(ktDeclaration: KtDeclaration, module: KtModule): FirBasedSymbol<*> { private fun findSourceFirSymbol(ktDeclaration: KtDeclaration): FirBasedSymbol<*> {
return findSourceFirDeclarationByDeclaration(ktDeclaration.originalDeclaration ?: ktDeclaration, module) val targetDeclaration = ktDeclaration.originalDeclaration ?: ktDeclaration
val targetModule = getModule(targetDeclaration)
return findSourceFirDeclarationByDeclaration(targetDeclaration, targetModule)
} }
private fun findSourceFirDeclarationByDeclaration(ktDeclaration: KtDeclaration, module: KtModule): FirBasedSymbol<*> { private fun findSourceFirDeclarationByDeclaration(ktDeclaration: KtDeclaration, module: KtModule): FirBasedSymbol<*> {
@@ -10,7 +10,6 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import java.nio.file.Path import java.nio.file.Path
@@ -216,21 +215,29 @@ public interface KtScriptDependencyModule : KtModule {
} }
/** /**
* A module for a Kotlin code fragment a piece of code analyzed against a specific context element. * A module for a dangling file. Such files are usually temporary and are stored in-memory.
* Dangling files may be created for various purposes, such as: a code fragment for the evaluator, a sandbox for testing code modification
* applicability, etc.
*/ */
public interface KtCodeFragmentModule : KtModule { public interface KtDanglingFileModule : KtModule {
/** /**
* A code fragment PSI. * A temporary file PSI.
*/ */
public val codeFragment: KtCodeFragment public val file: KtFile
/** /**
* Module of the context element. * The module against which the [file] is analyzed.
*/ */
public val contextModule: KtModule public val contextModule: KtModule
/**
* True if the [file] is a code fragment.
* Useful to recognize code fragments when their PSI was collected.
*/
public val isCodeFragment: Boolean
override val moduleDescription: String override val moduleDescription: String
get() = "Code fragment" get() = "Temporary file"
} }
/** /**
@@ -7,22 +7,35 @@ package org.jetbrains.kotlin.analysis.project.structure.impl
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtDanglingFileModule
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import java.util.Objects import java.util.Objects
public class KtCodeFragmentModuleImpl( public class KtDanglingFileModuleImpl(
codeFragment: KtCodeFragment, file: KtFile,
override val contextModule: KtModule override val contextModule: KtModule
) : KtCodeFragmentModule { ) : KtDanglingFileModule {
private val codeFragmentRef = codeFragment.createSmartPointer() override val isCodeFragment: Boolean = file is KtCodeFragment
override val codeFragment: KtCodeFragment private val fileRef = file.createSmartPointer()
get() = codeFragmentRef.element?.takeIf { it.isValid } ?: error("Code fragment module is invalid")
init {
require(contextModule != this)
if (contextModule is KtDanglingFileModule) {
// Only code fragments can depend on dangling files.
// This is needed for completion, inspections and refactorings.
require(file is KtCodeFragment)
}
}
override val file: KtFile
get() = fileRef.element?.takeIf { it.isValid } ?: error("Dangling file module is invalid")
override val project: Project override val project: Project
get() = contextModule.project get() = contextModule.project
@@ -34,7 +47,7 @@ public class KtCodeFragmentModuleImpl(
get() = contextModule.analyzerServices get() = contextModule.analyzerServices
override val contentScope: GlobalSearchScope override val contentScope: GlobalSearchScope
get() = GlobalSearchScope.fileScope(codeFragment) get() = GlobalSearchScope.fileScope(file)
override val directRegularDependencies: List<KtModule> override val directRegularDependencies: List<KtModule>
get() = contextModule.directRegularDependencies get() = contextModule.directRegularDependencies
@@ -51,16 +64,16 @@ public class KtCodeFragmentModuleImpl(
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other is KtCodeFragmentModuleImpl) { if (other is KtDanglingFileModuleImpl) {
val selfCodeFragment = this.codeFragmentRef.element val selfFile = this.fileRef.element
val otherCodeFragment = other.codeFragmentRef.element val otherFile = other.fileRef.element
return selfCodeFragment != null && selfCodeFragment == otherCodeFragment && contextModule == other.contextModule return selfFile != null && selfFile == otherFile && contextModule == other.contextModule
} }
return false return false
} }
override fun hashCode(): Int { override fun hashCode(): Int {
return Objects.hash(codeFragmentRef.element, contextModule) return Objects.hash(fileRef.element, contextModule)
} }
} }
@@ -194,10 +194,13 @@ class SymbolKotlinAsJavaSupport(project: Project) : KotlinAsJavaSupportBase<KtMo
private fun KtElement.isFromSourceOrLibraryBinary(): Boolean = getModuleIfSupportEnabled()?.isFromSourceOrLibraryBinary() == true private fun KtElement.isFromSourceOrLibraryBinary(): Boolean = getModuleIfSupportEnabled()?.isFromSourceOrLibraryBinary() == true
private fun KtModule.isFromSourceOrLibraryBinary() = when (this) { private fun KtModule.isFromSourceOrLibraryBinary(): Boolean {
is KtSourceModule -> true return when (this) {
is KtLibraryModule -> true is KtSourceModule -> true
else -> false is KtLibraryModule -> true
is KtDanglingFileModule -> contextModule.isFromSourceOrLibraryBinary()
else -> false
}
} }
} }