FIR IDE: implement incremental function analysis
This commit is contained in:
@@ -281,14 +281,19 @@ inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(noinline
|
|||||||
inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(
|
inline fun <reified T : PsiElement> PsiElement.collectDescendantsOfType(
|
||||||
crossinline canGoInside: (PsiElement) -> Boolean,
|
crossinline canGoInside: (PsiElement) -> Boolean,
|
||||||
noinline predicate: (T) -> Boolean = { true }
|
noinline predicate: (T) -> Boolean = { true }
|
||||||
): List<T> {
|
): List<T> = collectDescendantsOfTypeTo(ArrayList(), canGoInside, predicate)
|
||||||
val result = ArrayList<T>()
|
|
||||||
|
inline fun <reified T : PsiElement, C : MutableCollection<T>> PsiElement.collectDescendantsOfTypeTo(
|
||||||
|
to: C,
|
||||||
|
crossinline canGoInside: (PsiElement) -> Boolean,
|
||||||
|
noinline predicate: (T) -> Boolean = { true }
|
||||||
|
): C {
|
||||||
forEachDescendantOfType<T>(canGoInside) {
|
forEachDescendantOfType<T>(canGoInside) {
|
||||||
if (predicate(it)) {
|
if (predicate(it)) {
|
||||||
result.add(it)
|
to.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return to
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------- Working with offsets, ranges and texts ----------------------------------------------------------------------------------------------
|
// ----------- Working with offsets, ranges and texts ----------------------------------------------------------------------------------------------
|
||||||
|
|||||||
+3
-2
@@ -14,6 +14,8 @@ import com.intellij.psi.util.CachedValuesManager
|
|||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.LibraryModificationTracker
|
import org.jetbrains.kotlin.idea.caches.project.LibraryModificationTracker
|
||||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||||
|
import org.jetbrains.kotlin.idea.caches.trackers.KotlinCodeBlockModificationListener
|
||||||
|
import org.jetbrains.kotlin.idea.caches.trackers.KotlinModuleOutOfCodeBlockModificationTracker
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeDependentModulesSourcesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeDependentModulesSourcesSession
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeLibrariesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeLibrariesSession
|
||||||
@@ -25,7 +27,7 @@ import org.jetbrains.kotlin.idea.util.psiModificationTrackerBasedCachedValue
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
internal class FirIdeResolveStateService(private val project: Project) {
|
internal class FirIdeResolveStateService(private val project: Project) {
|
||||||
private val stateCache by psiModificationTrackerBasedCachedValue(project) {
|
private val stateCache by cachedValue(project, KotlinCodeBlockModificationListener.getInstance(project).kotlinOutOfCodeBlockTracker) {
|
||||||
ConcurrentHashMap<IdeaModuleInfo, FirModuleResolveStateImpl>()
|
ConcurrentHashMap<IdeaModuleInfo, FirModuleResolveStateImpl>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +37,6 @@ internal class FirIdeResolveStateService(private val project: Project) {
|
|||||||
ConcurrentHashMap<IdeaModuleInfo, FirIdeLibrariesSession>()
|
ConcurrentHashMap<IdeaModuleInfo, FirIdeLibrariesSession>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun createResolveStateFor(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl {
|
private fun createResolveStateFor(moduleInfo: IdeaModuleInfo): FirModuleResolveStateImpl {
|
||||||
require(moduleInfo is ModuleSourceInfo)
|
require(moduleInfo is ModuleSourceInfo)
|
||||||
val firPhaseRunner = FirPhaseRunner()
|
val firPhaseRunner = FirPhaseRunner()
|
||||||
|
|||||||
+11
-18
@@ -11,17 +11,16 @@ import org.jetbrains.kotlin.fir.FirSession
|
|||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.DiagnosticsCollector
|
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.DiagnosticsCollector
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirElementBuilder
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirElementBuilder
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.PsiToFirCache
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureCache
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.*
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeCurrentModuleSourcesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeCurrentModuleSourcesSession
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeDependentModulesSourcesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeDependentModulesSourcesSession
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeLibrariesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeLibrariesSession
|
||||||
@@ -45,16 +44,13 @@ abstract class FirModuleResolveState {
|
|||||||
// todo temporary, used only in completion
|
// todo temporary, used only in completion
|
||||||
abstract fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile)
|
abstract fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile)
|
||||||
|
|
||||||
// todo temporary, used only in completion
|
|
||||||
abstract fun getCachedMappingForCompletion(element: KtElement): FirElement?
|
|
||||||
|
|
||||||
abstract fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D
|
abstract fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D
|
||||||
|
|
||||||
// todo temporary, used only in completion
|
// todo temporary, used only in completion
|
||||||
internal abstract fun lazyResolveDeclarationForCompletion(
|
abstract fun lazyResolveDeclarationForCompletion(
|
||||||
firFunction: FirDeclaration,
|
firFunction: FirDeclaration,
|
||||||
containerFirFile: FirFile,
|
containerFirFile: FirFile,
|
||||||
firIdeProvider: FirIdeProvider,
|
firIdeProvider: FirProvider,
|
||||||
toPhase: FirResolvePhase,
|
toPhase: FirResolvePhase,
|
||||||
towerDataContextCollector: FirTowerDataContextCollector
|
towerDataContextCollector: FirTowerDataContextCollector
|
||||||
)
|
)
|
||||||
@@ -71,22 +67,21 @@ internal class FirModuleResolveStateImpl(
|
|||||||
val firFileBuilder: FirFileBuilder,
|
val firFileBuilder: FirFileBuilder,
|
||||||
val firLazyDeclarationResolver: FirLazyDeclarationResolver,
|
val firLazyDeclarationResolver: FirLazyDeclarationResolver,
|
||||||
) : FirModuleResolveState() {
|
) : FirModuleResolveState() {
|
||||||
val psiToFirCache = PsiToFirCache(currentModuleSourcesSession.cache)
|
val fileStructureCache = FileStructureCache(firFileBuilder, firLazyDeclarationResolver)
|
||||||
val elementBuilder = FirElementBuilder(firFileBuilder, firLazyDeclarationResolver)
|
val elementBuilder = FirElementBuilder()
|
||||||
private val diagnosticsCollector =
|
private val diagnosticsCollector = DiagnosticsCollector(fileStructureCache, currentModuleSourcesSession.cache)
|
||||||
DiagnosticsCollector(firFileBuilder, elementBuilder, psiToFirCache, currentModuleSourcesSession.cache)
|
|
||||||
|
|
||||||
override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession =
|
override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession =
|
||||||
sessionProvider.getSession(moduleInfo)
|
sessionProvider.getSession(moduleInfo)
|
||||||
|
|
||||||
override fun getOrBuildFirFor(element: KtElement, toPhase: FirResolvePhase): FirElement =
|
override fun getOrBuildFirFor(element: KtElement, toPhase: FirResolvePhase): FirElement =
|
||||||
elementBuilder.getOrBuildFirFor(element, currentModuleSourcesSession.cache, psiToFirCache, toPhase)
|
elementBuilder.getOrBuildFirFor(element, currentModuleSourcesSession.cache, fileStructureCache)
|
||||||
|
|
||||||
override fun getDiagnostics(element: KtElement): List<Diagnostic> =
|
override fun getDiagnostics(element: KtElement): List<Diagnostic> =
|
||||||
diagnosticsCollector.getDiagnosticsFor(element)
|
diagnosticsCollector.getDiagnosticsFor(element)
|
||||||
|
|
||||||
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
||||||
psiToFirCache.recordElementsForCompletionFrom(fir, firFile, ktFile)
|
error("Should be called only from FirModuleResolveStateForCompletion")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
|
override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
|
||||||
@@ -98,13 +93,10 @@ internal class FirModuleResolveStateImpl(
|
|||||||
return declaration
|
return declaration
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCachedMappingForCompletion(element: KtElement): FirElement? =
|
|
||||||
psiToFirCache.getCachedMapping(element)
|
|
||||||
|
|
||||||
override fun lazyResolveDeclarationForCompletion(
|
override fun lazyResolveDeclarationForCompletion(
|
||||||
firFunction: FirDeclaration,
|
firFunction: FirDeclaration,
|
||||||
containerFirFile: FirFile,
|
containerFirFile: FirFile,
|
||||||
firIdeProvider: FirIdeProvider,
|
firIdeProvider: FirProvider,
|
||||||
toPhase: FirResolvePhase,
|
toPhase: FirResolvePhase,
|
||||||
towerDataContextCollector: FirTowerDataContextCollector
|
towerDataContextCollector: FirTowerDataContextCollector
|
||||||
) {
|
) {
|
||||||
@@ -113,6 +105,7 @@ internal class FirModuleResolveStateImpl(
|
|||||||
currentModuleSourcesSession.cache,
|
currentModuleSourcesSession.cache,
|
||||||
containerFirFile,
|
containerFirFile,
|
||||||
firIdeProvider,
|
firIdeProvider,
|
||||||
|
fromPhase = firFunction.resolvePhase,
|
||||||
toPhase,
|
toPhase,
|
||||||
towerDataContextCollector,
|
towerDataContextCollector,
|
||||||
checkPCE = false
|
checkPCE = false
|
||||||
|
|||||||
+10
-13
@@ -11,9 +11,11 @@ import org.jetbrains.kotlin.fir.FirSession
|
|||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.PsiToFirCache
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FirElementsRecorder
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeCurrentModuleSourcesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeCurrentModuleSourcesSession
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeDependentModulesSourcesSession
|
import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.FirIdeDependentModulesSourcesSession
|
||||||
@@ -29,29 +31,24 @@ internal class FirModuleResolveStateForCompletion(
|
|||||||
override val dependentModulesSourcesSession: FirIdeDependentModulesSourcesSession get() = originalState.dependentModulesSourcesSession
|
override val dependentModulesSourcesSession: FirIdeDependentModulesSourcesSession get() = originalState.dependentModulesSourcesSession
|
||||||
override val librariesSession: FirIdeLibrariesSession get() = originalState.librariesSession
|
override val librariesSession: FirIdeLibrariesSession get() = originalState.librariesSession
|
||||||
|
|
||||||
private val psiToFirCache = PsiToFirCache(originalState.currentModuleSourcesSession.cache)
|
private val fileStructureCache = originalState.fileStructureCache
|
||||||
|
|
||||||
|
private val completionMapping = mutableMapOf<KtElement, FirElement>()
|
||||||
|
|
||||||
override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession =
|
override fun getSessionFor(moduleInfo: IdeaModuleInfo): FirSession =
|
||||||
originalState.getSessionFor(moduleInfo)
|
originalState.getSessionFor(moduleInfo)
|
||||||
|
|
||||||
override fun getOrBuildFirFor(element: KtElement, toPhase: FirResolvePhase): FirElement {
|
override fun getOrBuildFirFor(element: KtElement, toPhase: FirResolvePhase): FirElement {
|
||||||
getCachedMappingForCompletion(element)?.let { return it }
|
completionMapping[element]?.let { return it }
|
||||||
return originalState.elementBuilder.getOrBuildFirFor(
|
return originalState.elementBuilder.getOrBuildFirFor(
|
||||||
element,
|
element,
|
||||||
originalState.currentModuleSourcesSession.cache,
|
originalState.currentModuleSourcesSession.cache,
|
||||||
psiToFirCache,
|
fileStructureCache,
|
||||||
toPhase
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
override fun recordPsiToFirMappingsForCompletionFrom(fir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
||||||
psiToFirCache.recordElementsForCompletionFrom(fir, firFile, ktFile)
|
fir.accept(FirElementsRecorder(), completionMapping)
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCachedMappingForCompletion(element: KtElement): FirElement? {
|
|
||||||
psiToFirCache.getCachedMapping(element)?.let { return it }
|
|
||||||
originalState.psiToFirCache.getCachedMapping(element)?.let { return it }
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
|
override fun <D : FirDeclaration> resolvedFirToPhase(declaration: D, toPhase: FirResolvePhase): D {
|
||||||
@@ -61,7 +58,7 @@ internal class FirModuleResolveStateForCompletion(
|
|||||||
override fun lazyResolveDeclarationForCompletion(
|
override fun lazyResolveDeclarationForCompletion(
|
||||||
firFunction: FirDeclaration,
|
firFunction: FirDeclaration,
|
||||||
containerFirFile: FirFile,
|
containerFirFile: FirFile,
|
||||||
firIdeProvider: FirIdeProvider,
|
firIdeProvider: FirProvider,
|
||||||
toPhase: FirResolvePhase,
|
toPhase: FirResolvePhase,
|
||||||
towerDataContextCollector: FirTowerDataContextCollector
|
towerDataContextCollector: FirTowerDataContextCollector
|
||||||
) {
|
) {
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.FirSessionComponent
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.FirTransformerBasedResolveProcessor
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.createTransformerBasedProcessorByPhase
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class FirTransformerProvider(session: FirSession) : FirSessionComponent {
|
||||||
|
private val scopeSession = ThreadLocal.withInitial { ScopeSession() }
|
||||||
|
private val transformers = ThreadLocal.withInitial {
|
||||||
|
val scopesSession = scopeSession.get()
|
||||||
|
EnumMap<FirResolvePhase, FirTransformerBasedResolveProcessor>(FirResolvePhase::class.java).apply {
|
||||||
|
FirResolvePhase.values().forEach { resolvePhase ->
|
||||||
|
if (resolvePhase != FirResolvePhase.RAW_FIR) {
|
||||||
|
put(resolvePhase, resolvePhase.createTransformerBasedProcessorByPhase(session, scopesSession))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTransformerForPhase(phase: FirResolvePhase): FirTransformerBasedResolveProcessor =
|
||||||
|
transformers.get().getValue(phase)
|
||||||
|
|
||||||
|
fun getScopeSession(): ScopeSession = scopeSession.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
val FirSession.firTransformerProvider: FirTransformerProvider by FirSession.sessionComponentAccessor()
|
||||||
+5
-13
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.declarations.*
|
|||||||
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyAccessorCopy
|
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyAccessorCopy
|
||||||
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
|
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
|
||||||
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy
|
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
||||||
@@ -43,16 +42,11 @@ object LowLevelFirApiFacade {
|
|||||||
class FirCompletionContext internal constructor(
|
class FirCompletionContext internal constructor(
|
||||||
val session: FirSession,
|
val session: FirSession,
|
||||||
private val towerDataContextCollector: FirTowerDataContextCollector,
|
private val towerDataContextCollector: FirTowerDataContextCollector,
|
||||||
private val state: FirModuleResolveState,
|
|
||||||
) {
|
) {
|
||||||
fun getTowerDataContext(element: KtElement): FirTowerDataContext {
|
fun getTowerDataContext(element: KtElement): FirTowerDataContext {
|
||||||
var current: PsiElement? = element
|
var current: PsiElement? = element
|
||||||
while (current is KtElement) {
|
while (current is KtElement) {
|
||||||
val mappedFir = state.getCachedMappingForCompletion(current)
|
towerDataContextCollector.getContext(current)?.let { return it }
|
||||||
|
|
||||||
if (mappedFir is FirStatement) {
|
|
||||||
towerDataContextCollector.getContext(mappedFir)?.let { return it }
|
|
||||||
}
|
|
||||||
current = current.parent
|
current = current.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +72,7 @@ object LowLevelFirApiFacade {
|
|||||||
|
|
||||||
return FirCompletionContext(
|
return FirCompletionContext(
|
||||||
copyFunction.session,
|
copyFunction.session,
|
||||||
contextCollector,
|
contextCollector
|
||||||
state
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,8 +94,7 @@ object LowLevelFirApiFacade {
|
|||||||
|
|
||||||
return FirCompletionContext(
|
return FirCompletionContext(
|
||||||
copyProperty.session,
|
copyProperty.session,
|
||||||
contextCollector,
|
contextCollector
|
||||||
state
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +137,7 @@ object LowLevelFirApiFacade {
|
|||||||
symbol = builtSetter.symbol
|
symbol = builtSetter.symbol
|
||||||
resolvePhase = minOf(builtSetter.resolvePhase, FirResolvePhase.DECLARATIONS)
|
resolvePhase = minOf(builtSetter.resolvePhase, FirResolvePhase.DECLARATIONS)
|
||||||
source = builtSetter.source
|
source = builtSetter.source
|
||||||
session = state.firIdeSourcesSession
|
session = state.currentModuleSourcesSession
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builtSetter
|
builtSetter
|
||||||
@@ -159,7 +151,7 @@ object LowLevelFirApiFacade {
|
|||||||
|
|
||||||
resolvePhase = minOf(originalProperty.resolvePhase, FirResolvePhase.DECLARATIONS)
|
resolvePhase = minOf(originalProperty.resolvePhase, FirResolvePhase.DECLARATIONS)
|
||||||
source = builtProperty.source
|
source = builtProperty.source
|
||||||
session = state.firIdeSourcesSession
|
session = state.currentModuleSourcesSession
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-24
@@ -5,57 +5,68 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
|
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||||
import org.jetbrains.kotlin.fir.resolve.firProvider
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.ThreadSafe
|
import org.jetbrains.kotlin.idea.fir.low.level.api.annotations.ThreadSafe
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.FirElementFinder
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.structure.FileStructureCache
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.findNonLocalFirDeclaration
|
|
||||||
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps [KtElement] to [FirElement]
|
* Maps [KtElement] to [FirElement]
|
||||||
* Stateless, caches everything into [ModuleFileCache] & [PsiToFirCache] passed into the function
|
* Stateless, caches everything into [ModuleFileCache] & [FileStructureCache] passed into the function
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
internal class FirElementBuilder(
|
internal class FirElementBuilder {
|
||||||
private val firFileBuilder: FirFileBuilder,
|
|
||||||
private val firLazyDeclarationResolver: FirLazyDeclarationResolver,
|
|
||||||
) {
|
|
||||||
fun getOrBuildFirFor(
|
fun getOrBuildFirFor(
|
||||||
element: KtElement,
|
element: KtElement,
|
||||||
moduleFileCache: ModuleFileCache,
|
moduleFileCache: ModuleFileCache,
|
||||||
psiToFirCache: PsiToFirCache,
|
fileStructureCache: FileStructureCache,
|
||||||
toPhase: FirResolvePhase,
|
|
||||||
): FirElement {
|
): FirElement {
|
||||||
val ktFile = element.containingKtFile
|
val fileStructure = fileStructureCache.getFileStructure(element.containingKtFile, moduleFileCache)
|
||||||
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache)
|
val mappings = fileStructure.getStructureElementFor(element).mappings
|
||||||
|
|
||||||
val containerFir = when (val container = element.getNonLocalContainingDeclarationWithFqName()) {
|
val psi = when {
|
||||||
is KtDeclaration -> container.findNonLocalFirDeclaration(firFileBuilder, firFile.session.firProvider, moduleFileCache)
|
element is KtPropertyDelegate -> element.expression ?: element
|
||||||
null -> firFile
|
element is KtQualifiedExpression && element.selectorExpression is KtCallExpression -> {
|
||||||
else -> error("Unsupported: ${container.text}")
|
/*
|
||||||
|
KtQualifiedExpression with KtCallExpression in selector transformed in FIR to FirFunctionCall expression
|
||||||
|
Which will have a receiver as qualifier
|
||||||
|
*/
|
||||||
|
element.selectorExpression ?: error("Incomplete code:\n${element.getElementTextInContext()}")
|
||||||
|
}
|
||||||
|
else -> element
|
||||||
}
|
}
|
||||||
firLazyDeclarationResolver.lazyResolveDeclaration(containerFir, moduleFileCache, toPhase, checkPCE = true)
|
mappings[psi]?.let { return it }
|
||||||
|
return psi.getFirOfClosestParent(mappings)?.second
|
||||||
return psiToFirCache.getFir(element, containerFir, firFile)
|
?: error("FirElement is not found for:\n${element.getElementTextInContext()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtElement.getFirOfClosestParent(cache: Map<KtElement, FirElement>): Pair<KtElement, FirElement>? {
|
||||||
|
var current: PsiElement? = this
|
||||||
|
while (current is KtElement) {
|
||||||
|
val mappedFir = cache[current]
|
||||||
|
if (mappedFir != null) {
|
||||||
|
return current to mappedFir
|
||||||
|
}
|
||||||
|
current = current.parent
|
||||||
|
}
|
||||||
|
|
||||||
fun KtElement.getNonLocalContainingDeclarationWithFqName(): KtNamedDeclaration? {
|
return null
|
||||||
var container = parent
|
}
|
||||||
|
|
||||||
|
fun KtElement.getNonLocalContainingOrThisDeclaration(): KtNamedDeclaration? {
|
||||||
|
var container: PsiElement? = this
|
||||||
while (container != null && container !is KtFile) {
|
while (container != null && container !is KtFile) {
|
||||||
if (container is KtNamedDeclaration
|
if (container is KtNamedDeclaration
|
||||||
&& (container is KtClassOrObject || container is KtDeclarationWithBody)
|
&& (container is KtClassOrObject || container is KtDeclarationWithBody || container is KtProperty || container is KtTypeAlias)
|
||||||
&& !KtPsiUtil.isLocal(container)
|
&& !KtPsiUtil.isLocal(container)
|
||||||
&& container.name != null
|
|
||||||
&& container !is KtEnumEntry
|
&& container !is KtEnumEntry
|
||||||
&& container.containingClassOrObject !is KtEnumEntry
|
&& container.containingClassOrObject !is KtEnumEntry
|
||||||
) {
|
) {
|
||||||
|
|||||||
+6
-15
@@ -5,31 +5,22 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
|
package org.jetbrains.kotlin.idea.fir.low.level.api.element.builder
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
import org.jetbrains.kotlin.fir.psi
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
|
||||||
internal class FirTowerDataContextCollector {
|
class FirTowerDataContextCollector {
|
||||||
private val state: MutableMap<FirElement, FirTowerDataContext> = mutableMapOf()
|
private val state: MutableMap<KtElement, FirTowerDataContext> = mutableMapOf()
|
||||||
|
|
||||||
fun addStatementContext(statement: FirStatement, context: FirTowerDataContext) {
|
fun addStatementContext(statement: FirStatement, context: FirTowerDataContext) {
|
||||||
state[statement] = context
|
(statement.psi as? KtElement)?.let { state[it] = context }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addDeclarationContext(declaration: FirDeclaration, context: FirTowerDataContext) {
|
fun addDeclarationContext(declaration: FirDeclaration, context: FirTowerDataContext) {
|
||||||
state[declaration] = context
|
(declaration.psi as? KtElement)?.let { state[it] = context }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getContext(statement: FirElement): FirTowerDataContext? {
|
fun getContext(psi: KtElement): FirTowerDataContext? = state[psi]
|
||||||
this.state[statement]?.let { return it }
|
|
||||||
for ((key, value) in this.state) {
|
|
||||||
// TODO Rework that
|
|
||||||
if (key.psi == statement.psi) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
-160
@@ -1,160 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.idea.fir.low.level.api.element.builder
|
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
|
||||||
import org.jetbrains.kotlin.fir.psi
|
|
||||||
import org.jetbrains.kotlin.fir.realPsi
|
|
||||||
import org.jetbrains.kotlin.fir.references.*
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
|
|
||||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.isErrorElement
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Belongs to a [org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveState]
|
|
||||||
*/
|
|
||||||
internal class PsiToFirCache(private val moduleFileCache: ModuleFileCache) {
|
|
||||||
private val caches = ConcurrentHashMap<KtFile, FileCache>()
|
|
||||||
|
|
||||||
fun getCachedMapping(element: KtElement): FirElement? {
|
|
||||||
val ktFile = element.containingKtFile
|
|
||||||
val cache = caches[ktFile]
|
|
||||||
return cache?.getCachedMapping(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFir(element: KtElement, containerFir: FirDeclaration, firFile: FirFile): FirElement {
|
|
||||||
val ktFile = element.containingKtFile
|
|
||||||
val cache = caches.getOrPut(ktFile) { FileCache(ktFile, firFile, moduleFileCache) }
|
|
||||||
return cache.getFir(element, containerFir)
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo for completion only
|
|
||||||
fun recordElementsForCompletionFrom(containerFir: FirDeclaration, firFile: FirFile, ktFile: KtFile) {
|
|
||||||
val cache = caches.getOrPut(ktFile) { FileCache(ktFile, firFile, moduleFileCache) }
|
|
||||||
cache.recordElementsForCompletionFrom(containerFir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class FileCache(val ktFile: KtFile, firFile: FirFile, moduleFileCache: ModuleFileCache) {
|
|
||||||
private val cache: ConcurrentHashMap<KtElement, FirElement> = ConcurrentHashMap()
|
|
||||||
|
|
||||||
fun getCachedMapping(ktElement: KtElement): FirElement? {
|
|
||||||
require(ktElement.containingKtFile === ktFile)
|
|
||||||
return cache[ktElement]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFir(element: KtElement, containerFir: FirDeclaration): FirElement {
|
|
||||||
val psi = when {
|
|
||||||
element is KtPropertyDelegate -> element.expression ?: element
|
|
||||||
element is KtQualifiedExpression && element.selectorExpression is KtCallExpression -> {
|
|
||||||
/*
|
|
||||||
KtQualifiedExpression with KtCallExpression in selector transformed in FIR to FirFunctionCall expression
|
|
||||||
Which will have a receiver as qualifier
|
|
||||||
*/
|
|
||||||
element.selectorExpression ?: error("Incomplete code:\n${element.getElementTextInContext()}")
|
|
||||||
}
|
|
||||||
else -> element
|
|
||||||
}
|
|
||||||
cache[psi]?.let { return it }
|
|
||||||
recordElementsFrom(containerFir)
|
|
||||||
|
|
||||||
val (current, mappedFir) = psi.getFirOfClosestParent()
|
|
||||||
?: error("FirElement is not found for:\n${element.getElementTextInContext()}")
|
|
||||||
if (current !== element) {
|
|
||||||
cache(current, mappedFir)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mappedFir
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cache(psi: KtElement, fir: FirElement) {
|
|
||||||
// todo make it thread safe
|
|
||||||
val existingFir = cache[psi]
|
|
||||||
if (existingFir != null && existingFir !== fir) {
|
|
||||||
when {
|
|
||||||
existingFir is FirTypeRef && fir is FirTypeRef && psi is KtTypeReference -> {
|
|
||||||
// FirTypeRefs are often created during resolve
|
|
||||||
// a lot of them with have the same source
|
|
||||||
// we want to take the most "resolved one" here
|
|
||||||
if (fir is FirResolvedTypeRefImpl && existingFir !is FirResolvedTypeRefImpl) {
|
|
||||||
cache[psi] = fir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
existingFir.isErrorElement && !fir.isErrorElement -> {
|
|
||||||
// TODO better handle error elements
|
|
||||||
// but for now just take first non-error one if such exist
|
|
||||||
cache[psi] = fir
|
|
||||||
}
|
|
||||||
existingFir.isErrorElement || fir.isErrorElement -> {
|
|
||||||
// do nothing and maybe upgrade to a non-error element in the branch above in the future
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
if (DuplicatedFirSourceElementsException.IS_ENABLED) {
|
|
||||||
throw DuplicatedFirSourceElementsException(existingFir, fir, psi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (existingFir == null) {
|
|
||||||
cache[psi] = fir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun recordElementsForCompletionFrom(firDeclaration: FirDeclaration) {
|
|
||||||
recordElementsFrom(firDeclaration)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recordElementsFrom(firDeclaration: FirDeclaration) {
|
|
||||||
firDeclaration.accept(elementsRecorder)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val elementsRecorder = object : FirVisitorVoid() {
|
|
||||||
override fun visitElement(element: FirElement) {
|
|
||||||
(element.realPsi as? KtElement)?.let { psi ->
|
|
||||||
cache(psi, element)
|
|
||||||
}
|
|
||||||
element.acceptChildren(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitReference(reference: FirReference) {}
|
|
||||||
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference) {}
|
|
||||||
override fun visitNamedReference(namedReference: FirNamedReference) {}
|
|
||||||
override fun visitResolvedNamedReference(resolvedNamedReference: FirResolvedNamedReference) {}
|
|
||||||
override fun visitDelegateFieldReference(delegateFieldReference: FirDelegateFieldReference) {}
|
|
||||||
override fun visitBackingFieldReference(backingFieldReference: FirBackingFieldReference) {}
|
|
||||||
override fun visitSuperReference(superReference: FirSuperReference) {}
|
|
||||||
override fun visitThisReference(thisReference: FirThisReference) {}
|
|
||||||
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef) {}
|
|
||||||
|
|
||||||
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef) {
|
|
||||||
userTypeRef.acceptChildren(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtElement.getFirOfClosestParent(): Pair<KtElement, FirElement>? {
|
|
||||||
var current: PsiElement? = this
|
|
||||||
while (current is KtElement) {
|
|
||||||
val mappedFir = cache[current]
|
|
||||||
if (mappedFir != null) {
|
|
||||||
return current to mappedFir
|
|
||||||
}
|
|
||||||
current = current.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.file.structure
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.findNonLocalFirDeclaration
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.hasExplicitTypeOrUnit
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.replaceFirst
|
||||||
|
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
|
||||||
|
internal class FileStructure(
|
||||||
|
private val ktFile: KtFile,
|
||||||
|
private val firFile: FirFile,
|
||||||
|
private val firLazyDeclarationResolver: FirLazyDeclarationResolver,
|
||||||
|
private val firFileBuilder: FirFileBuilder,
|
||||||
|
private val moduleFileCache: ModuleFileCache
|
||||||
|
) {
|
||||||
|
private val firIdeProvider = firFile.session.firIdeProvider
|
||||||
|
|
||||||
|
private val structureElements = ConcurrentHashMap<KtAnnotated, FileStructureElement>()
|
||||||
|
|
||||||
|
fun getStructureElementFor(element: KtElement): FileStructureElement {
|
||||||
|
val container: KtAnnotated = element.getNonLocalContainingOrThisDeclaration() ?: element.containingKtFile
|
||||||
|
return getStructureElementForDeclaration(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStructureElementForDeclaration(declaration: KtAnnotated): FileStructureElement {
|
||||||
|
val structureElement = structureElements.compute(declaration) { _, structureElement ->
|
||||||
|
when {
|
||||||
|
structureElement == null -> createStructureElement(declaration)
|
||||||
|
structureElement is WithInBlockModificationFileStructureElement && !structureElement.isUpToDate() -> {
|
||||||
|
createMappingsCopy(structureElement, declaration as KtNamedFunction)
|
||||||
|
}
|
||||||
|
else -> structureElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return structureElement
|
||||||
|
?: error("FileStructureElement for was not defined for \n${declaration.getElementTextInContext()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
fun getAllDiagnosticsForFile(): Collection<Diagnostic> {
|
||||||
|
val containersForStructureElement = buildList {
|
||||||
|
add(ktFile)
|
||||||
|
ktFile.forEachDescendantOfType<KtDeclaration>(
|
||||||
|
canGoInside = { psi -> psi !is KtFunction && psi !is KtValVarKeywordOwner }
|
||||||
|
) { declaration ->
|
||||||
|
if (declaration.isStructureElementContainer()) {
|
||||||
|
add(declaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val structureElements = containersForStructureElement.map(::getStructureElementFor)
|
||||||
|
return buildList {
|
||||||
|
structureElements.forEach { it.diagnostics.forEach { diagnostics -> addAll(diagnostics) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replaceFunction(from: FirSimpleFunction, to: FirSimpleFunction) {
|
||||||
|
val declarations = if (from.symbol.callableId.className == null) {
|
||||||
|
firFile.declarations as MutableList<FirDeclaration>
|
||||||
|
} else {
|
||||||
|
val classId = from.symbol.callableId.classId
|
||||||
|
?: error("Class name should not be null for non-top-level & non-local declarations")
|
||||||
|
val containingClass = firIdeProvider.getFirClassifierByFqName(classId) as FirRegularClass
|
||||||
|
containingClass.declarations as MutableList<FirDeclaration>
|
||||||
|
}
|
||||||
|
declarations.replaceFirst(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMappingsCopy(
|
||||||
|
original: WithInBlockModificationFileStructureElement,
|
||||||
|
containerKtFunction: KtNamedFunction
|
||||||
|
): WithInBlockModificationFileStructureElement {
|
||||||
|
val newFunction = firIdeProvider.buildFunctionWithBody(containerKtFunction) as FirSimpleFunction
|
||||||
|
val originalFunction = original.firSymbol.fir as FirSimpleFunction
|
||||||
|
replaceFunction(originalFunction, newFunction)
|
||||||
|
|
||||||
|
try {
|
||||||
|
firLazyDeclarationResolver.lazyResolveDeclaration(
|
||||||
|
newFunction,
|
||||||
|
moduleFileCache,
|
||||||
|
FirResolvePhase.BODY_RESOLVE,
|
||||||
|
checkPCE = true,
|
||||||
|
reresolveFile = true,
|
||||||
|
)
|
||||||
|
return WithInBlockModificationFileStructureElement(
|
||||||
|
firFile,
|
||||||
|
containerKtFunction,
|
||||||
|
newFunction.symbol,
|
||||||
|
containerKtFunction.modificationStamp,
|
||||||
|
)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
replaceFunction(newFunction, originalFunction)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDeclarationStructure(declaration: KtDeclaration): FileStructureElement {
|
||||||
|
val firDeclaration = declaration.findNonLocalFirDeclaration(firFileBuilder, firIdeProvider, moduleFileCache)
|
||||||
|
firLazyDeclarationResolver.lazyResolveDeclaration(
|
||||||
|
firDeclaration,
|
||||||
|
moduleFileCache,
|
||||||
|
FirResolvePhase.BODY_RESOLVE,
|
||||||
|
checkPCE = true
|
||||||
|
)
|
||||||
|
return when {
|
||||||
|
declaration is KtNamedFunction && declaration.hasExplicitTypeOrUnit -> {
|
||||||
|
WithInBlockModificationFileStructureElement(
|
||||||
|
firFile,
|
||||||
|
declaration,
|
||||||
|
(firDeclaration as FirSimpleFunction).symbol,
|
||||||
|
declaration.modificationStamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
NonLocalDeclarationFileStructureElement(
|
||||||
|
firFile,
|
||||||
|
firDeclaration,
|
||||||
|
declaration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStructureElement(container: KtAnnotated): FileStructureElement = when (container) {
|
||||||
|
is KtFile -> {
|
||||||
|
val firFile = firFileBuilder.getFirFileResolvedToPhaseWithCaching(
|
||||||
|
container,
|
||||||
|
moduleFileCache,
|
||||||
|
FirResolvePhase.BODY_RESOLVE,
|
||||||
|
checkPCE = true
|
||||||
|
)
|
||||||
|
FileWithoutDeclarationsFileStructureElement(
|
||||||
|
firFile,
|
||||||
|
container,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is KtDeclaration -> createDeclarationStructure(container)
|
||||||
|
else -> error("Invalid container $container")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtDeclaration.isStructureElementContainer(): Boolean {
|
||||||
|
if (this !is KtClassOrObject && this !is KtDeclarationWithBody && this !is KtProperty && this !is KtTypeAlias) return false
|
||||||
|
if (this is KtEnumEntry) return false
|
||||||
|
if (containingClassOrObject is KtEnumEntry) return false
|
||||||
|
return !KtPsiUtil.isLocal(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.file.structure
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.lazy.resolve.FirLazyDeclarationResolver
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Belongs to a [org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveState]
|
||||||
|
*/
|
||||||
|
internal class FileStructureCache(
|
||||||
|
private val fileBuilder: FirFileBuilder,
|
||||||
|
private val firLazyDeclarationResolver: FirLazyDeclarationResolver,
|
||||||
|
) {
|
||||||
|
private val cache = ConcurrentHashMap<KtFile, FileStructure>()
|
||||||
|
|
||||||
|
fun getFileStructure(ktFile: KtFile, moduleFileCache: ModuleFileCache): FileStructure = cache.computeIfAbsent(ktFile) {
|
||||||
|
val firFile = fileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache)
|
||||||
|
FileStructure(ktFile, firFile, firLazyDeclarationResolver, fileBuilder, moduleFileCache)
|
||||||
|
}
|
||||||
|
}
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.file.structure
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||||
|
import org.jetbrains.kotlin.fir.psi
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.diagnostics.FirIdeStructureElementDiagnosticsCollector
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.hasExplicitTypeOrUnit
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfTypeTo
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
|
internal class FileStructureElementDiagnostics(
|
||||||
|
private val map: Map<KtElement, List<Diagnostic>>
|
||||||
|
) {
|
||||||
|
fun diagnosticsFor(element: KtElement): List<Diagnostic> = map[element] ?: emptyList()
|
||||||
|
|
||||||
|
inline fun forEach(action: (List<Diagnostic>) -> Unit) = map.values.forEach(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class FileStructureElement {
|
||||||
|
abstract val firFile: FirFile
|
||||||
|
abstract val psi: KtAnnotated
|
||||||
|
abstract val mappings: Map<KtElement, FirElement>
|
||||||
|
abstract val diagnostics: FileStructureElementDiagnostics
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class WithInBlockModificationFileStructureElement(
|
||||||
|
override val firFile: FirFile,
|
||||||
|
override val psi: KtFunction,
|
||||||
|
val firSymbol: FirFunctionSymbol<*>,
|
||||||
|
val timestamp: Long
|
||||||
|
) : FileStructureElement() {
|
||||||
|
|
||||||
|
override val mappings: Map<KtElement, FirElement> =
|
||||||
|
FirElementsRecorder.recordElementsFrom(firSymbol.fir, recorder)
|
||||||
|
|
||||||
|
|
||||||
|
fun isUpToDate(): Boolean = psi.getModificationStamp() == timestamp
|
||||||
|
|
||||||
|
override val diagnostics: FileStructureElementDiagnostics by lazy {
|
||||||
|
val innerDeclarations = psi
|
||||||
|
.collectDescendantsOfTypeTo<KtDeclaration, HashSet<KtDeclaration>>(
|
||||||
|
hashSetOf(psi),
|
||||||
|
canGoInside = { true }
|
||||||
|
)
|
||||||
|
FirIdeStructureElementDiagnosticsCollector.collectForStructureElement(
|
||||||
|
innerDeclarations,
|
||||||
|
firFile,
|
||||||
|
substitution = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val recorder = FirElementsRecorder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NonLocalDeclarationFileStructureElement(
|
||||||
|
override val firFile: FirFile,
|
||||||
|
fir: FirDeclaration,
|
||||||
|
override val psi: KtDeclaration,
|
||||||
|
) : FileStructureElement() {
|
||||||
|
override val mappings: Map<KtElement, FirElement> =
|
||||||
|
FirElementsRecorder.recordElementsFrom(fir, recorder)
|
||||||
|
|
||||||
|
override val diagnostics: FileStructureElementDiagnostics by lazy {
|
||||||
|
@Suppress("RemoveExplicitTypeArguments")
|
||||||
|
val innerDeclarations = psi
|
||||||
|
.collectDescendantsOfTypeTo<KtDeclaration, HashSet<KtDeclaration>>(
|
||||||
|
hashSetOf(psi),
|
||||||
|
canGoInside = { it.safeAs<KtNamedFunction>()?.hasExplicitTypeOrUnit != true }
|
||||||
|
)
|
||||||
|
|
||||||
|
FirIdeStructureElementDiagnosticsCollector.collectForStructureElement(innerDeclarations, firFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val recorder = object : FirElementsRecorder() {
|
||||||
|
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: MutableMap<KtElement, FirElement>) {
|
||||||
|
val psi = simpleFunction.psi as? KtNamedFunction ?: return super.visitSimpleFunction(simpleFunction, data)
|
||||||
|
if (!psi.hasExplicitTypeOrUnit || KtPsiUtil.isLocal(psi)) {
|
||||||
|
super.visitSimpleFunction(simpleFunction, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal data class FileWithoutDeclarationsFileStructureElement(
|
||||||
|
override val firFile: FirFile,
|
||||||
|
override val psi: KtFile,
|
||||||
|
) : FileStructureElement() {
|
||||||
|
override val mappings: Map<KtElement, FirElement> =
|
||||||
|
FirElementsRecorder.recordElementsFrom(firFile, recorder)
|
||||||
|
|
||||||
|
override val diagnostics: FileStructureElementDiagnostics by lazy {
|
||||||
|
FirIdeStructureElementDiagnosticsCollector.collectForStructureElement(hashSetOf(psi), firFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val recorder = object : FirElementsRecorder() {
|
||||||
|
override fun visitElement(element: FirElement, data: MutableMap<KtElement, FirElement>) {
|
||||||
|
if (element !is FirDeclaration || element is FirFile) {
|
||||||
|
super.visitElement(element, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.file.structure
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.realPsi
|
||||||
|
import org.jetbrains.kotlin.fir.references.*
|
||||||
|
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
|
||||||
|
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||||
|
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
|
||||||
|
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||||
|
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.DuplicatedFirSourceElementsException
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.isErrorElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||||
|
|
||||||
|
internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement, FirElement>>() {
|
||||||
|
private fun cache(psi: KtElement, fir: FirElement, cache: MutableMap<KtElement, FirElement>) {
|
||||||
|
val existingFir = cache[psi]
|
||||||
|
if (existingFir != null && existingFir !== fir) {
|
||||||
|
when {
|
||||||
|
existingFir is FirTypeRef && fir is FirTypeRef && psi is KtTypeReference -> {
|
||||||
|
// FirTypeRefs are often created during resolve
|
||||||
|
// a lot of them with have the same source
|
||||||
|
// we want to take the most "resolved one" here
|
||||||
|
if (fir is FirResolvedTypeRefImpl && existingFir !is FirResolvedTypeRefImpl) {
|
||||||
|
cache[psi] = fir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
existingFir.isErrorElement && !fir.isErrorElement -> {
|
||||||
|
// TODO better handle error elements
|
||||||
|
// but for now just take first non-error one if such exist
|
||||||
|
cache[psi] = fir
|
||||||
|
}
|
||||||
|
existingFir.isErrorElement || fir.isErrorElement -> {
|
||||||
|
// do nothing and maybe upgrade to a non-error element in the branch above in the future
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (DuplicatedFirSourceElementsException.IS_ENABLED) {
|
||||||
|
throw DuplicatedFirSourceElementsException(existingFir, fir, psi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (existingFir == null) {
|
||||||
|
cache[psi] = fir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitElement(element: FirElement, data: MutableMap<KtElement, FirElement>) {
|
||||||
|
(element.realPsi as? KtElement)?.let { psi ->
|
||||||
|
cache(psi, element, data)
|
||||||
|
}
|
||||||
|
element.acceptChildren(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
override fun visitReference(reference: FirReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitControlFlowGraphReference(controlFlowGraphReference: FirControlFlowGraphReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitNamedReference(namedReference: FirNamedReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitResolvedNamedReference(resolvedNamedReference: FirResolvedNamedReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitDelegateFieldReference(delegateFieldReference: FirDelegateFieldReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitBackingFieldReference(backingFieldReference: FirBackingFieldReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitSuperReference(superReference: FirSuperReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitThisReference(thisReference: FirThisReference, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
override fun visitErrorTypeRef(errorTypeRef: FirErrorTypeRef, data: MutableMap<KtElement, FirElement>) {}
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
override fun visitUserTypeRef(userTypeRef: FirUserTypeRef, data: MutableMap<KtElement, FirElement>) {
|
||||||
|
userTypeRef.acceptChildren(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
fun recordElementsFrom(firDeclaration: FirDeclaration, recorder: FirElementsRecorder): Map<KtElement, FirElement> =
|
||||||
|
buildMap { firDeclaration.accept(recorder, this) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-9
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
|||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirDesignatedBodyResolveTransformerForIDE
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirDesignatedBodyResolveTransformerForIDE
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingDeclarationWithFqName
|
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.getNonLocalContainingOrThisDeclaration
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
|
||||||
@@ -31,12 +31,14 @@ internal class FirLazyDeclarationResolver(
|
|||||||
moduleFileCache: ModuleFileCache,
|
moduleFileCache: ModuleFileCache,
|
||||||
toPhase: FirResolvePhase,
|
toPhase: FirResolvePhase,
|
||||||
towerDataContextCollector: FirTowerDataContextCollector? = null,
|
towerDataContextCollector: FirTowerDataContextCollector? = null,
|
||||||
checkPCE: Boolean = false
|
checkPCE: Boolean = false,
|
||||||
|
reresolveFile: Boolean = false,
|
||||||
) {
|
) {
|
||||||
if (declaration.resolvePhase >= toPhase) return
|
if (declaration.resolvePhase >= toPhase) return
|
||||||
val firFile = declaration.getContainingFile()
|
val firFile = declaration.getContainingFile()
|
||||||
?: error("FirFile was not found for\n${declaration.render()}")
|
?: error("FirFile was not found for\n${declaration.render()}")
|
||||||
val provider = firFile.session.firIdeProvider
|
val provider = firFile.session.firIdeProvider
|
||||||
|
val fromPhase = if (reresolveFile) declaration.resolvePhase else minOf(firFile.resolvePhase, declaration.resolvePhase)
|
||||||
if (checkPCE) {
|
if (checkPCE) {
|
||||||
firFileBuilder.runCustomResolveWithPCECheck(firFile, moduleFileCache) {
|
firFileBuilder.runCustomResolveWithPCECheck(firFile, moduleFileCache) {
|
||||||
runLazyResolveWithoutLock(
|
runLazyResolveWithoutLock(
|
||||||
@@ -44,6 +46,7 @@ internal class FirLazyDeclarationResolver(
|
|||||||
moduleFileCache,
|
moduleFileCache,
|
||||||
firFile,
|
firFile,
|
||||||
provider,
|
provider,
|
||||||
|
fromPhase,
|
||||||
toPhase,
|
toPhase,
|
||||||
towerDataContextCollector,
|
towerDataContextCollector,
|
||||||
checkPCE = true
|
checkPCE = true
|
||||||
@@ -57,6 +60,7 @@ internal class FirLazyDeclarationResolver(
|
|||||||
moduleFileCache,
|
moduleFileCache,
|
||||||
firFile,
|
firFile,
|
||||||
provider,
|
provider,
|
||||||
|
fromPhase,
|
||||||
toPhase,
|
toPhase,
|
||||||
towerDataContextCollector,
|
towerDataContextCollector,
|
||||||
checkPCE = false
|
checkPCE = false
|
||||||
@@ -71,16 +75,17 @@ internal class FirLazyDeclarationResolver(
|
|||||||
moduleFileCache: ModuleFileCache,
|
moduleFileCache: ModuleFileCache,
|
||||||
containerFirFile: FirFile,
|
containerFirFile: FirFile,
|
||||||
provider: FirProvider,
|
provider: FirProvider,
|
||||||
|
fromPhase: FirResolvePhase,
|
||||||
toPhase: FirResolvePhase,
|
toPhase: FirResolvePhase,
|
||||||
towerDataContextCollector: FirTowerDataContextCollector? = null,
|
towerDataContextCollector: FirTowerDataContextCollector? = null,
|
||||||
checkPCE: Boolean
|
checkPCE: Boolean,
|
||||||
) {
|
) {
|
||||||
if (firDeclarationToResolve.resolvePhase >= toPhase) return
|
if (fromPhase >= toPhase) return
|
||||||
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
|
val nonLazyPhase = minOf(toPhase, FirResolvePhase.DECLARATIONS)
|
||||||
if (firDeclarationToResolve.resolvePhase < nonLazyPhase) {
|
if (fromPhase < nonLazyPhase) {
|
||||||
firFileBuilder.runResolveWithoutLock(
|
firFileBuilder.runResolveWithoutLock(
|
||||||
containerFirFile,
|
containerFirFile,
|
||||||
fromPhase = firDeclarationToResolve.resolvePhase,
|
fromPhase = fromPhase,
|
||||||
toPhase = nonLazyPhase,
|
toPhase = nonLazyPhase,
|
||||||
checkPCE = checkPCE
|
checkPCE = checkPCE
|
||||||
)
|
)
|
||||||
@@ -106,7 +111,7 @@ internal class FirLazyDeclarationResolver(
|
|||||||
is FirCallableDeclaration<*> -> {
|
is FirCallableDeclaration<*> -> {
|
||||||
nonLocalDeclarationToResolve.symbol.callableId.classId
|
nonLocalDeclarationToResolve.symbol.callableId.classId
|
||||||
}
|
}
|
||||||
is FirRegularClass -> {
|
is FirClassLikeDeclaration<*> -> {
|
||||||
nonLocalDeclarationToResolve.symbol.classId
|
nonLocalDeclarationToResolve.symbol.classId
|
||||||
}
|
}
|
||||||
else -> error("Unsupported: ${nonLocalDeclarationToResolve.render()}")
|
else -> error("Unsupported: ${nonLocalDeclarationToResolve.render()}")
|
||||||
@@ -126,7 +131,7 @@ internal class FirLazyDeclarationResolver(
|
|||||||
val transformer = FirDesignatedBodyResolveTransformerForIDE(
|
val transformer = FirDesignatedBodyResolveTransformerForIDE(
|
||||||
designation.iterator(), containerFirFile.session,
|
designation.iterator(), containerFirFile.session,
|
||||||
scopeSession,
|
scopeSession,
|
||||||
implicitTypeOnly = toPhase == FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE,
|
toPhase,
|
||||||
towerDataContextCollector
|
towerDataContextCollector
|
||||||
)
|
)
|
||||||
executeWithoutPCE {
|
executeWithoutPCE {
|
||||||
@@ -138,7 +143,7 @@ internal class FirLazyDeclarationResolver(
|
|||||||
if (this is FirFile) return this
|
if (this is FirFile) return this
|
||||||
val ktDeclaration = psi as? KtDeclaration ?: error("FirDeclaration should have a PSI of type KtDeclaration")
|
val ktDeclaration = psi as? KtDeclaration ?: error("FirDeclaration should have a PSI of type KtDeclaration")
|
||||||
if (!KtPsiUtil.isLocal(ktDeclaration)) return this
|
if (!KtPsiUtil.isLocal(ktDeclaration)) return this
|
||||||
val nonLocalPsi = ktDeclaration.getNonLocalContainingDeclarationWithFqName()
|
val nonLocalPsi = ktDeclaration.getNonLocalContainingOrThisDeclaration()
|
||||||
?: error("Container for local declaration cannot be null")
|
?: error("Container for local declaration cannot be null")
|
||||||
return nonLocalPsi.findNonLocalFirDeclaration(firFileBuilder, provider, moduleFileCache)
|
return nonLocalPsi.findNonLocalFirDeclaration(firFileBuilder, provider, moduleFileCache)
|
||||||
}
|
}
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.util
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
internal inline fun <K, V> MutableMap<K, MutableList<V>>.addValueFor(element: K, value: V) {
|
||||||
|
getOrPut(element) { mutableListOf() } += value
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun <T> MutableList<T>.replaceFirst(from: T, to: T) {
|
||||||
|
val index = indexOf(from)
|
||||||
|
if (index < 0) {
|
||||||
|
error("$from was not found in $this")
|
||||||
|
}
|
||||||
|
set(index, to)
|
||||||
|
}
|
||||||
+27
-7
@@ -7,11 +7,13 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.util
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
|
||||||
import org.jetbrains.kotlin.fir.psi
|
import org.jetbrains.kotlin.fir.psi
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.FirFileBuilder
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
import org.jetbrains.kotlin.idea.fir.low.level.api.file.builder.ModuleFileCache
|
||||||
import org.jetbrains.kotlin.idea.util.classIdIfNonLocal
|
import org.jetbrains.kotlin.idea.util.classIdIfNonLocal
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
|
|
||||||
@@ -25,27 +27,45 @@ internal fun KtDeclaration.findNonLocalFirDeclaration(
|
|||||||
this is KtClassOrObject -> findFir(provider)
|
this is KtClassOrObject -> findFir(provider)
|
||||||
this is KtNamedDeclaration && (this is KtProperty || this is KtNamedFunction) -> {
|
this is KtNamedDeclaration && (this is KtProperty || this is KtNamedFunction) -> {
|
||||||
val containerClass = containingClassOrObject
|
val containerClass = containingClassOrObject
|
||||||
if (containerClass != null) {
|
val declarations = if (containerClass != null) {
|
||||||
val containerClassFir = containerClass.findFir(provider)
|
val containerClassFir = containerClass.findFir(provider)
|
||||||
containerClassFir.declarations.first { it.psi === this }
|
containerClassFir.declarations
|
||||||
} else {
|
} else {
|
||||||
val ktFile = containingKtFile
|
val ktFile = containingKtFile
|
||||||
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache)
|
val firFile = firFileBuilder.buildRawFirFileWithCaching(ktFile, moduleFileCache)
|
||||||
firFile.declarations.first { it.psi === this }
|
firFile.declarations
|
||||||
}
|
}
|
||||||
|
val original = originalDeclaration
|
||||||
|
declarations.first { it.psi === this || it.psi == original }
|
||||||
}
|
}
|
||||||
this is KtConstructor<*> -> {
|
this is KtConstructor<*> -> {
|
||||||
val containerClassFir = containingClassOrObject?.findFir(provider)
|
val containerClassFir = containingClassOrObject?.findFir(provider)
|
||||||
?: error("Container class should be not null for KtConstructor")
|
?: error("Container class should be not null for KtConstructor")
|
||||||
containerClassFir.declarations.first { it.psi === this }
|
containerClassFir.declarations.first { it.psi === this }
|
||||||
}
|
}
|
||||||
else -> error("Invalid container $this")
|
this is KtTypeAlias -> findFir(provider)
|
||||||
|
else -> error("Invalid container $this::class")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val ORIGINAL_DECLARATION_KEY = com.intellij.openapi.util.Key<KtDeclaration>("ORIGINAL_DECLARATION_KEY")
|
||||||
|
|
||||||
|
var KtDeclaration.originalDeclaration by UserDataProperty(ORIGINAL_DECLARATION_KEY)
|
||||||
|
|
||||||
|
|
||||||
private fun KtClassOrObject.findFir(provider: FirProvider): FirRegularClass {
|
private fun KtClassOrObject.findFir(provider: FirProvider): FirRegularClass {
|
||||||
val containerClassId = classIdIfNonLocal()
|
val classId = classIdIfNonLocal()
|
||||||
?: error("Container classId should not be null for non-local declaration")
|
?: error("Container classId should not be null for non-local declaration")
|
||||||
return provider.getFirClassifierByFqName(containerClassId) as? FirRegularClass
|
return executeWithoutPCE {
|
||||||
?: error("Could not find class $containerClassId")
|
provider.getFirClassifierByFqName(classId) as? FirRegularClass
|
||||||
|
?: error("Could not find class $classId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtTypeAlias.findFir(provider: FirProvider): FirTypeAlias {
|
||||||
|
val typeAlias = ClassId(containingKtFile.packageFqName, nameAsSafeName)
|
||||||
|
return executeWithoutPCE {
|
||||||
|
provider.getFirClassifierByFqName(typeAlias) as? FirTypeAlias
|
||||||
|
?: error("Could not find type alias $typeAlias")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* 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.idea.fir.low.level.api.util
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
|
||||||
|
internal val KtNamedFunction.hasExplicitTypeOrUnit
|
||||||
|
get() = hasBlockBody() || typeReference != null
|
||||||
-4
@@ -52,10 +52,6 @@ internal inline fun checkCanceled() {
|
|||||||
internal val FirElement.isErrorElement
|
internal val FirElement.isErrorElement
|
||||||
get() = this is FirDiagnosticHolder
|
get() = this is FirDiagnosticHolder
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
internal inline fun <K, V> MutableMap<K, MutableList<V>>.addValueFor(element: K, value: V) {
|
|
||||||
getOrPut(element) { mutableListOf() } += value
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun IdeaModuleInfo.collectTransitiveDependenciesWithSelf(): List<IdeaModuleInfo> {
|
internal fun IdeaModuleInfo.collectTransitiveDependenciesWithSelf(): List<IdeaModuleInfo> {
|
||||||
val result = mutableSetOf<IdeaModuleInfo>()
|
val result = mutableSetOf<IdeaModuleInfo>()
|
||||||
|
|||||||
+1
-1
@@ -128,7 +128,7 @@ internal class KtFirScopeProvider(
|
|||||||
originalFile: KtFile,
|
originalFile: KtFile,
|
||||||
positionInFakeFile: KtElement
|
positionInFakeFile: KtElement
|
||||||
): LowLevelFirApiFacade.FirCompletionContext {
|
): LowLevelFirApiFacade.FirCompletionContext {
|
||||||
val originalFirFile = originalFile.getOrBuildFirOfType<FirFile>(firResolveState)
|
val originalFirFile = firResolveState.getFirFile(originalFile)
|
||||||
val declarationContext = EnclosingDeclarationContext.detect(originalFile, positionInFakeFile)
|
val declarationContext = EnclosingDeclarationContext.detect(originalFile, positionInFakeFile)
|
||||||
|
|
||||||
return declarationContext.buildCompletionContext(originalFirFile, firResolveState)
|
return declarationContext.buildCompletionContext(originalFirFile, firResolveState)
|
||||||
|
|||||||
+3
-1
@@ -9,6 +9,7 @@ import com.intellij.psi.util.parentsOfType
|
|||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveState
|
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveState
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
||||||
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalDeclaration
|
||||||
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
import org.jetbrains.kotlin.idea.util.getElementTextInContext
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
@@ -20,7 +21,7 @@ internal sealed class EnclosingDeclarationContext {
|
|||||||
if (fakeFunction != null) {
|
if (fakeFunction != null) {
|
||||||
val originalFunction = originalFile.findDeclarationOfTypeAt<KtNamedFunction>(fakeFunction.textOffset)
|
val originalFunction = originalFile.findDeclarationOfTypeAt<KtNamedFunction>(fakeFunction.textOffset)
|
||||||
?: error("Cannot find original function matching to ${fakeFunction.getElementTextInContext()} in $originalFile")
|
?: error("Cannot find original function matching to ${fakeFunction.getElementTextInContext()} in $originalFile")
|
||||||
|
fakeFunction.originalDeclaration = originalFunction
|
||||||
return FunctionContext(fakeFunction, originalFunction)
|
return FunctionContext(fakeFunction, originalFunction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ internal sealed class EnclosingDeclarationContext {
|
|||||||
if (fakeProperty != null) {
|
if (fakeProperty != null) {
|
||||||
val originalProperty = originalFile.findDeclarationOfTypeAt<KtProperty>(fakeProperty.textOffset)
|
val originalProperty = originalFile.findDeclarationOfTypeAt<KtProperty>(fakeProperty.textOffset)
|
||||||
?: error("Cannot find original property matching to ${fakeProperty.getElementTextInContext()} in $originalFile")
|
?: error("Cannot find original property matching to ${fakeProperty.getElementTextInContext()} in $originalFile")
|
||||||
|
fakeProperty.originalDeclaration = originalProperty
|
||||||
|
|
||||||
return PropertyContext(fakeProperty, originalProperty)
|
return PropertyContext(fakeProperty, originalProperty)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user