Iterate on symbols, not on scopes

- This way it is easier to get the correct results
This commit is contained in:
Roman Golyshev
2020-06-24 19:39:51 +03:00
committed by Ilya Kirillov
parent 708c54f4d2
commit a3da1ea1a0
5 changed files with 118 additions and 49 deletions
@@ -0,0 +1,18 @@
class A {
fun aa() {}
val aaa = 10
inner class AA {
fun bb() {}
val bbb = 20
fun test() {
this.<caret>
}
}
}
// ABSENT: aa
// ABSENT: aaa
// EXIST: bb
// EXIST: bbb
@@ -0,0 +1,25 @@
fun Any.anyFun() {}
val Any.anyVal: Int get() = 10
class A
fun A.aFun() {}
val A.aVal: Int get() = 10
class B
fun B.bFun() {}
val B.bVal: Int get() = 10
fun test(a: A) {
a.run {
<caret>
Unit
}
}
// EXIST: anyFun
// EXIST: anyVal
// EXIST: aVal
// EXIST: aFun
// ABSENT: bVal
// ABSENT: bFun
@@ -2247,6 +2247,11 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt");
}
@TestMetadata("classFieldsAndFunctionsFromInnerClassExplicitReceiver.kt")
public void testClassFieldsAndFunctionsFromInnerClassExplicitReceiver() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClassExplicitReceiver.kt");
}
@TestMetadata("explicitReceiverCompletion.kt")
public void testExplicitReceiverCompletion() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt");
@@ -2257,6 +2262,11 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt");
}
@TestMetadata("extensionPropertyAndFunctionImplicitReceiver.kt")
public void testExtensionPropertyAndFunctionImplicitReceiver() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionImplicitReceiver.kt");
}
@TestMetadata("genericExtensionPropertyAndFunctionExplicitReceiver.kt")
public void testGenericExtensionPropertyAndFunctionExplicitReceiver() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt");
@@ -2247,6 +2247,11 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt");
}
@TestMetadata("classFieldsAndFunctionsFromInnerClassExplicitReceiver.kt")
public void testClassFieldsAndFunctionsFromInnerClassExplicitReceiver() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClassExplicitReceiver.kt");
}
@TestMetadata("explicitReceiverCompletion.kt")
public void testExplicitReceiverCompletion() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt");
@@ -2257,6 +2262,11 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt");
}
@TestMetadata("extensionPropertyAndFunctionImplicitReceiver.kt")
public void testExtensionPropertyAndFunctionImplicitReceiver() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionImplicitReceiver.kt");
}
@TestMetadata("genericExtensionPropertyAndFunctionExplicitReceiver.kt")
public void testGenericExtensionPropertyAndFunctionExplicitReceiver() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt");
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
import org.jetbrains.kotlin.idea.fir.getFirOfClosestParent
import org.jetbrains.kotlin.idea.fir.getOrBuildFirSafe
@@ -27,6 +26,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
class KotlinFirCompletionContributor : CompletionContributor() {
init {
@@ -53,65 +53,71 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
val element = nameExpression.getFirOfClosestParent() as? FirQualifiedAccessExpression ?: return
val towerDataContext = completionContext.getTowerDataContext(nameExpression)
val receiver = element.explicitReceiver
val explicitReceiverType = receiver?.typeRef?.coneTypeUnsafe<ConeKotlinType>()
val scopes: Sequence<FirScope> = sequence {
val symbols: Sequence<FirCallableSymbol<*>> = sequence {
val explicitReceiver = element.explicitReceiver
val explicitReceiverType = explicitReceiver?.typeRef?.coneTypeUnsafe<ConeKotlinType>()
val explicitReceiverScope = explicitReceiverType?.scope(completionContext.session, ScopeSession())
if (explicitReceiverScope != null) {
yield(explicitReceiverScope)
}
yieldAll(towerDataContext.localScopes)
val implicitReceiversScopes = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.implicitReceiver?.implicitScope }
yieldAll(implicitReceiversScopes)
val implicitReceivers = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.implicitReceiver }
val implicitReceiversTypes = implicitReceivers.map { it.type }
val localScopes = towerDataContext.localScopes
val nonLocalScopes = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.scope }
yieldAll(nonLocalScopes)
}
val implicitReceiversScopes = implicitReceivers.mapNotNull { it.implicitScope }
for (scope in scopes) {
for (symbol in scope.collectCallableSymbols()) {
val symbolName = symbol.callableId.callableName
if (symbolName.isConstructor) continue
val allNonExplicitScopes = localScopes.asSequence() + nonLocalScopes + implicitReceiversScopes
val expectedReceiverType = symbol.fir.receiverTypeRef?.coneTypeUnsafe<ConeKotlinType>()
val typeContext = completionContext.session.typeContext
if (expectedReceiverType != null) {
val receiverTypes = if (explicitReceiverType != null) {
listOf(explicitReceiverType)
} else {
towerDataContext.nonLocalTowerDataElements.mapNotNull { it.implicitReceiver?.type }
}
if (explicitReceiverScope != null) {
yieldAll(explicitReceiverScope.collectCallableSymbols())
val expectedReceiverTypeIsPresent = receiverTypes.any {
AbstractTypeChecker.isSubtypeOf(completionContext.session.typeContext, it, expectedReceiverType)
}
val allApplicableExtensions = allNonExplicitScopes
.flatMap { it.collectCallableSymbols() }
.filter { it.isExtension && it.isExtensionThatCanBeCalledOnTypes(listOf(explicitReceiverType), typeContext) }
if (expectedReceiverTypeIsPresent) {
result.addElement(LookupElementBuilder.create(symbolName.toString()))
}
} else if (explicitReceiverType == null || symbol.callableId.classId == explicitReceiverType.classId) {
result.addElement(LookupElementBuilder.create(symbolName.toString()))
}
yieldAll(allApplicableExtensions)
} else {
val allAvailableSymbols = allNonExplicitScopes
.flatMap { it.collectCallableSymbols() }
.filter { !it.isExtension || it.isExtensionThatCanBeCalledOnTypes(implicitReceiversTypes, typeContext) }
yieldAll(allAvailableSymbols)
}
}
for (symbol in symbols) {
val symbolName = symbol.callableId.callableName
if (symbolName.isConstructor) continue
result.addElement(LookupElementBuilder.create(symbolName.toString()))
}
}
private fun FirScope.collectCallableSymbols(): MutableList<FirCallableSymbol<*>> {
val allCallableSymbols = mutableListOf<FirCallableSymbol<*>>()
fun symbolsCollector(symbol: FirCallableSymbol<*>) {
allCallableSymbols.add(symbol)
}
for (name in getCallableNames()) {
processFunctionsByName(name, ::symbolsCollector)
processPropertiesByName(name, ::symbolsCollector)
}
return allCallableSymbols
}
private val Name.isConstructor get() = this == Name.special("<init>")
}
private val FirCallableSymbol<*>.isExtension get() = fir.receiverTypeRef != null
private val Name.isConstructor get() = this == Name.special("<init>")
private fun FirScope.collectCallableSymbols(): MutableList<FirCallableSymbol<*>> {
val allCallableSymbols = mutableListOf<FirCallableSymbol<*>>()
fun symbolsCollector(symbol: FirCallableSymbol<*>) {
allCallableSymbols.add(symbol)
}
for (name in getCallableNames()) {
processFunctionsByName(name, ::symbolsCollector)
processPropertiesByName(name, ::symbolsCollector)
}
return allCallableSymbols
}
private fun FirCallableSymbol<*>.isExtensionThatCanBeCalledOnTypes(
receivers: List<ConeKotlinType>,
session: TypeCheckerProviderContext
): Boolean {
val expectedReceiverType = fir.receiverTypeRef?.coneTypeUnsafe<ConeKotlinType>() ?: return false
return receivers.any { AbstractTypeChecker.isSubtypeOf(session, it, expectedReceiverType) }
}