FIR IDE: Add building completion context for properties

This commit is contained in:
Roman Golyshev
2020-09-07 17:52:12 +03:00
parent 58965d1e71
commit 6fd871d0ba
7 changed files with 118 additions and 25 deletions
@@ -90,3 +90,31 @@ inline fun buildProperty(init: FirPropertyBuilder.() -> Unit): FirProperty {
} }
return FirPropertyBuilder().apply(init).build() return FirPropertyBuilder().apply(init).build()
} }
@OptIn(ExperimentalContracts::class)
inline fun buildPropertyCopy(original: FirProperty, init: FirPropertyBuilder.() -> Unit): FirProperty {
contract {
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
val copyBuilder = FirPropertyBuilder()
copyBuilder.source = original.source
copyBuilder.session = original.session
copyBuilder.resolvePhase = original.resolvePhase
copyBuilder.origin = original.origin
copyBuilder.returnTypeRef = original.returnTypeRef
copyBuilder.receiverTypeRef = original.receiverTypeRef
copyBuilder.name = original.name
copyBuilder.initializer = original.initializer
copyBuilder.delegate = original.delegate
copyBuilder.delegateFieldSymbol = original.delegateFieldSymbol
copyBuilder.isVar = original.isVar
copyBuilder.getter = original.getter
copyBuilder.setter = original.setter
copyBuilder.annotations.addAll(original.annotations)
copyBuilder.typeParameters.addAll(original.typeParameters)
copyBuilder.containerSource = original.containerSource
copyBuilder.symbol = original.symbol
copyBuilder.isLocal = original.isLocal
copyBuilder.status = original.status
return copyBuilder.apply(init).build()
}
@@ -172,6 +172,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
parents += typeParametersOwnerBuilder parents += typeParametersOwnerBuilder
defaultNull("getter", "setter", "containerSource", "delegateFieldSymbol") defaultNull("getter", "setter", "containerSource", "delegateFieldSymbol")
default("resolvePhase", "FirResolvePhase.RAW_FIR") default("resolvePhase", "FirResolvePhase.RAW_FIR")
withCopy()
} }
builder(typeOperatorCall) { builder(typeOperatorCall) {
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession 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.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
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
@@ -46,8 +45,8 @@ abstract class FirModuleResolveState {
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 lazyResolveFunctionForCompletion( internal abstract fun lazyResolveDeclarationForCompletion(
firFunction: FirFunction<*>, firFunction: FirDeclaration,
containerFirFile: FirFile, containerFirFile: FirFile,
firIdeProvider: FirIdeProvider, firIdeProvider: FirIdeProvider,
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
@@ -91,8 +90,8 @@ internal open class FirModuleResolveStateImpl(
override fun getCachedMappingForCompletion(element: KtElement): FirElement? = override fun getCachedMappingForCompletion(element: KtElement): FirElement? =
psiToFirCache.getCachedMapping(element) psiToFirCache.getCachedMapping(element)
override fun lazyResolveFunctionForCompletion( override fun lazyResolveDeclarationForCompletion(
firFunction: FirFunction<*>, firFunction: FirDeclaration,
containerFirFile: FirFile, containerFirFile: FirFile,
firIdeProvider: FirIdeProvider, firIdeProvider: FirIdeProvider,
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession 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.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
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
@@ -55,14 +54,14 @@ internal class FirModuleResolveStateForCompletion(
return originalState.resolvedFirToPhase(declaration, toPhase) return originalState.resolvedFirToPhase(declaration, toPhase)
} }
override fun lazyResolveFunctionForCompletion( override fun lazyResolveDeclarationForCompletion(
firFunction: FirFunction<*>, firFunction: FirDeclaration,
containerFirFile: FirFile, containerFirFile: FirFile,
firIdeProvider: FirIdeProvider, firIdeProvider: FirIdeProvider,
toPhase: FirResolvePhase, toPhase: FirResolvePhase,
towerDataContextCollector: FirTowerDataContextCollector towerDataContextCollector: FirTowerDataContextCollector
) { ) {
originalState.lazyResolveFunctionForCompletion(firFunction, containerFirFile, firIdeProvider, toPhase, towerDataContextCollector) originalState.lazyResolveDeclarationForCompletion(firFunction, containerFirFile, firIdeProvider, toPhase, towerDataContextCollector)
} }
override fun getDiagnostics(element: KtElement): List<Diagnostic> { override fun getDiagnostics(element: KtElement): List<Diagnostic> {
@@ -10,9 +10,11 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
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.expressions.FirStatement 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
@@ -22,6 +24,7 @@ import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
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.util.getElementTextInContext import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.KtProperty
object LowLevelFirApiFacade { object LowLevelFirApiFacade {
fun getResolveStateFor(element: KtElement): FirModuleResolveState = fun getResolveStateFor(element: KtElement): FirModuleResolveState =
@@ -82,7 +85,40 @@ object LowLevelFirApiFacade {
} }
val function = frankensteinFunction.apply { val function = frankensteinFunction.apply {
state.lazyResolveFunctionForCompletion(this, firFile, firIdeProvider, phase, contextCollector) state.lazyResolveDeclarationForCompletion(this, firFile, firIdeProvider, phase, contextCollector)
state.recordPsiToFirMappingsForCompletionFrom(this, firFile, element.containingKtFile)
}
return FirCompletionContext(
function.session,
contextCollector,
state
)
}
fun buildCompletionContextForProperty(
firFile: FirFile,
element: KtProperty,
originalElement: KtProperty,
state: FirModuleResolveState,
phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE
): FirCompletionContext {
val firIdeProvider = firFile.session.firIdeProvider
val originalProperty = state.getOrBuildFirFor(originalElement, phase) as FirProperty
val builtProperty = firIdeProvider.buildPropertyWithBody(element)
val contextCollector = FirTowerDataContextCollector()
val frankensteinProperty = buildPropertyCopy(originalProperty) {
symbol = builtProperty.symbol
getter = builtProperty.getter
setter = builtProperty.setter
resolvePhase = minOf(originalProperty.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtProperty.source
session = state.firIdeSourcesSession
}
val function = frankensteinProperty.apply {
state.lazyResolveDeclarationForCompletion(this, firFile, firIdeProvider, phase, contextCollector)
state.recordPsiToFirMappingsForCompletionFrom(this, firFile, element.containingKtFile) state.recordPsiToFirMappingsForCompletionFrom(this, firFile, element.containingKtFile)
} }
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
@ThreadSafeMutableState @ThreadSafeMutableState
internal class FirIdeProvider( internal class FirIdeProvider(
@@ -103,6 +104,10 @@ internal class FirIdeProvider(
return RawFirBuilder(session, kotlinScopeProvider, stubMode = false).buildFunctionWithBody(ktNamedFunction) return RawFirBuilder(session, kotlinScopeProvider, stubMode = false).buildFunctionWithBody(ktNamedFunction)
} }
fun buildPropertyWithBody(ktNamedFunction: KtProperty): FirProperty {
return RawFirBuilder(session, kotlinScopeProvider, stubMode = false).buildPropertyWithBody(ktNamedFunction)
}
@FirProviderInternals @FirProviderInternals
override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) { override fun recordGeneratedClass(owner: FirAnnotatedDeclaration, klass: FirRegularClass) {
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.components package org.jetbrains.kotlin.idea.frontend.api.fir.components
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.util.parentsOfType
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.ScopeSession import org.jetbrains.kotlin.fir.resolve.ScopeSession
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.idea.util.getElementTextInContext
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import java.util.* import java.util.*
@@ -108,18 +109,7 @@ internal class KtFirScopeProvider(
originalFile: KtFile, originalFile: KtFile,
positionInFakeFile: KtElement positionInFakeFile: KtElement
): KtScopeContext = withValidityAssertion { ): KtScopeContext = withValidityAssertion {
val originalFirFile = originalFile.getOrBuildFirOfType<FirFile>(firResolveState) val completionContext = buildCompletionContextForEnclosingDeclaration(originalFile, positionInFakeFile)
val fakeEnclosingFunction = positionInFakeFile.getNonStrictParentOfType<KtNamedFunction>()
?: error("Cannot find enclosing function for ${positionInFakeFile.getElementTextInContext()}")
val originalEnclosingFunction = originalFile.findFunctionDeclarationAt(fakeEnclosingFunction.textOffset)
?: error("Cannot find original function matching to ${fakeEnclosingFunction.getElementTextInContext()} in $originalFile")
val completionContext = LowLevelFirApiFacade.buildCompletionContextForFunction(
originalFirFile,
fakeEnclosingFunction,
originalEnclosingFunction,
state = firResolveState
)
val towerDataContext = completionContext.getTowerDataContext(positionInFakeFile) val towerDataContext = completionContext.getTowerDataContext(positionInFakeFile)
@@ -143,9 +133,44 @@ internal class KtFirScopeProvider(
) )
} }
private fun KtFile.findFunctionDeclarationAt(offset: Int): KtNamedFunction? = private fun buildCompletionContextForEnclosingDeclaration(
originalFile: KtFile,
positionInFakeFile: KtElement
): LowLevelFirApiFacade.FirCompletionContext {
val originalFirFile = originalFile.getOrBuildFirOfType<FirFile>(firResolveState)
val fakeEnclosingFunction = positionInFakeFile.getNonStrictParentOfType<KtNamedFunction>()
if (fakeEnclosingFunction != null) {
val originalEnclosingFunction = originalFile.findDeclarationOfTypeAt<KtNamedFunction>(fakeEnclosingFunction.textOffset)
?: error("Cannot find original function matching to ${fakeEnclosingFunction.getElementTextInContext()} in $originalFile")
return LowLevelFirApiFacade.buildCompletionContextForFunction(
originalFirFile,
fakeEnclosingFunction,
originalEnclosingFunction,
state = firResolveState
)
}
val fakeEnclosingProperty = positionInFakeFile.parentsOfType<KtProperty>().firstOrNull { !it.isLocal }
if (fakeEnclosingProperty != null) {
val originalEnclosingProperty = originalFile.findDeclarationOfTypeAt<KtProperty>(fakeEnclosingProperty.textOffset)
?: error("Cannot find original property matching to ${fakeEnclosingProperty.getElementTextInContext()} in $originalFile")
return LowLevelFirApiFacade.buildCompletionContextForProperty(
originalFirFile,
fakeEnclosingProperty,
originalEnclosingProperty,
state = firResolveState
)
}
error("Cannot find enclosing declaration for ${positionInFakeFile.getElementTextInContext()}")
}
private inline fun<reified T: KtElement> KtFile.findDeclarationOfTypeAt(offset: Int): T? =
findElementAt(offset) findElementAt(offset)
?.getNonStrictParentOfType<KtNamedFunction>() ?.getNonStrictParentOfType<T>()
?.takeIf { it.textOffset == offset } ?.takeIf { it.textOffset == offset }
private fun convertToKtScope(firScope: FirScope): KtScope { private fun convertToKtScope(firScope: FirScope): KtScope {