From c7d6a79c252660610001a5cf0568f03e532fc442 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Thu, 13 Aug 2020 17:36:37 +0300 Subject: [PATCH] FIR IDE: Fix completion in case of function with parameters --- .../builder/FirSimpleFunctionBuilder.kt | 24 +++++++++++++++++++ .../fir/tree/generator/BuilderConfigurator.kt | 1 + .../KotlinFirCompletionContributor.kt | 2 +- .../idea/frontend/api/KtAnalysisSession.kt | 4 +++- .../api/components/KtScopeProvider.kt | 7 +++++- .../fir/low/level/api/LowLevelFirApiFacade.kt | 17 ++++++++++++- .../api/fir/components/KtFirScopeProvider.kt | 10 +++++++- 7 files changed, 60 insertions(+), 5 deletions(-) diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirSimpleFunctionBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirSimpleFunctionBuilder.kt index 5acb705efe1..b42c68f58bc 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirSimpleFunctionBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirSimpleFunctionBuilder.kt @@ -85,3 +85,27 @@ inline fun buildSimpleFunction(init: FirSimpleFunctionBuilder.() -> Unit): FirSi } return FirSimpleFunctionBuilder().apply(init).build() } + +@OptIn(ExperimentalContracts::class) +inline fun buildSimpleFunctionCopy(original: FirSimpleFunction, init: FirSimpleFunctionBuilder.() -> Unit): FirSimpleFunction { + contract { + callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE) + } + val copyBuilder = FirSimpleFunctionBuilder() + 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.valueParameters.addAll(original.valueParameters) + copyBuilder.body = original.body + copyBuilder.status = original.status + copyBuilder.containerSource = original.containerSource + copyBuilder.contractDescription = original.contractDescription + copyBuilder.name = original.name + copyBuilder.symbol = original.symbol + copyBuilder.annotations.addAll(original.annotations) + copyBuilder.typeParameters.addAll(original.typeParameters) + return copyBuilder.apply(init).build() +} diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt index ce95b204edc..3c9bfe579a1 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt @@ -269,6 +269,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator(FirTree default("contractDescription", "FirEmptyContractDescription") useTypes(emptyContractDescriptionType) openBuilder() + withCopy() } builder(tryExpression) { diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt index 215c3ccbb0b..1bacc75100f 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt @@ -43,7 +43,7 @@ private object KotlinHighLevelApiContributor : CompletionProvider) diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/LowLevelFirApiFacade.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/LowLevelFirApiFacade.kt index a8002cf88e2..2180b548999 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/LowLevelFirApiFacade.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/LowLevelFirApiFacade.kt @@ -11,6 +11,8 @@ import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext import org.jetbrains.kotlin.idea.caches.project.getModuleInfo @@ -18,6 +20,7 @@ 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.psi +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.idea.util.getElementTextInContext object LowLevelFirApiFacade { @@ -64,14 +67,26 @@ object LowLevelFirApiFacade { fun buildCompletionContextForFunction( firFile: FirFile, element: KtNamedFunction, + originalElement: KtNamedFunction, state: FirModuleResolveState, phase: FirResolvePhase = FirResolvePhase.BODY_RESOLVE ): FirCompletionContext { val firIdeProvider = firFile.session.firIdeProvider + val originalFunction = state.getOrBuildFirFor(originalElement, phase) as FirSimpleFunction val builtFunction = firIdeProvider.buildFunctionWithBody(element) val towerDataContextForStatement = mutableMapOf() - val function = builtFunction.apply { + // 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 + val frankensteinFunction = buildSimpleFunctionCopy(originalFunction) { + body = builtFunction.body + symbol = builtFunction.symbol as FirNamedFunctionSymbol + resolvePhase = minOf(originalFunction.resolvePhase, FirResolvePhase.DECLARATIONS) + this.session = state.firIdeSourcesSession + } + + val function = frankensteinFunction.apply { state.lazyResolveFunctionForCompletion(this, firFile, firIdeProvider, phase, towerDataContextForStatement) state.recordPsiToFirMappingsForCompletionFrom(this, firFile, element.containingKtFile) } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt index 8c8ea70c676..1e53aaf4867 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirScopeProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.components import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.resolve.ScopeSession @@ -105,14 +106,21 @@ internal class KtFirScopeProvider( return convertToKtScope(firTypeScope) } - override fun getScopeContextForPosition(originalFile: KtFile, positionInFakeFile: KtElement): KtScopeContext = withValidityAssertion { + override fun getScopeContextForPosition( + originalFile: KtFile, + originalPosition: PsiElement?, + positionInFakeFile: KtElement + ): KtScopeContext = withValidityAssertion { val originalFirFile = originalFile.getOrBuildFirOfType(firResolveState) val fakeEnclosingFunction = positionInFakeFile.getNonStrictParentOfType() ?: error("Cannot find enclosing function for ${positionInFakeFile.getElementTextInContext()}") + val originalEnclosingFunction = originalPosition?.getNonStrictParentOfType() + ?: error("Cannot find original enclosing function for $originalPosition") val completionContext = LowLevelFirApiFacade.buildCompletionContextForFunction( originalFirFile, fakeEnclosingFunction, + originalEnclosingFunction, state = firResolveState )