FIR IDE: separate LowLevelFirApiFacade from completion

This commit is contained in:
Ilya Kirillov
2020-09-22 11:53:37 +03:00
parent 7cc714c598
commit 8e41384f6d
6 changed files with 158 additions and 140 deletions
@@ -5,157 +5,22 @@
package org.jetbrains.kotlin.idea.fir.low.level.api.api
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyAccessorCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.FirIdeProvider
import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.KtProperty
object LowLevelFirApiFacade {
fun getResolveStateFor(element: KtElement): FirModuleResolveState =
element.firResolveState()
fun getResolveStateForCompletion(element: KtElement, originalState: FirModuleResolveState): FirModuleResolveState {
check(originalState is FirModuleResolveStateImpl)
return FirModuleResolveStateForCompletion(originalState)
}
fun getSessionFor(element: KtElement): FirSession =
getResolveStateFor(element).getSessionFor(element.getModuleInfo())
fun getOrBuildFirFor(element: KtElement, resolveState: FirModuleResolveState): FirElement =
resolveState.getOrBuildFirFor(element)
class FirCompletionContext internal constructor(
val session: FirSession,
private val towerDataContextCollector: FirTowerDataContextCollector,
) {
fun getTowerDataContext(element: KtElement): FirTowerDataContext {
var current: PsiElement? = element
while (current is KtElement) {
towerDataContextCollector.getContext(current)?.let { return it }
current = current.parent
}
error("No context for ${element.getElementTextInContext()}")
}
}
fun buildCompletionContextForFunction(
firFile: FirFile,
fakeElement: KtNamedFunction,
originalElement: KtNamedFunction,
state: FirModuleResolveState,
): FirCompletionContext {
val firIdeProvider = firFile.session.firIdeProvider
val originalFunction = state.getOrBuildFirFor(originalElement) as FirSimpleFunction
val copyFunction = buildFunctionCopyForCompletion(firIdeProvider, fakeElement, originalFunction, state)
val contextCollector = FirTowerDataContextCollector()
state.lazyResolveDeclarationForCompletion(copyFunction, firFile, firIdeProvider, FirResolvePhase.BODY_RESOLVE, contextCollector)
state.recordPsiToFirMappingsForCompletionFrom(copyFunction, firFile, fakeElement.containingKtFile)
return FirCompletionContext(
copyFunction.session,
contextCollector
)
}
fun buildCompletionContextForProperty(
firFile: FirFile,
fakeElement: KtProperty,
originalElement: KtProperty,
state: FirModuleResolveState,
): FirCompletionContext {
val firIdeProvider = firFile.session.firIdeProvider
val originalProperty = state.getOrBuildFirFor(originalElement) as FirProperty
val copyProperty = buildPropertyCopyForCompletion(firIdeProvider, fakeElement, originalProperty, state)
val contextCollector = FirTowerDataContextCollector()
state.lazyResolveDeclarationForCompletion(copyProperty, firFile, firIdeProvider, FirResolvePhase.BODY_RESOLVE, contextCollector)
state.recordPsiToFirMappingsForCompletionFrom(copyProperty, firFile, fakeElement.containingKtFile)
return FirCompletionContext(
copyProperty.session,
contextCollector
)
}
private fun buildFunctionCopyForCompletion(
firIdeProvider: FirIdeProvider,
element: KtNamedFunction,
originalFunction: FirSimpleFunction,
state: FirModuleResolveState
): FirSimpleFunction {
val builtFunction = firIdeProvider.buildFunctionWithBody(element)
// right now we can't resolve builtFunction header properly, as it built right in air,
// without file, which is now required for running stages other then body resolve, so we
// take original function header (which is resolved) and copy replacing body with body from builtFunction
return buildSimpleFunctionCopy(originalFunction) {
body = builtFunction.body
symbol = builtFunction.symbol as FirNamedFunctionSymbol
resolvePhase = minOf(originalFunction.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtFunction.source
session = state.rootModuleSession
}
}
private fun buildPropertyCopyForCompletion(
firIdeProvider: FirIdeProvider,
element: KtProperty,
originalProperty: FirProperty,
state: FirModuleResolveState
): FirProperty {
val builtProperty = firIdeProvider.buildPropertyWithBody(element)
val originalSetter = originalProperty.setter
val builtSetter = builtProperty.setter
// setter has a header with `value` parameter, and we want it type to be resolved
val copySetter = if (originalSetter != null && builtSetter != null) {
buildPropertyAccessorCopy(originalSetter) {
body = builtSetter.body
symbol = builtSetter.symbol
resolvePhase = minOf(builtSetter.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtSetter.source
session = state.rootModuleSession
}
} else {
builtSetter
}
return buildPropertyCopy(originalProperty) {
symbol = builtProperty.symbol
initializer = builtProperty.initializer
getter = builtProperty.getter
setter = copySetter
resolvePhase = minOf(originalProperty.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtProperty.source
session = state.rootModuleSession
}
}
fun getDiagnosticsFor(element: KtElement, resolveState: FirModuleResolveState): Collection<Diagnostic> {
return resolveState.getDiagnostics(element)
}
@@ -0,0 +1,149 @@
/*
* 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.api
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyAccessorCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.FirModuleResolveStateImpl
import org.jetbrains.kotlin.idea.fir.low.level.api.element.builder.FirTowerDataContextCollector
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.util.getElementTextInContext
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
object LowLevelFirApiFacadeForCompletion {
fun getResolveStateForCompletion(element: KtElement, originalState: FirModuleResolveState): FirModuleResolveState {
check(originalState is FirModuleResolveStateImpl)
return FirModuleResolveStateForCompletion(originalState)
}
class FirCompletionContext internal constructor(
val session: FirSession,
private val towerDataContextCollector: FirTowerDataContextCollector,
) {
fun getTowerDataContext(element: KtElement): FirTowerDataContext {
var current: PsiElement? = element
while (current is KtElement) {
towerDataContextCollector.getContext(current)?.let { return it }
current = current.parent
}
error("No context for ${element.getElementTextInContext()}")
}
}
fun buildCompletionContextForFunction(
firFile: FirFile,
fakeElement: KtNamedFunction,
originalElement: KtNamedFunction,
state: FirModuleResolveState,
): FirCompletionContext {
val firIdeProvider = firFile.session.firIdeProvider
val originalFunction = state.getOrBuildFirFor(originalElement) as FirSimpleFunction
val copyFunction = buildFunctionCopyForCompletion(firIdeProvider, fakeElement, originalFunction, state)
val contextCollector = FirTowerDataContextCollector()
state.lazyResolveDeclarationForCompletion(copyFunction, firFile, firIdeProvider, FirResolvePhase.BODY_RESOLVE, contextCollector)
state.recordPsiToFirMappingsForCompletionFrom(copyFunction, firFile, fakeElement.containingKtFile)
return FirCompletionContext(
copyFunction.session,
contextCollector
)
}
fun buildCompletionContextForProperty(
firFile: FirFile,
fakeElement: KtProperty,
originalElement: KtProperty,
state: FirModuleResolveState,
): FirCompletionContext {
val firIdeProvider = firFile.session.firIdeProvider
val originalProperty = state.getOrBuildFirFor(originalElement) as FirProperty
val copyProperty = buildPropertyCopyForCompletion(firIdeProvider, fakeElement, originalProperty, state)
val contextCollector = FirTowerDataContextCollector()
state.lazyResolveDeclarationForCompletion(copyProperty, firFile, firIdeProvider, FirResolvePhase.BODY_RESOLVE, contextCollector)
state.recordPsiToFirMappingsForCompletionFrom(copyProperty, firFile, fakeElement.containingKtFile)
return FirCompletionContext(
copyProperty.session,
contextCollector
)
}
private fun buildFunctionCopyForCompletion(
firIdeProvider: FirIdeProvider,
element: KtNamedFunction,
originalFunction: FirSimpleFunction,
state: FirModuleResolveState
): FirSimpleFunction {
val builtFunction = firIdeProvider.buildFunctionWithBody(element)
// right now we can't resolve builtFunction header properly, as it built right in air,
// without file, which is now required for running stages other then body resolve, so we
// take original function header (which is resolved) and copy replacing body with body from builtFunction
return buildSimpleFunctionCopy(originalFunction) {
body = builtFunction.body
symbol = builtFunction.symbol as FirNamedFunctionSymbol
resolvePhase = minOf(originalFunction.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtFunction.source
session = state.rootModuleSession
}
}
private fun buildPropertyCopyForCompletion(
firIdeProvider: FirIdeProvider,
element: KtProperty,
originalProperty: FirProperty,
state: FirModuleResolveState
): FirProperty {
val builtProperty = firIdeProvider.buildPropertyWithBody(element)
val originalSetter = originalProperty.setter
val builtSetter = builtProperty.setter
// setter has a header with `value` parameter, and we want it type to be resolved
val copySetter = if (originalSetter != null && builtSetter != null) {
buildPropertyAccessorCopy(originalSetter) {
body = builtSetter.body
symbol = builtSetter.symbol
resolvePhase = minOf(builtSetter.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtSetter.source
session = state.rootModuleSession
}
} else {
builtSetter
}
return buildPropertyCopy(originalProperty) {
symbol = builtProperty.symbol
initializer = builtProperty.initializer
getter = builtProperty.getter
setter = copySetter
resolvePhase = minOf(originalProperty.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtProperty.source
session = state.rootModuleSession
}
}
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCompletion
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.ReadActionConfinementValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
@@ -47,7 +48,7 @@ private constructor(
override fun createContextDependentCopy(): KtAnalysisSession {
check(!isContextSession) { "Cannot create context-dependent copy of KtAnalysis session from a context dependent one" }
val contextResolveState = LowLevelFirApiFacade.getResolveStateForCompletion(element, firResolveState)
val contextResolveState = LowLevelFirApiFacadeForCompletion.getResolveStateForCompletion(element, firResolveState)
return KtFirAnalysisSession(
element,
contextResolveState,
@@ -38,7 +38,8 @@ internal class KtFirCompletionCandidateChecker(
) : KtCompletionCandidateChecker(), KtFirAnalysisSessionComponent {
override val analysisSession: KtFirAnalysisSession by weakRef(analysisSession)
private val completionContextCache = HashMap<Pair<FirFile, KtCallableDeclaration>, LowLevelFirApiFacade.FirCompletionContext>()
private val completionContextCache =
HashMap<Pair<FirFile, KtCallableDeclaration>, LowLevelFirApiFacadeForCompletion.FirCompletionContext>()
override fun checkExtensionFitsCandidate(
firSymbolForCandidate: KtCallableSymbol,
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCompletion
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.ValidityToken
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
@@ -126,7 +127,7 @@ internal class KtFirScopeProvider(
private fun buildCompletionContextForEnclosingDeclaration(
originalFile: KtFile,
positionInFakeFile: KtElement
): LowLevelFirApiFacade.FirCompletionContext {
): LowLevelFirApiFacadeForCompletion.FirCompletionContext {
val originalFirFile = firResolveState.getFirFile(originalFile)
val declarationContext = EnclosingDeclarationContext.detect(originalFile, positionInFakeFile)
@@ -9,6 +9,7 @@ import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacade
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCompletion
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalDeclaration
import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.*
@@ -57,14 +58,14 @@ internal val EnclosingDeclarationContext.fakeEnclosingDeclaration: KtCallableDec
internal fun EnclosingDeclarationContext.buildCompletionContext(originalFirFile: FirFile, firResolveState: FirModuleResolveState) =
when (this) {
is FunctionContext -> LowLevelFirApiFacade.buildCompletionContextForFunction(
is FunctionContext -> LowLevelFirApiFacadeForCompletion.buildCompletionContextForFunction(
originalFirFile,
fakeEnclosingFunction,
originalEnclosingFunction,
state = firResolveState
)
is PropertyContext -> LowLevelFirApiFacade.buildCompletionContextForProperty(
is PropertyContext -> LowLevelFirApiFacadeForCompletion.buildCompletionContextForProperty(
originalFirFile,
fakeEnclosingProperty,
originalEnclosingProperty,