From 2eb439899e8eeb1c23b7e42e085f7b7d425269dc Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Tue, 7 Jul 2020 22:48:41 +0300 Subject: [PATCH] Use index to get symbols from importing scopes --- .../impl/FirAbstractSimpleImportingScope.kt | 3 +- .../impl/FirAbstractStarImportingScope.kt | 3 +- .../defaultKotlinImports.kt | 6 +++ .../insideFunctionWithParameters.kt | 6 +++ .../primitiveCompletion/nullableCompletion.kt | 7 +++ .../primitiveCompletion/stringCompletion.kt | 6 +++ .../test/JSBasicCompletionTestGenerated.java | 20 +++++++ .../test/JvmBasicCompletionTestGenerated.java | 20 +++++++ .../KotlinFirCompletionContributor.kt | 53 +++++++++++++++---- 9 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/defaultKotlinImports.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/nullableCompletion.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/stringCompletion.kt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt index 4f3b8f7c452..678e8e6b620 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractSimpleImportingScope.kt @@ -21,7 +21,8 @@ abstract class FirAbstractSimpleImportingScope( scopeSession: ScopeSession ) : FirAbstractImportingScope(session, scopeSession, lookupInFir = true) { - protected abstract val simpleImports: Map> + // TODO try to hide this + abstract val simpleImports: Map> override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) { val imports = simpleImports[name] ?: return diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractStarImportingScope.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractStarImportingScope.kt index 1ebc74053df..a6654ab432c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractStarImportingScope.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/scopes/impl/FirAbstractStarImportingScope.kt @@ -21,7 +21,8 @@ abstract class FirAbstractStarImportingScope( lookupInFir: Boolean = true ) : FirAbstractImportingScope(session, scopeSession, lookupInFir) { - protected abstract val starImports: List + // TODO try to hide this + abstract val starImports: List private val absentClassifierNames = mutableSetOf() diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/defaultKotlinImports.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/defaultKotlinImports.kt new file mode 100644 index 00000000000..274b840cf09 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/defaultKotlinImports.kt @@ -0,0 +1,6 @@ +fun usage() { + val a = +} + +// EXIST: listOf +// EXIST: setOf \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt new file mode 100644 index 00000000000..8400243a857 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt @@ -0,0 +1,6 @@ +fun test(a: Int, b: Int) { + +} + +// EXIST: a +// EXIST: b \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/nullableCompletion.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/nullableCompletion.kt new file mode 100644 index 00000000000..0a5f965f5e9 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/nullableCompletion.kt @@ -0,0 +1,7 @@ +fun usage() { + val a = "10".toIntOrNull() + + a?. +} + +// EXIST: toString \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/stringCompletion.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/stringCompletion.kt new file mode 100644 index 00000000000..5f76fc8297e --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/stringCompletion.kt @@ -0,0 +1,6 @@ +fun usage() { + "". +} + +// EXIST: length +// EXIST: get \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index 66ad5d64dde..3ee07da46af 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -2252,6 +2252,11 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClassExplicitReceiver.kt"); } + @TestMetadata("defaultKotlinImports.kt") + public void testDefaultKotlinImports() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/defaultKotlinImports.kt"); + } + @TestMetadata("explicitReceiverCompletion.kt") public void testExplicitReceiverCompletion() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt"); @@ -2282,6 +2287,11 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverFromExtensionMethod.kt"); } + @TestMetadata("insideFunctionWithParameters.kt") + public void testInsideFunctionWithParameters() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt"); + } + @TestMetadata("localVariablesAndFunctions.kt") public void testLocalVariablesAndFunctions() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt"); @@ -2292,11 +2302,21 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt"); } + @TestMetadata("nullableCompletion.kt") + public void testNullableCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/nullableCompletion.kt"); + } + @TestMetadata("smartCast.kt") public void testSmartCast() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/smartCast.kt"); } + @TestMetadata("stringCompletion.kt") + public void testStringCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/stringCompletion.kt"); + } + @TestMetadata("topLevelVariablesAndFunctions.kt") public void testTopLevelVariablesAndFunctions() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 6bef398da25..926853c3abd 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -2252,6 +2252,11 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClassExplicitReceiver.kt"); } + @TestMetadata("defaultKotlinImports.kt") + public void testDefaultKotlinImports() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/defaultKotlinImports.kt"); + } + @TestMetadata("explicitReceiverCompletion.kt") public void testExplicitReceiverCompletion() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt"); @@ -2282,6 +2287,11 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverFromExtensionMethod.kt"); } + @TestMetadata("insideFunctionWithParameters.kt") + public void testInsideFunctionWithParameters() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt"); + } + @TestMetadata("localVariablesAndFunctions.kt") public void testLocalVariablesAndFunctions() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt"); @@ -2292,11 +2302,21 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt"); } + @TestMetadata("nullableCompletion.kt") + public void testNullableCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/nullableCompletion.kt"); + } + @TestMetadata("smartCast.kt") public void testSmartCast() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/smartCast.kt"); } + @TestMetadata("stringCompletion.kt") + public void testStringCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/stringCompletion.kt"); + } + @TestMetadata("topLevelVariablesAndFunctions.kt") public void testTopLevelVariablesAndFunctions() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt"); diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt index e2b7696148f..73b7f13da6f 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt @@ -7,14 +7,19 @@ package org.jetbrains.kotlin.idea.completion import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.openapi.project.Project import com.intellij.patterns.PlatformPatterns +import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.ProcessingContext import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.declarations.FirTypeParametersOwner import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.fir.realPsi import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.scope import org.jetbrains.kotlin.fir.scopes.FirScope +import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractSimpleImportingScope +import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractStarImportingScope import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol import org.jetbrains.kotlin.fir.typeContext @@ -24,6 +29,9 @@ import org.jetbrains.kotlin.idea.fir.getFirOfClosestParent import org.jetbrains.kotlin.idea.fir.getOrBuildFirSafe import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionByPackageIndex +import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyByPackageIndex +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType @@ -36,9 +44,6 @@ class KotlinFirCompletionContributor : CompletionContributor() { } } -/** - * 1. Collect all receivers for the expression (implicit and explicit) - */ private object KotlinFirCompletionProvider : CompletionProvider() { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { val originalFile = parameters.originalFile as? KtFile ?: return @@ -56,6 +61,8 @@ private object KotlinFirCompletionProvider : CompletionProvider> = sequence { + val packageIndexHelper = PackageIndexHelper(parameters.position.project) + val explicitReceiver = element.explicitReceiver val explicitReceiverType = explicitReceiver?.typeRef?.coneTypeUnsafe() val explicitReceiverScope = explicitReceiverType?.scope(completionContext.session, ScopeSession()) @@ -72,16 +79,16 @@ private object KotlinFirCompletionProvider : CompletionProvider.isExtension get() = fir.receiverTypeRef != null -private fun FirScope.collectCallableSymbols(): MutableList> { + +private fun FirScope.getScopeNames(indexHelper: PackageIndexHelper): Set = when (this) { + is FirAbstractStarImportingScope -> { + this.starImports.flatMapTo(mutableSetOf()) { indexHelper.getPackageTopLevelNames(it.packageFqName) } + } + + is FirAbstractSimpleImportingScope -> simpleImports.keys + + else -> getCallableNames() +} + +private fun FirScope.collectCallableSymbols(packageIndexHelper: PackageIndexHelper): MutableList> { val allCallableSymbols = mutableListOf>() fun symbolsCollector(symbol: FirCallableSymbol<*>) { allCallableSymbols.add(symbol) } - for (name in getCallableNames()) { + val scopeNames = getScopeNames(packageIndexHelper) + for (name in scopeNames) { processFunctionsByName(name, ::symbolsCollector) processPropertiesByName(name, ::symbolsCollector) } @@ -114,6 +133,22 @@ private fun FirScope.collectCallableSymbols(): MutableList> return allCallableSymbols } +private class PackageIndexHelper(private val project: Project) { + private val searchScope = GlobalSearchScope.allScope(project) + + private val functionByPackageIndex = KotlinTopLevelFunctionByPackageIndex.getInstance() + private val propertyByPackageIndex = KotlinTopLevelPropertyByPackageIndex.getInstance() + + fun getPackageTopLevelNames(packageFqName: FqName): Set { + return getTopLevelCallables(packageFqName).mapTo(mutableSetOf()) { it.nameAsSafeName } + } + + private fun getTopLevelCallables(packageFqName: FqName): Sequence = sequence { + yieldAll(functionByPackageIndex.get(packageFqName.asString(), project, searchScope)) + yieldAll(propertyByPackageIndex.get(packageFqName.asString(), project, searchScope)) + } +} + private fun FirCallableSymbol<*>.isExtensionThatCanBeCalledOnTypes( receivers: List, session: TypeCheckerProviderContext