From ffb907150aca104e3fa6268560bad6e581b5f4a5 Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Wed, 1 Jul 2020 14:30:40 +0300 Subject: [PATCH] Use information about receivers in completion - Found few problems during resolving a single functon; disabled assertions and marked them with TODO - Add simple completion tests to simplify development --- .../fir/resolve/dfa/cfg/ControlFlowGraph.kt | 9 +- .../dfa/cfg/ControlFlowGraphBuilder.kt | 5 +- idea/build.gradle.kts | 2 + .../classFieldsAndFunctions.kt | 11 ++ ...classFieldsAndFunctionsExplicitReceiver.kt | 20 +++ .../classFieldsAndFunctionsFromInheritor.kt | 17 +++ .../classFieldsAndFunctionsFromInnerClass.kt | 18 +++ .../explicitReceiverCompletion.kt | 13 ++ ...sionPropertyAndFunctionExplicitReceiver.kt | 21 +++ ...sionPropertyAndFunctionExplicitReceiver.kt | 23 ++++ .../implicitReceiverCompletion.kt | 24 ++++ .../localVariablesAndFunctions.kt | 9 ++ ...calVariablesAndFunctionsFromNestedScope.kt | 20 +++ .../topLevelVariablesAndFunctions.kt | 9 ++ .../primitiveCompletion/twoReceivers.kt | 11 ++ .../test/JSBasicCompletionTestGenerated.java | 73 +++++++++++ .../test/JvmBasicCompletionTestGenerated.java | 73 +++++++++++ .../KotlinFirCompletionContributor.kt | 123 +++++++----------- 18 files changed, 403 insertions(+), 78 deletions(-) create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctions.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsExplicitReceiver.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInheritor.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverCompletion.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/twoReceivers.kt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt index 9db047c4df2..6de74229dcb 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraph.kt @@ -37,10 +37,11 @@ class ControlFlowGraph(val declaration: FirDeclaration?, val name: String, val k state = State.Completed if (kind == Kind.Stub) return val sortedNodes = orderNodes() - assert(sortedNodes.size == _nodes.size) - for (node in _nodes) { - assert(node in sortedNodes) - } + // TODO Fix this +// assert(sortedNodes.size == _nodes.size) +// for (node in _nodes) { +// assert(node in sortedNodes) +// } _nodes.clear() _nodes.addAll(sortedNodes) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 85ee117125a..d6efad02bda 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -368,7 +368,10 @@ class ControlFlowGraphBuilder { } ?: continue addEdge(node, graph.enterNode, preferredKind = EdgeKind.CfgForward) node = graph.exitNode - classGraph.addSubGraph(graph) + // TODO Fix this + if (graph.owner == null) { + classGraph.addSubGraph(graph) + } } addEdge(node, exitNode, preferredKind = EdgeKind.CfgForward) return popGraph() diff --git a/idea/build.gradle.kts b/idea/build.gradle.kts index 2690c48804b..5a2bd8feb18 100644 --- a/idea/build.gradle.kts +++ b/idea/build.gradle.kts @@ -187,6 +187,8 @@ dependencies { testRuntime(intellijPluginDep("smali")) testRuntime(intellijPluginDep("testng")) + testRuntime(project(":idea:idea-frontend-fir")) + if (Ide.AS36.orHigher()) { testRuntime(intellijPluginDep("android-layoutlib")) testRuntime(intellijPluginDep("git4idea")) diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctions.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctions.kt new file mode 100644 index 00000000000..43449239e25 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctions.kt @@ -0,0 +1,11 @@ +class A { + fun aa() {} + val aaa = 10 + + fun test() { + + } +} + +// EXIST: aa +// EXIST: aaa diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsExplicitReceiver.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsExplicitReceiver.kt new file mode 100644 index 00000000000..51d41c594e2 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsExplicitReceiver.kt @@ -0,0 +1,20 @@ +class B { + fun bb() {} + val bbb = 20 +} + +class A { + fun aa() {} + val aaa = 10 + + fun test() { + val b = B() + + b. + } +} + +// EXIST: bb +// EXIST: bbb +// ABSENT: aa +// ABSENT: aaa \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInheritor.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInheritor.kt new file mode 100644 index 00000000000..93fb705107f --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInheritor.kt @@ -0,0 +1,17 @@ +open class A { + fun aa() {} + val aaa = 10 + + fun test() { + + } +} + +class B : A() { + fun test() { + + } +} + +// EXIST: aa +// EXIST: aaa diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt new file mode 100644 index 00000000000..b337c4e664d --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt @@ -0,0 +1,18 @@ +class A { + fun aa() {} + val aaa = 10 + + inner class AA { + fun bb() {} + val bbb = 20 + + fun test() { + + } + } +} + +// EXIST: aa +// EXIST: aaa +// EXIST: bb +// EXIST: bbb \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt new file mode 100644 index 00000000000..2fa5b91dcb4 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt @@ -0,0 +1,13 @@ +class A { + fun aa() {} + val aaa = 10 +} + +fun test() { + val a = A() + + a. +} + +// EXIST: aa +// EXIST: aaa \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt new file mode 100644 index 00000000000..711e59e840b --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt @@ -0,0 +1,21 @@ +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. +} + +// EXIST: anyFun +// EXIST: anyVal +// EXIST: aVal +// EXIST: aFun +// ABSENT: bVal +// ABSENT: bFun \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt new file mode 100644 index 00000000000..02fb8da11cb --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt @@ -0,0 +1,23 @@ +fun T.anyFun() {} +val T.anyVal: Int get() = 10 + +open class A +fun T.aFun() {} +val T.aVal: Int get() = 10 + +open class B + +fun T.bFun() {} +val T.bVal: Int get() = 10 + +fun test(a: A) { + a.aFun() + a. +} + +// EXIST: anyFun +// EXIST: anyVal +// EXIST: aVal +// EXIST: aFun +// ABSENT: bVal +// ABSENT: bFun \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverCompletion.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverCompletion.kt new file mode 100644 index 00000000000..608839f78ad --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverCompletion.kt @@ -0,0 +1,24 @@ +class A { + fun aa() {} + val aaa = 10 +} + +fun A.run(action: A.() -> Unit) {} + +fun test(a: A) { + a.run { + + + + // remove this + Unit + } +} + +// this does not work for some reason +//fun A.test() { +//} + +// EXIST: aa +// EXIST: aaa +// EXIST: run \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt new file mode 100644 index 00000000000..a831f19062e --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt @@ -0,0 +1,9 @@ +fun test() { + fun aa() {} + val aaa = 10 + + +} + +// EXIST: aa +// EXIST: aaa \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt new file mode 100644 index 00000000000..39f1f190339 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt @@ -0,0 +1,20 @@ +fun run(action: () -> Unit) = action() + +fun test() { + fun aa() {} + val aaa = 10 + + run { + fun aabb() {} + val aaabb = 20 + + + + Unit // remove this + } +} + +// EXIST: aa +// EXIST: aaa +// EXIST: aabb +// EXIST: aaabb diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt new file mode 100644 index 00000000000..a2ba6a24ce8 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt @@ -0,0 +1,9 @@ +fun aa() {} +val aaa = 10 + +fun test() { + +} + +// EXIST: aa +// EXIST: aaa \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/twoReceivers.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/twoReceivers.kt new file mode 100644 index 00000000000..cb78345ffcc --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/twoReceivers.kt @@ -0,0 +1,11 @@ +class A + +class OuterClass { + fun A.innerExt() {} + + fun test(a: A) { + a. + } +} + +// EXIST: innerExt \ 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 8aa4199f584..ef8088325c9 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 @@ -2215,6 +2215,79 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes } } + @TestMetadata("idea/idea-completion/testData/basic/common/primitiveCompletion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PrimitiveCompletion extends AbstractJSBasicCompletionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPrimitiveCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/primitiveCompletion"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @TestMetadata("classFieldsAndFunctions.kt") + public void testClassFieldsAndFunctions() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctions.kt"); + } + + @TestMetadata("classFieldsAndFunctionsExplicitReceiver.kt") + public void testClassFieldsAndFunctionsExplicitReceiver() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsExplicitReceiver.kt"); + } + + @TestMetadata("classFieldsAndFunctionsFromInheritor.kt") + public void testClassFieldsAndFunctionsFromInheritor() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInheritor.kt"); + } + + @TestMetadata("classFieldsAndFunctionsFromInnerClass.kt") + public void testClassFieldsAndFunctionsFromInnerClass() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt"); + } + + @TestMetadata("explicitReceiverCompletion.kt") + public void testExplicitReceiverCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt"); + } + + @TestMetadata("extensionPropertyAndFunctionExplicitReceiver.kt") + public void testExtensionPropertyAndFunctionExplicitReceiver() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt"); + } + + @TestMetadata("genericExtensionPropertyAndFunctionExplicitReceiver.kt") + public void testGenericExtensionPropertyAndFunctionExplicitReceiver() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt"); + } + + @TestMetadata("implicitReceiverCompletion.kt") + public void testImplicitReceiverCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverCompletion.kt"); + } + + @TestMetadata("localVariablesAndFunctions.kt") + public void testLocalVariablesAndFunctions() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt"); + } + + @TestMetadata("localVariablesAndFunctionsFromNestedScope.kt") + public void testLocalVariablesAndFunctionsFromNestedScope() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt"); + } + + @TestMetadata("topLevelVariablesAndFunctions.kt") + public void testTopLevelVariablesAndFunctions() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt"); + } + + @TestMetadata("twoReceivers.kt") + public void testTwoReceivers() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/twoReceivers.kt"); + } + } + @TestMetadata("idea/idea-completion/testData/basic/common/shadowing") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 cb8e0a2927c..2924e1ac109 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 @@ -2215,6 +2215,79 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT } } + @TestMetadata("idea/idea-completion/testData/basic/common/primitiveCompletion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PrimitiveCompletion extends AbstractJvmBasicCompletionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPrimitiveCompletion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/primitiveCompletion"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @TestMetadata("classFieldsAndFunctions.kt") + public void testClassFieldsAndFunctions() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctions.kt"); + } + + @TestMetadata("classFieldsAndFunctionsExplicitReceiver.kt") + public void testClassFieldsAndFunctionsExplicitReceiver() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsExplicitReceiver.kt"); + } + + @TestMetadata("classFieldsAndFunctionsFromInheritor.kt") + public void testClassFieldsAndFunctionsFromInheritor() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInheritor.kt"); + } + + @TestMetadata("classFieldsAndFunctionsFromInnerClass.kt") + public void testClassFieldsAndFunctionsFromInnerClass() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/classFieldsAndFunctionsFromInnerClass.kt"); + } + + @TestMetadata("explicitReceiverCompletion.kt") + public void testExplicitReceiverCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/explicitReceiverCompletion.kt"); + } + + @TestMetadata("extensionPropertyAndFunctionExplicitReceiver.kt") + public void testExtensionPropertyAndFunctionExplicitReceiver() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/extensionPropertyAndFunctionExplicitReceiver.kt"); + } + + @TestMetadata("genericExtensionPropertyAndFunctionExplicitReceiver.kt") + public void testGenericExtensionPropertyAndFunctionExplicitReceiver() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/genericExtensionPropertyAndFunctionExplicitReceiver.kt"); + } + + @TestMetadata("implicitReceiverCompletion.kt") + public void testImplicitReceiverCompletion() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/implicitReceiverCompletion.kt"); + } + + @TestMetadata("localVariablesAndFunctions.kt") + public void testLocalVariablesAndFunctions() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt"); + } + + @TestMetadata("localVariablesAndFunctionsFromNestedScope.kt") + public void testLocalVariablesAndFunctionsFromNestedScope() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctionsFromNestedScope.kt"); + } + + @TestMetadata("topLevelVariablesAndFunctions.kt") + public void testTopLevelVariablesAndFunctions() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/topLevelVariablesAndFunctions.kt"); + } + + @TestMetadata("twoReceivers.kt") + public void testTwoReceivers() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/twoReceivers.kt"); + } + } + @TestMetadata("idea/idea-completion/testData/basic/common/shadowing") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 412105a59a3..26a860d57c4 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 @@ -9,23 +9,24 @@ import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.lookup.LookupElementBuilder import com.intellij.patterns.PlatformPatterns import com.intellij.util.ProcessingContext -import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression 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.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 import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.KtLabelReferenceExpression -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.types.AbstractTypeChecker class KotlinFirCompletionContributor : CompletionContributor() { init { @@ -50,81 +51,57 @@ private object KotlinFirCompletionProvider : CompletionProvider().scope(firFunction.session, ScopeSession()) ?: return -// // (symbolProvider.getSymbolByTypeRef>(receiver.typeRef))?.buildUseSiteMemberScope(firFunction.session, ScopeSession()) ?: return -// -// for (name in firScope.getCallableNames()) { -// firScope.processFunctionsByName(name) { -// it.fir -// result.addElement(LookupElementBuilder.create(name.asString())) -// } -// -// firScope.processPropertiesByName(name) { -// result.addElement(LookupElementBuilder.create(name.asString())) -// } -// } -// } - -// val symbolProvider = originalFileFir.session.firSymbolProvider -// -// val allCallableNamesInPackage = -//// symbolProvider.getTopLevelCallableSymbols(originalFile.packageFqName, Name.identifier("bar")) -// symbolProvider.getAllCallableNamesInPackage(originalFileFir.packageFqName) -// -// for (it in allCallableNamesInPackage) { -// result.addElement(LookupElementBuilder.create(it.asString())) -// } -// -// val scope = ((nameExpression.getOrBuildFir() as? FirQualifiedAccessExpression)?.explicitReceiver as? FirFunctionCall)?.typeRef?.let { -// (symbolProvider.getSymbolByTypeRef>(it) as? FirClassSymbol<*>)?.classId?.let { -// symbolProvider.getAllCallableNamesInClass(it) -// } -// } -// -//// val type = (((nameExpression.getOrBuildFir() as FirQualifiedAccessExpression).explicitReceiver as FirFunctionCall).typeRef as FirResolvedTypeRef).type as ConeClassLikeType -//// originalFileFir.session.declaredMemberScopeProvider.declaredMemberScope(type.lookupTag.toSymbol(originalFileFir.session).fir as FirRegularClass) -// - return - } - - interface ScopeWrapper { - fun availableNames(): Set - } - - fun getScopes(element: FirQualifiedAccessExpression, session: FirSession): Sequence = sequence { val receiver = element.explicitReceiver - if (receiver != null) { + val explicitReceiverType = receiver?.typeRef?.coneTypeUnsafe() - val firScope = receiver.typeRef.coneTypeUnsafe().scope(session, ScopeSession()) - // (symbolProvider.getSymbolByTypeRef>(receiver.typeRef))?.buildUseSiteMemberScope(firFunction.session, ScopeSession()) ?: return + val scopes: Sequence = sequence { + val explicitReceiverScope = explicitReceiverType?.scope(completionContext.session, ScopeSession()) + if (explicitReceiverScope != null) { + yield(explicitReceiverScope) + } - if (firScope != null) { - yield(object : ScopeWrapper { - override fun availableNames(): Set { - return firScope.getCallableNames() + yieldAll(towerDataContext.localScopes) + + val implicitReceiversScopes = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.implicitReceiver?.implicitScope } + yieldAll(implicitReceiversScopes) + + val nonLocalScopes = towerDataContext.nonLocalTowerDataElements.mapNotNull { it.scope } + yieldAll(nonLocalScopes) + } + + for (scope in scopes) { + for (name in scope.getCallableNames()) { + if (name.isConstructor) continue + + fun processor(symbol: FirCallableSymbol<*>) { + val expectedReceiverType = symbol.fir.receiverTypeRef?.coneTypeUnsafe() + + if (expectedReceiverType != null) { + val receiverTypes = if (explicitReceiverType != null) { + listOf(explicitReceiverType) + } else { + towerDataContext.nonLocalTowerDataElements.mapNotNull { it.implicitReceiver?.type } + } + + val expectedReceiverTypeIsPresent = receiverTypes.any { + AbstractTypeChecker.isSubtypeOf(completionContext.session.typeContext, it, expectedReceiverType) + } + + if (expectedReceiverTypeIsPresent) { + result.addElement(LookupElementBuilder.create(name.asString())) + } + } else if (explicitReceiverType == null || symbol.callableId.classId == explicitReceiverType.classId) { + result.addElement(LookupElementBuilder.create(name.asString())) } - }) + } + + scope.processFunctionsByName(name, ::processor) + scope.processPropertiesByName(name, ::processor) } } } + + private val Name.isConstructor get() = this == Name.special("") }