Fixed KT-5611 Completion doesn't include not imported extensions for implicit receiver
#KT-5611 Fixed
This commit is contained in:
@@ -35,6 +35,8 @@ import java.util.HashSet
|
|||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
|
||||||
import org.jetbrains.jet.lang.resolve.bindingContextUtil.getDataFlowInfo
|
import org.jetbrains.jet.lang.resolve.bindingContextUtil.getDataFlowInfo
|
||||||
import org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode
|
import org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode
|
||||||
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.smartcasts.DataFlowInfo
|
||||||
|
|
||||||
public class KotlinIndicesHelper(private val project: Project) {
|
public class KotlinIndicesHelper(private val project: Project) {
|
||||||
public fun getTopLevelObjects(nameFilter: (String) -> Boolean, resolveSession: ResolveSessionForBodies, scope: GlobalSearchScope): Collection<ClassDescriptor> {
|
public fun getTopLevelObjects(nameFilter: (String) -> Boolean, resolveSession: ResolveSessionForBodies, scope: GlobalSearchScope): Collection<ClassDescriptor> {
|
||||||
@@ -131,45 +133,60 @@ public class KotlinIndicesHelper(private val project: Project) {
|
|||||||
expression: JetSimpleNameExpression,
|
expression: JetSimpleNameExpression,
|
||||||
resolveSession: ResolveSessionForBodies,
|
resolveSession: ResolveSessionForBodies,
|
||||||
scope: GlobalSearchScope): Collection<CallableDescriptor> {
|
scope: GlobalSearchScope): Collection<CallableDescriptor> {
|
||||||
val context = resolveSession.resolveToElement(expression)
|
val bindingContext = resolveSession.resolveToElement(expression)
|
||||||
val receiverExpression = expression.getReceiverExpression() ?: return listOf()
|
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
||||||
val isInfixCall = expression.getParent() is JetBinaryExpression
|
|
||||||
val expressionType = context.get<JetExpression, JetType>(BindingContext.EXPRESSION_TYPE, receiverExpression)
|
|
||||||
val jetScope = context.get(BindingContext.RESOLUTION_SCOPE, receiverExpression)
|
|
||||||
|
|
||||||
if (expressionType == null || jetScope == null || expressionType.isError()) {
|
val sourceNames = JetTopLevelFunctionsFqnNameIndex.getInstance().getAllKeys(project).stream() +
|
||||||
return listOf()
|
JetTopLevelPropertiesFqnNameIndex.getInstance().getAllKeys(project).stream()
|
||||||
}
|
|
||||||
|
|
||||||
val sourceNames = JetTopLevelFunctionsFqnNameIndex.getInstance().getAllKeys(project).stream() + JetTopLevelPropertiesFqnNameIndex.getInstance().getAllKeys(project).stream()
|
|
||||||
val allFqNames = sourceNames.map { FqName(it) } + JetFromJavaDescriptorHelper.getTopLevelCallableFqNames(project, scope, true).stream()
|
val allFqNames = sourceNames.map { FqName(it) } + JetFromJavaDescriptorHelper.getTopLevelCallableFqNames(project, scope, true).stream()
|
||||||
|
val matchingFqNames = allFqNames.filter { nameFilter(it.shortName().asString()) }.toSet()
|
||||||
|
|
||||||
// Iterate through the function with attempt to resolve found functions
|
val receiverExpression = expression.getReceiverExpression()
|
||||||
return allFqNames
|
if (receiverExpression != null) {
|
||||||
.filter { nameFilter(it.shortName().asString()) }
|
val isInfixCall = expression.getParent() is JetBinaryExpression
|
||||||
.toSet()
|
|
||||||
.flatMap { findSuitableExtensions(it, receiverExpression, expressionType, isInfixCall, jetScope, resolveSession.getModuleDescriptor(), context) }
|
val expressionType = bindingContext.get<JetExpression, JetType>(BindingContext.EXPRESSION_TYPE, receiverExpression)
|
||||||
|
if (expressionType == null || expressionType.isError()) return listOf()
|
||||||
|
|
||||||
|
val resolutionScope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, receiverExpression) ?: return listOf()
|
||||||
|
|
||||||
|
val receiverValue = ExpressionReceiver(receiverExpression, expressionType)
|
||||||
|
|
||||||
|
return matchingFqNames.flatMap {
|
||||||
|
findSuitableExtensions(it, receiverValue, dataFlowInfo, isInfixCall, resolutionScope, resolveSession.getModuleDescriptor(), bindingContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, expression] ?: return listOf()
|
||||||
|
|
||||||
|
val result = HashSet<CallableDescriptor>()
|
||||||
|
for (receiver in resolutionScope.getImplicitReceiversHierarchy()) {
|
||||||
|
matchingFqNames.flatMapTo(result) {
|
||||||
|
findSuitableExtensions(it, receiver.getValue(), dataFlowInfo, false, resolutionScope, resolveSession.getModuleDescriptor(), bindingContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
|
* Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
|
||||||
*/
|
*/
|
||||||
private fun findSuitableExtensions(callableFQN: FqName,
|
private fun findSuitableExtensions(callableFQN: FqName,
|
||||||
receiverExpression: JetExpression,
|
receiverValue: ReceiverValue,
|
||||||
receiverType: JetType,
|
dataFlowInfo: DataFlowInfo,
|
||||||
isInfixCall: Boolean,
|
isInfixCall: Boolean,
|
||||||
scope: JetScope,
|
scope: JetScope,
|
||||||
module: ModuleDescriptor,
|
module: ModuleDescriptor,
|
||||||
bindingContext: BindingContext): List<CallableDescriptor> {
|
bindingContext: BindingContext): List<CallableDescriptor> {
|
||||||
val importDirective = JetPsiFactory(receiverExpression).createImportDirective(callableFQN.asString())
|
val importDirective = JetPsiFactory(project).createImportDirective(callableFQN.asString())
|
||||||
val declarationDescriptors = analyzeImportReference(importDirective, scope, BindingTraceContext(), module)
|
val declarationDescriptors = analyzeImportReference(importDirective, scope, BindingTraceContext(), module)
|
||||||
|
|
||||||
val receiverValue = ExpressionReceiver(receiverExpression, receiverType)
|
|
||||||
val dataFlowInfo = bindingContext.getDataFlowInfo(receiverExpression)
|
|
||||||
|
|
||||||
return declarationDescriptors
|
return declarationDescriptors
|
||||||
.filterIsInstance(javaClass<CallableDescriptor>())
|
.filterIsInstance(javaClass<CallableDescriptor>())
|
||||||
.filter { it.getExtensionReceiverParameter() != null && ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, it, isInfixCall, bindingContext, dataFlowInfo) }
|
.filter { it.getExtensionReceiverParameter() != null &&
|
||||||
|
ExpressionTypingUtils.checkIsExtensionCallable(receiverValue, it, isInfixCall, bindingContext, dataFlowInfo) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun getClassDescriptors(nameFilter: (String) -> Boolean, analyzer: KotlinCodeAnalyzer, scope: GlobalSearchScope): Collection<ClassDescriptor> {
|
public fun getClassDescriptors(nameFilter: (String) -> Boolean, analyzer: KotlinCodeAnalyzer, scope: GlobalSearchScope): Collection<ClassDescriptor> {
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package second
|
||||||
|
|
||||||
|
import first.C
|
||||||
|
|
||||||
|
fun String.helloFun() { }
|
||||||
|
|
||||||
|
fun String.helloWithParams(i : Int): String = ""
|
||||||
|
|
||||||
|
val String.helloProp: Int get() = 1
|
||||||
|
|
||||||
|
val C.helloForC: Int get() = 1
|
||||||
|
|
||||||
|
fun Int.helloFake() { }
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package first
|
||||||
|
|
||||||
|
class C {
|
||||||
|
fun String.firstFun() {
|
||||||
|
hello<caret>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: helloFun
|
||||||
|
// EXIST: helloWithParams
|
||||||
|
// EXIST: helloProp
|
||||||
|
// EXIST: helloForC
|
||||||
|
// ABSENT: helloFake
|
||||||
|
// NUMBER: 4
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// "Import" "true"
|
||||||
|
// ERROR: Unresolved reference: someFun
|
||||||
|
package testingExtensionFunctionsImport
|
||||||
|
|
||||||
|
import testingExtensionFunctionsImport.data.someFun
|
||||||
|
|
||||||
|
fun String.some() {
|
||||||
|
<caret>someFun()
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Import" "true"
|
||||||
|
// ERROR: Unresolved reference: someFun
|
||||||
|
package testingExtensionFunctionsImport
|
||||||
|
|
||||||
|
fun String.some() {
|
||||||
|
<caret>someFun()
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package testingExtensionFunctionsImport.data
|
||||||
|
|
||||||
|
fun String.someFun() {
|
||||||
|
}
|
||||||
+7
-5
@@ -17,13 +17,9 @@
|
|||||||
package org.jetbrains.jet.completion;
|
package org.jetbrains.jet.completion;
|
||||||
|
|
||||||
import com.intellij.testFramework.TestDataPath;
|
import com.intellij.testFramework.TestDataPath;
|
||||||
import junit.framework.Test;
|
|
||||||
import junit.framework.TestSuite;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.jetbrains.jet.JetTestUtils;
|
import org.jetbrains.jet.JetTestUtils;
|
||||||
import org.jetbrains.jet.test.InnerTestClasses;
|
|
||||||
import org.jetbrains.jet.test.TestMetadata;
|
import org.jetbrains.jet.test.TestMetadata;
|
||||||
import org.jetbrains.jet.JUnit3RunnerWithInners;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -116,6 +112,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("NotImportedExtensionForImplicitReceiver")
|
||||||
|
public void testNotImportedExtensionForImplicitReceiver() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/multifile/NotImportedExtensionForImplicitReceiver/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("NotImportedExtensionFunction")
|
@TestMetadata("NotImportedExtensionFunction")
|
||||||
public void testNotImportedExtensionFunction() throws Exception {
|
public void testNotImportedExtensionFunction() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/multifile/NotImportedExtensionFunction/");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/basic/multifile/NotImportedExtensionFunction/");
|
||||||
|
|||||||
@@ -17,13 +17,10 @@
|
|||||||
package org.jetbrains.jet.plugin.quickfix;
|
package org.jetbrains.jet.plugin.quickfix;
|
||||||
|
|
||||||
import com.intellij.testFramework.TestDataPath;
|
import com.intellij.testFramework.TestDataPath;
|
||||||
import junit.framework.Test;
|
|
||||||
import junit.framework.TestSuite;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.jetbrains.jet.JetTestUtils;
|
import org.jetbrains.jet.JetTestUtils;
|
||||||
import org.jetbrains.jet.test.InnerTestClasses;
|
import org.jetbrains.jet.test.InnerTestClasses;
|
||||||
import org.jetbrains.jet.test.TestMetadata;
|
import org.jetbrains.jet.test.TestMetadata;
|
||||||
import org.jetbrains.jet.JUnit3RunnerWithInners;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -76,6 +73,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
doTestWithExtraFile(fileName);
|
doTestWithExtraFile(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extensionFunctionImportImplicitReceiver.before.Main.kt")
|
||||||
|
public void testExtensionFunctionImportImplicitReceiver() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/extensionFunctionImportImplicitReceiver.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("extensionPropertyImport.before.Main.kt")
|
@TestMetadata("extensionPropertyImport.before.Main.kt")
|
||||||
public void testExtensionPropertyImport() throws Exception {
|
public void testExtensionPropertyImport() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/extensionPropertyImport.before.Main.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/extensionPropertyImport.before.Main.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user