[KT-58817] Implicit receiver for code completion

Prior to this commit code completion for .kts files was supported only
partially. Script has an implicit receiver - base class representing a
script itself, the point where its basic API resides. The knowledge of
this receiver was missing.

There are at least two context where this knowledge is crucial:
1. Code highlighting (worked fine)
2. Code completion (failed)

`FirScriptConfiguratorExtension` is responsible for filling
`FirScriptBuilder` with base script class (in addition to other
properties). See usages of [1] in its implementation.

The thing is that resolution during the completion works a bit
different. Instead of converting the entire `KtFile` it's interested
in `KtScript` only. See usages of [2].
`RawFirBuilder.Visitor.visitScript` is where implicit receivers were
missing.

Code completion and inspections applied to `.kts` files already have
`FirScript` and don't require its full reconstruction with expensive
`FirScriptConfiguratorExtension`. `RawFirBuilder.Visitor` was modified
to support sometimes already existing `FirElement`.

----------------------------------------------------------------
[1]: ScriptCompilationConfiguration.baseClass
[2]: RawFirBuilder.Visitor.convertScript
This commit is contained in:
Andrei Klunnyi
2023-05-17 15:21:28 +02:00
committed by Space Team
parent 2a1d4a42ae
commit 6535278bd3
5 changed files with 133 additions and 105 deletions
@@ -24,8 +24,8 @@ internal fun buildFileFirAnnotation(
val builder = object : RawFirBuilder(session, baseScopeProvider) {
inner class VisitorWithReplacement : Visitor() {
override fun convertElement(element: KtElement): FirElement? =
super.convertElement(replacementApplier?.tryReplace(element) ?: element)
override fun convertElement(element: KtElement, original: FirElement?): FirElement? =
super.convertElement(replacementApplier?.tryReplace(element) ?: element, original)
}
}
builder.context.packageFqName = fileAnnotation.containingKtFile.packageFqName
@@ -177,8 +177,8 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
}
private inner class VisitorWithReplacement(private val containingClass: FirRegularClass?) : Visitor() {
override fun convertElement(element: KtElement): FirElement? =
super.convertElement(replacementApplier?.tryReplace(element) ?: element)
override fun convertElement(element: KtElement, original: FirElement?): FirElement? =
super.convertElement(replacementApplier?.tryReplace(element) ?: element, original)
override fun convertProperty(
property: KtProperty,
@@ -226,7 +226,7 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
return ConstructorConversionParams(superTypeCallEntry, selfType, typeParameters)
}
override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor, data: Unit?): FirElement {
override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor, data: FirElement?): FirElement {
val classOrObject = constructor.getContainingClassOrObject()
val params = extractContructorConversionParams(classOrObject, constructor)
val delegatedTypeRef = (originalDeclaration as FirConstructor).delegatedConstructor?.constructedTypeRef
@@ -260,10 +260,10 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
return newConstructor
}
override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor, data: Unit?): FirElement =
override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor, data: FirElement?): FirElement =
processPrimaryConstructor(constructor.getContainingClassOrObject(), constructor)
override fun visitEnumEntry(enumEntry: KtEnumEntry, data: Unit?): FirElement {
override fun visitEnumEntry(enumEntry: KtEnumEntry, data: FirElement?): FirElement {
val owner = containingClass ?: buildErrorWithAttachment("Enum entry outside of class") {
withPsiEntry("enumEntry", enumEntry, baseSession.llFirModuleData.ktModule)
}
@@ -310,17 +310,17 @@ internal class RawFirNonLocalDeclarationBuilder private constructor(
// Constructor outside of class, syntax error, we should not do anything
originalDeclaration
} else {
visitor.convertElement(declarationToBuild)
visitor.convertElement(declarationToBuild, originalDeclaration)
}
}
is KtClassOrObject -> {
when {
originalDeclaration is FirConstructor -> visitor.processPrimaryConstructor(declarationToBuild, null)
originalDeclaration is FirField -> visitor.processField(declarationToBuild, originalDeclaration)
else -> visitor.convertElement(declarationToBuild)
else -> visitor.convertElement(declarationToBuild, originalDeclaration)
}
}
else -> visitor.convertElement(declarationToBuild)
else -> visitor.convertElement(declarationToBuild, originalDeclaration)
} as FirDeclaration
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.builder.RawFirBuilder
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
@@ -17,7 +17,7 @@ internal fun buildFirUserTypeRef(
baseScopeProvider: FirScopeProvider
): FirUserTypeRef {
val builder = object : RawFirBuilder(session, baseScopeProvider) {
fun build(): FirUserTypeRef = Visitor().visitTypeReference(typeReference, Unit) as FirUserTypeRef
fun build(): FirUserTypeRef = Visitor().visitTypeReference(typeReference, null) as FirUserTypeRef
}
builder.context.packageFqName = typeReference.containingKtFile.packageFqName
return builder.build()