FIR IDE: add this labels to keyword completion
This commit is contained in:
committed by
TeamCityServer
parent
47a596c214
commit
ca320f2f8d
@@ -8,6 +8,7 @@ dependencies {
|
|||||||
compileOnly(project(":compiler:util"))
|
compileOnly(project(":compiler:util"))
|
||||||
compileOnly(project(":compiler:frontend"))
|
compileOnly(project(":compiler:frontend"))
|
||||||
compileOnly(project(":compiler:frontend.java"))
|
compileOnly(project(":compiler:frontend.java"))
|
||||||
|
compileOnly(project(":idea:idea-frontend-independent"))
|
||||||
compileOnly(project(":js:js.frontend"))
|
compileOnly(project(":js:js.frontend"))
|
||||||
compileOnly(project(":js:js.serializer"))
|
compileOnly(project(":js:js.serializer"))
|
||||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) }
|
compileOnly(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) }
|
||||||
|
|||||||
@@ -18,31 +18,6 @@ import org.jetbrains.kotlin.types.FlexibleType
|
|||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
|
|
||||||
fun KtFunctionLiteral.findLabelAndCall(): Pair<Name?, KtCallExpression?> {
|
|
||||||
val literalParent = (this.parent as KtLambdaExpression).parent
|
|
||||||
|
|
||||||
fun KtValueArgument.callExpression(): KtCallExpression? {
|
|
||||||
val parent = parent
|
|
||||||
return (if (parent is KtValueArgumentList) parent else this).parent as? KtCallExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
when (literalParent) {
|
|
||||||
is KtLabeledExpression -> {
|
|
||||||
val callExpression = (literalParent.parent as? KtValueArgument)?.callExpression()
|
|
||||||
return Pair(literalParent.getLabelNameAsName(), callExpression)
|
|
||||||
}
|
|
||||||
|
|
||||||
is KtValueArgument -> {
|
|
||||||
val callExpression = literalParent.callExpression()
|
|
||||||
val label = (callExpression?.calleeExpression as? KtSimpleNameExpression)?.getReferencedNameAsName()
|
|
||||||
return Pair(label, callExpression)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
return Pair(null, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun SmartCastManager.getSmartCastVariantsWithLessSpecificExcluded(
|
fun SmartCastManager.getSmartCastVariantsWithLessSpecificExcluded(
|
||||||
receiverToCast: ReceiverValue,
|
receiverToCast: ReceiverValue,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
fun foo(): String.() -> Unit {
|
fun foo(): String.() -> Unit {
|
||||||
return (label@ {
|
return (label@ {
|
||||||
f {
|
f {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
class Outer {
|
class Outer {
|
||||||
class Nested {
|
class Nested {
|
||||||
inner class Inner {
|
inner class Inner {
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
|
class C {
|
||||||
|
companion object {
|
||||||
|
fun String.foo() {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// INVOCATION_COUNT: 1
|
||||||
|
// EXIST: "this@Companion"
|
||||||
+5
@@ -579,6 +579,11 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
|
|||||||
runTest("idea/idea-completion/testData/keywords/This.kt");
|
runTest("idea/idea-completion/testData/keywords/This.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ThisInCompanion.kt")
|
||||||
|
public void testThisInCompanion() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/ThisInCompanion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ThisPrefixMatching.kt")
|
@TestMetadata("ThisPrefixMatching.kt")
|
||||||
public void testThisPrefixMatching() throws Exception {
|
public void testThisPrefixMatching() throws Exception {
|
||||||
runTest("idea/idea-completion/testData/keywords/ThisPrefixMatching.kt");
|
runTest("idea/idea-completion/testData/keywords/ThisPrefixMatching.kt");
|
||||||
|
|||||||
+2
-1
@@ -226,7 +226,8 @@ private class KotlinWithNameReferenceCompletionProvider(
|
|||||||
extensionChecker: ExtensionApplicabilityChecker,
|
extensionChecker: ExtensionApplicabilityChecker,
|
||||||
visibilityChecker: CompletionVisibilityChecker,
|
visibilityChecker: CompletionVisibilityChecker,
|
||||||
) {
|
) {
|
||||||
val (implicitScopes, implicitReceiversTypes) = implicitScopesContext
|
val (implicitScopes, implicitReceivers) = implicitScopesContext
|
||||||
|
val implicitReceiversTypes = implicitReceivers.map { it.type }
|
||||||
|
|
||||||
val availableNonExtensions = implicitScopes.collectNonExtensions(visibilityChecker)
|
val availableNonExtensions = implicitScopes.collectNonExtensions(visibilityChecker)
|
||||||
val extensionsWhichCanBeCalled = implicitScopes.collectSuitableExtensions(extensionChecker, visibilityChecker)
|
val extensionsWhichCanBeCalled = implicitScopes.collectSuitableExtensions(extensionChecker, visibilityChecker)
|
||||||
|
|||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.completion.contributors.keywords
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.completion.CompletionParameters
|
||||||
|
import com.intellij.codeInsight.lookup.LookupElement
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.util.isAncestor
|
||||||
|
import org.jetbrains.kotlin.idea.completion.KeywordLookupObject
|
||||||
|
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||||
|
import org.jetbrains.kotlin.idea.completion.createKeywordElement
|
||||||
|
import org.jetbrains.kotlin.idea.completion.keywords.CompletionKeywordHandler
|
||||||
|
import org.jetbrains.kotlin.idea.completion.labelNameToTail
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtImplicitReceiver
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtAnonymousFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassKind
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.util.findLabelAndCall
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtFunctionLiteral
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||||
|
|
||||||
|
internal class ThisKeywordHandler(
|
||||||
|
private val basicContext: FirBasicCompletionContext
|
||||||
|
) : CompletionKeywordHandler<KtAnalysisSession>(KtTokens.THIS_KEYWORD) {
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
override fun KtAnalysisSession.createLookups(
|
||||||
|
parameters: CompletionParameters,
|
||||||
|
expression: KtExpression?,
|
||||||
|
lookup: LookupElement,
|
||||||
|
project: Project
|
||||||
|
): Collection<LookupElement> {
|
||||||
|
if (expression == null) {
|
||||||
|
// for completion in secondary constructor delegation call
|
||||||
|
return listOf(lookup)
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = mutableListOf<LookupElement>()
|
||||||
|
val receivers = basicContext.originalKtFile.getScopeContextForPosition(expression).implicitReceivers
|
||||||
|
|
||||||
|
receivers.forEachIndexed { index, receiver ->
|
||||||
|
if (!canReferenceSymbolByThis(parameters, receiver.ownerSymbol)) {
|
||||||
|
return@forEachIndexed
|
||||||
|
}
|
||||||
|
val labelName = if (index != 0) getThisLabelBySymbol(receiver.ownerSymbol) else null
|
||||||
|
result += createThisLookupElement(receiver, labelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtAnalysisSession.canReferenceSymbolByThis(parameters: CompletionParameters, symbol: KtSymbol): Boolean {
|
||||||
|
if (symbol !is KtClassOrObjectSymbol) return true
|
||||||
|
if (symbol.classKind != KtClassKind.COMPANION_OBJECT) return true
|
||||||
|
val companionPsi = symbol.psi as KtClassOrObject
|
||||||
|
return parameters.offset in companionPsi.textRange
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtAnalysisSession.createThisLookupElement(receiver: KtImplicitReceiver, labelName: Name?): LookupElement {
|
||||||
|
return createKeywordElement("this", labelName.labelNameToTail(), lookupObject = KeywordLookupObject())
|
||||||
|
.withTypeText(receiver.type.render(KtTypeRendererOptions.SHORT_NAMES))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtAnalysisSession.getThisLabelBySymbol(symbol: KtSymbol): Name? = when {
|
||||||
|
symbol is KtNamedSymbol && !symbol.name.isSpecial -> symbol.name
|
||||||
|
symbol is KtAnonymousFunctionSymbol -> {
|
||||||
|
val psi = symbol.psi as KtFunctionLiteral
|
||||||
|
psi.findLabelAndCall().first
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+5
@@ -579,6 +579,11 @@ public class FirKeywordCompletionTestGenerated extends AbstractFirKeywordComplet
|
|||||||
runTest("idea/idea-completion/testData/keywords/This.kt");
|
runTest("idea/idea-completion/testData/keywords/This.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ThisInCompanion.kt")
|
||||||
|
public void testThisInCompanion() throws Exception {
|
||||||
|
runTest("idea/idea-completion/testData/keywords/ThisInCompanion.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ThisPrefixMatching.kt")
|
@TestMetadata("ThisPrefixMatching.kt")
|
||||||
public void testThisPrefixMatching() throws Exception {
|
public void testThisPrefixMatching() throws Exception {
|
||||||
runTest("idea/idea-completion/testData/keywords/ThisPrefixMatching.kt");
|
runTest("idea/idea-completion/testData/keywords/ThisPrefixMatching.kt");
|
||||||
|
|||||||
+10
-1
@@ -5,11 +5,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.frontend.api.components
|
package org.jetbrains.kotlin.idea.frontend.api.components
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.*
|
import org.jetbrains.kotlin.idea.frontend.api.scopes.*
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFileSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFileSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPackageSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPackageSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithDeclarations
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithDeclarations
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
@@ -55,4 +58,10 @@ interface KtScopeProviderMixIn : KtAnalysisSessionMixIn {
|
|||||||
analysisSession.scopeProvider.getScopeContextForPosition(this, this)
|
analysisSession.scopeProvider.getScopeContextForPosition(this, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class KtScopeContext(val scopes: KtCompositeScope, val implicitReceiversTypes: List<KtType>)
|
data class KtScopeContext(val scopes: KtCompositeScope, val implicitReceivers: List<KtImplicitReceiver>)
|
||||||
|
|
||||||
|
class KtImplicitReceiver(
|
||||||
|
override val token: ValidityToken,
|
||||||
|
val type: KtType,
|
||||||
|
val ownerSymbol: KtSymbol
|
||||||
|
): ValidityTokenOwner
|
||||||
|
|||||||
+10
-2
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project
|
|||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
|
import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
|
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.ScopeSession
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertiesScope
|
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertiesScope
|
||||||
@@ -18,6 +19,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.*
|
|||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.FirModuleResolveState
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getTowerDataContextUnsafe
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getTowerDataContextUnsafe
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
|
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtImplicitReceiver
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeContext
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeContext
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeProvider
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeProvider
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||||
@@ -143,7 +145,13 @@ internal class KtFirScopeProvider(
|
|||||||
val towerDataContext = analysisSession.firResolveState.getTowerDataContextUnsafe(positionInFakeFile)
|
val towerDataContext = analysisSession.firResolveState.getTowerDataContextUnsafe(positionInFakeFile)
|
||||||
|
|
||||||
val implicitReceivers = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.implicitReceiver }.distinct()
|
val implicitReceivers = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.implicitReceiver }.distinct()
|
||||||
val implicitReceiversTypes = implicitReceivers.map { builder.typeBuilder.buildKtType(it.type) }
|
val implicitKtReceivers = implicitReceivers.map { receiver ->
|
||||||
|
KtImplicitReceiver(
|
||||||
|
token,
|
||||||
|
builder.typeBuilder.buildKtType(receiver.type),
|
||||||
|
builder.buildSymbol(receiver.boundSymbol.fir as FirDeclaration),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val implicitReceiverScopes = implicitReceivers.mapNotNull { it.implicitScope }
|
val implicitReceiverScopes = implicitReceivers.mapNotNull { it.implicitScope }
|
||||||
val nonLocalScopes = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.scope }.distinct()
|
val nonLocalScopes = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.scope }.distinct()
|
||||||
@@ -158,7 +166,7 @@ internal class KtFirScopeProvider(
|
|||||||
|
|
||||||
KtScopeContext(
|
KtScopeContext(
|
||||||
getCompositeScope(allKtScopes.asReversed()),
|
getCompositeScope(allKtScopes.asReversed()),
|
||||||
implicitReceiversTypes.asReversed()
|
implicitKtReceivers.asReversed()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.util
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
|
fun KtFunctionLiteral.findLabelAndCall(): Pair<Name?, KtCallExpression?> {
|
||||||
|
val literalParent = (this.parent as KtLambdaExpression).parent
|
||||||
|
|
||||||
|
fun KtValueArgument.callExpression(): KtCallExpression? {
|
||||||
|
val parent = parent
|
||||||
|
return (if (parent is KtValueArgumentList) parent else this).parent as? KtCallExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
when (literalParent) {
|
||||||
|
is KtLabeledExpression -> {
|
||||||
|
val callExpression = (literalParent.parent as? KtValueArgument)?.callExpression()
|
||||||
|
return Pair(literalParent.getLabelNameAsName(), callExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
is KtValueArgument -> {
|
||||||
|
val callExpression = literalParent.callExpression()
|
||||||
|
val label = (callExpression?.calleeExpression as? KtSimpleNameExpression)?.getReferencedNameAsName()
|
||||||
|
return Pair(label, callExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
return Pair(null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ dependencies {
|
|||||||
testRuntimeOnly(project(":kotlin-compiler"))
|
testRuntimeOnly(project(":kotlin-compiler"))
|
||||||
testRuntimeOnly(commonDep("org.jetbrains.intellij.deps", "trove4j"))
|
testRuntimeOnly(commonDep("org.jetbrains.intellij.deps", "trove4j"))
|
||||||
testRuntimeOnly(project(":idea:ide-common")) { isTransitive = false }
|
testRuntimeOnly(project(":idea:ide-common")) { isTransitive = false }
|
||||||
|
testRuntimeOnly(project(":idea:idea-frontend-independent")) { isTransitive = false }
|
||||||
|
|
||||||
embeddableTestRuntime(project(":kotlin-scripting-ide-services", configuration="runtimeElements"))
|
embeddableTestRuntime(project(":kotlin-scripting-ide-services", configuration="runtimeElements"))
|
||||||
embeddableTestRuntime(project(":kotlin-scripting-compiler-impl-embeddable", configuration="runtimeElements"))
|
embeddableTestRuntime(project(":kotlin-scripting-compiler-impl-embeddable", configuration="runtimeElements"))
|
||||||
|
|||||||
Reference in New Issue
Block a user