diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt index d9c68070891..b3f9d073574 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt @@ -116,8 +116,8 @@ public class ReferenceVariantsHelper( else -> throw RuntimeException() //TODO: see KT-9394 } - val resolutionScope = resolutionScope(expression) ?: return emptyList() - val dataFlowInfo = dataFlowInfo(expression) + val resolutionScope = resolutionScope(expression, context) ?: return emptyList() + val dataFlowInfo = dataFlowInfo(expression, context) val containingDeclaration = resolutionScope.getContainingDeclaration() val smartCastManager = resolutionFacade.frontendService() @@ -334,14 +334,16 @@ public class ReferenceVariantsHelper( return resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.PACKAGES, nameFilter).filter(visibilityFilter) } - //TODO: drop these methods - public fun resolutionScope(expression: JetSimpleNameExpression): JetScope? { - val parent = expression.parent - return context[BindingContext.RESOLUTION_SCOPE, if (parent is JetCallableReferenceExpression) parent else expression] - } + companion object { + //TODO: drop these methods + public fun resolutionScope(expression: JetExpression, bindingContext: BindingContext): JetScope? { + val parent = expression.parent + return bindingContext[BindingContext.RESOLUTION_SCOPE, if (parent is JetCallableReferenceExpression) parent else expression] + } - public fun dataFlowInfo(expression: JetSimpleNameExpression): DataFlowInfo { - val parent = expression.parent - return context.getDataFlowInfo(if (parent is JetCallableReferenceExpression) parent else expression) + public fun dataFlowInfo(expression: JetExpression, bindingContext: BindingContext): DataFlowInfo { + val parent = expression.parent + return bindingContext.getDataFlowInfo(if (parent is JetCallableReferenceExpression) parent else expression) + } } } diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt index b29236c2d99..766ea5b0c67 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/util/CallType.kt @@ -18,14 +18,22 @@ package org.jetbrains.kotlin.idea.util import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression import org.jetbrains.kotlin.psi.psiUtil.isPackageDirectiveExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList public sealed class CallType(val descriptorKindFilter: DescriptorKindFilter) { object UNKNOWN : CallType(DescriptorKindFilter.ALL) @@ -140,3 +148,58 @@ public sealed class CallTypeAndReceiver.receiverTypes( + bindingContext: BindingContext, + position: JetExpression, + moduleDescriptor: ModuleDescriptor, + predictableSmartCastsOnly: Boolean +): Collection? { + val receiverExpression: JetExpression? + when (this) { + is CallTypeAndReceiver.CALLABLE_REFERENCE -> { + if (receiver != null) { + val type = bindingContext[BindingContext.TYPE, receiver] + return type.singletonOrEmptyList() + } + else { + receiverExpression = null + } + } + + is CallTypeAndReceiver.DEFAULT -> receiverExpression = null + is CallTypeAndReceiver.DOT -> receiverExpression = receiver + is CallTypeAndReceiver.SAFE -> receiverExpression = receiver + is CallTypeAndReceiver.INFIX -> receiverExpression = receiver + is CallTypeAndReceiver.UNARY -> receiverExpression = receiver + + is CallTypeAndReceiver.IMPORT_DIRECTIVE, + is CallTypeAndReceiver.PACKAGE_DIRECTIVE, + is CallTypeAndReceiver.TYPE, + is CallTypeAndReceiver.UNKNOWN -> + return null + + else -> throw RuntimeException() //TODO: see KT-9394 + } + + val receiverValues = if (receiverExpression != null) { + val expressionType = bindingContext.getType(receiverExpression) + expressionType?.let { listOf(ExpressionReceiver(receiverExpression, expressionType)) } ?: return emptyList() + } + else { + val resolutionScope = ReferenceVariantsHelper.resolutionScope(position, bindingContext) ?: return emptyList() + resolutionScope.getImplicitReceiversWithInstance().map { it.value } + } + + val dataFlowInfo = ReferenceVariantsHelper.dataFlowInfo(position, bindingContext) + + return receiverValues.flatMap { receiverValue -> + val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor) + if (dataFlowValue.isPredictable || !predictableSmartCastsOnly) { // we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed + SmartCastManager().getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo) + } + else { + listOf(receiverValue.type) + } + } +} \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 37955dc6375..4b577ba2bb6 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -35,25 +35,20 @@ import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.references.mainReference -import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.ShadowedDeclarationsFilter -import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance +import org.jetbrains.kotlin.idea.util.receiverTypes import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory -import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance -import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList class CompletionSessionConfiguration( val completeNonImportedDeclarations: Boolean, @@ -327,58 +322,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression) - val receiverExpression: JetExpression? - when (callTypeAndReceiver) { - is CallTypeAndReceiver.CALLABLE_REFERENCE -> { - if (callTypeAndReceiver.receiver != null) { - val type = bindingContext[BindingContext.TYPE, callTypeAndReceiver.receiver] - return callTypeAndReceiver to type.singletonOrEmptyList() - } - else { - receiverExpression = null - } - } - - is CallTypeAndReceiver.DEFAULT -> receiverExpression = null - is CallTypeAndReceiver.DOT -> receiverExpression = callTypeAndReceiver.receiver - is CallTypeAndReceiver.SAFE -> receiverExpression = callTypeAndReceiver.receiver - is CallTypeAndReceiver.INFIX -> receiverExpression = callTypeAndReceiver.receiver - is CallTypeAndReceiver.UNARY -> receiverExpression = callTypeAndReceiver.receiver - - is CallTypeAndReceiver.IMPORT_DIRECTIVE, - is CallTypeAndReceiver.PACKAGE_DIRECTIVE, - is CallTypeAndReceiver.TYPE, - is CallTypeAndReceiver.UNKNOWN -> - // we don't need to highlight immediate members in these cases - return callTypeAndReceiver to null - - else -> throw RuntimeException() //TODO: see KT-9394 - } - - val receiverValues = if (receiverExpression != null) { - val expressionType = bindingContext.getType(receiverExpression) - expressionType?.let { listOf(ExpressionReceiver(receiverExpression, expressionType)) } ?: emptyList() - } - else { - val resolutionScope = referenceVariantsHelper.resolutionScope(nameExpression) - resolutionScope?.getImplicitReceiversWithInstance()?.map { it.value } ?: emptyList() - } - - val dataFlowInfo = referenceVariantsHelper.dataFlowInfo(nameExpression) - - var receiverTypes = receiverValues.flatMap { receiverValue -> - val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor) - if (dataFlowValue.isPredictable) { // we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed - resolutionFacade.frontendService() - .getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo) - } - else { - listOf(receiverValue.type) - } - } + var receiverTypes = callTypeAndReceiver.receiverTypes( + bindingContext, nameExpression, moduleDescriptor, + predictableSmartCastsOnly = true /* we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed */) if (callTypeAndReceiver is CallTypeAndReceiver.SAFE) { - receiverTypes = receiverTypes.map { it.makeNotNullable() } + receiverTypes = receiverTypes!!.map { it.makeNotNullable() } } return callTypeAndReceiver to receiverTypes diff --git a/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImported/CallableReferenceNotImported.dependency.kt b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImported/CallableReferenceNotImported.dependency.kt new file mode 100644 index 00000000000..5b5d42561ab --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImported/CallableReferenceNotImported.dependency.kt @@ -0,0 +1,9 @@ +package dependency + +fun CharSequence.extFun(){} + +fun Int.wrongExtFun(){} + +fun topLevelFun(){} + +val topLevelVal: Int = 1 \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImported/CallableReferenceNotImported.kt b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImported/CallableReferenceNotImported.kt new file mode 100644 index 00000000000..07036ccbc1a --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImported/CallableReferenceNotImported.kt @@ -0,0 +1,9 @@ +fun String.foo() { + val v = :: +} + +// INVOCATION_COUNT: 2 +// EXIST: extFun +// EXIST: topLevelFun +// EXIST: topLevelVal +// ABSENT: wrongExtFun diff --git a/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension/CallableReferenceNotImportedExtension.dependency.kt b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension/CallableReferenceNotImportedExtension.dependency.kt new file mode 100644 index 00000000000..9fdc5264363 --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension/CallableReferenceNotImportedExtension.dependency.kt @@ -0,0 +1,7 @@ +package dependency + +fun CharSequence.extFun(){} + +val String.extVal: Int get() = 1 + +fun Int.wrongExtFun(){} diff --git a/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension/CallableReferenceNotImportedExtension.kt b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension/CallableReferenceNotImportedExtension.kt new file mode 100644 index 00000000000..f4ad1bfcc4e --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension/CallableReferenceNotImportedExtension.kt @@ -0,0 +1,5 @@ +val v = String:: + +// EXIST: extFun +// EXIST: extVal +// ABSENT: wrongExtFun diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/NotImportedTopLevel.kt b/idea/idea-completion/testData/handlers/basic/callableReference/NotImportedTopLevel.kt new file mode 100644 index 00000000000..9b1e75685d6 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/NotImportedTopLevel.kt @@ -0,0 +1,4 @@ +val v = ::tim + +// INVOCATION_COUNT: 2 +// ELEMENT: timerTask diff --git a/idea/idea-completion/testData/handlers/basic/callableReference/NotImportedTopLevel.kt.after b/idea/idea-completion/testData/handlers/basic/callableReference/NotImportedTopLevel.kt.after new file mode 100644 index 00000000000..fa7f4945b41 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/callableReference/NotImportedTopLevel.kt.after @@ -0,0 +1,6 @@ +import kotlin.concurrent.timerTask + +val v = ::timerTask + +// INVOCATION_COUNT: 2 +// ELEMENT: timerTask diff --git a/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject-1.kt b/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject-1.kt index 26f67d2b76e..1119b0cc187 100644 --- a/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject-1.kt +++ b/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject-1.kt @@ -1,5 +1,3 @@ fun foo() { KtCla } - -// ELEMENT: KtClass diff --git a/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject.kt.after b/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject.kt.after index d14d00d8632..6d7ef7edcad 100644 --- a/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject.kt.after +++ b/idea/idea-completion/testData/handlers/multifile/ClassWithClassObject.kt.after @@ -3,5 +3,3 @@ import pack.KtClass fun foo() { KtClass } - -// ELEMENT: KtClass diff --git a/idea/idea-completion/testData/handlers/multifile/NotImportedExtension-1.kt b/idea/idea-completion/testData/handlers/multifile/NotImportedExtension-1.kt new file mode 100644 index 00000000000..aabeecd7ee6 --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/NotImportedExtension-1.kt @@ -0,0 +1,3 @@ +fun foo() { + val v = String::myExtFu +} diff --git a/idea/idea-completion/testData/handlers/multifile/NotImportedExtension-2.kt b/idea/idea-completion/testData/handlers/multifile/NotImportedExtension-2.kt new file mode 100644 index 00000000000..9ea9a85a71f --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/NotImportedExtension-2.kt @@ -0,0 +1,3 @@ +package dependency + +fun String.myExtFun(){} diff --git a/idea/idea-completion/testData/handlers/multifile/NotImportedExtension.kt.after b/idea/idea-completion/testData/handlers/multifile/NotImportedExtension.kt.after new file mode 100644 index 00000000000..11483928f7b --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/NotImportedExtension.kt.after @@ -0,0 +1,5 @@ +import dependency.myExtFun + +fun foo() { + val v = String::myExtFun +} diff --git a/idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/CallableReferenceNotImported.dependency.kt b/idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/CallableReferenceNotImported.dependency.kt new file mode 100644 index 00000000000..c3485310a67 --- /dev/null +++ b/idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/CallableReferenceNotImported.dependency.kt @@ -0,0 +1,7 @@ +package dependency + +fun String.extFun(){} +fun String.wrongExtFun(p: Int){} + +fun topLevelFun(){} +fun wrongTopLevelFun(p: Int){} diff --git a/idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/CallableReferenceNotImported.kt b/idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/CallableReferenceNotImported.kt new file mode 100644 index 00000000000..d82f957740b --- /dev/null +++ b/idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/CallableReferenceNotImported.kt @@ -0,0 +1,13 @@ +fun String.foo() { + bar(::) +} + +fun bar(p: () -> Unit) { } +fun bar(p: String.() -> Unit) { } + + +// INVOCATION_COUNT: 2 +// EXIST: extFun +// EXIST: topLevelFun +// ABSENT: wrongExtFun +// ABSENT: wrongTopLevelFun diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java index 8edf7ccac04..1344881c10e 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java @@ -35,6 +35,18 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/multifile"), Pattern.compile("^([^\\.]+)$"), false); } + @TestMetadata("CallableReferenceNotImported") + public void testCallableReferenceNotImported() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/CallableReferenceNotImported/"); + doTest(fileName); + } + + @TestMetadata("CallableReferenceNotImportedExtension") + public void testCallableReferenceNotImportedExtension() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension/"); + doTest(fileName); + } + @TestMetadata("CallablesInExcludedPackage") public void testCallablesInExcludedPackage() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/CallablesInExcludedPackage/"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileSmartCompletionTestGenerated.java index 2ccb5daec3b..2aeffb7cc39 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileSmartCompletionTestGenerated.java @@ -41,6 +41,12 @@ public class MultiFileSmartCompletionTestGenerated extends AbstractMultiFileSmar doTest(fileName); } + @TestMetadata("CallableReferenceNotImported") + public void testCallableReferenceNotImported() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/"); + doTest(fileName); + } + @TestMetadata("CallablesInExcludedPackage") public void testCallablesInExcludedPackage() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smartMultiFile/CallablesInExcludedPackage/"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java index aa7a7a93c92..15f4af561fb 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java @@ -181,6 +181,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion doTest(fileName); } + @TestMetadata("NotImportedTopLevel.kt") + public void testNotImportedTopLevel() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/NotImportedTopLevel.kt"); + doTest(fileName); + } + @TestMetadata("Property.kt") public void testProperty() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/Property.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt index dffa89898af..c24030c6027 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt @@ -85,6 +85,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() { doTest('\t', "TestBundle.properties") } + fun testNotImportedExtension() { + doTest() + } + fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) { val fileName = getTestName(false) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt index 071bab37cf3..99727c7bd5d 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt @@ -24,23 +24,19 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.resolve.ResolutionFacade -import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver -import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance +import org.jetbrains.kotlin.idea.util.receiverTypes import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.JetCallableDeclaration +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetNamedDeclaration +import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.bindingContextUtil.get -import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotatedAsHidden import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList @@ -94,12 +90,12 @@ public class KotlinIndicesHelper( } public fun getCallableTopLevelExtensions(nameFilter: (String) -> Boolean, expression: JetSimpleNameExpression, bindingContext: BindingContext): Collection { - val (callType, receiverValues) = receiverValues(expression, bindingContext) - if (receiverValues.isEmpty()) return emptyList() + val callTypeAndReceiver = CallTypeAndReceiver.detect(expression) + val receiverTypes = callTypeAndReceiver.receiverTypes(bindingContext, expression, moduleDescriptor, predictableSmartCastsOnly = false) + if (receiverTypes == null || receiverTypes.isEmpty()) return emptyList() - val dataFlowInfo = bindingContext.getDataFlowInfo(expression) - - val receiverTypeNames = possibleReceiverTypeNames(receiverValues, dataFlowInfo, bindingContext) + val receiverTypeNames = HashSet() + receiverTypes.forEach { receiverTypeNames.addTypeNames(it) } val index = JetTopLevelExtensionsByReceiverTypeIndex.INSTANCE @@ -111,18 +107,7 @@ public class KotlinIndicesHelper( } .flatMap { index.get(it, project, scope).asSequence() } - return findSuitableExtensions(declarations, callType to receiverValues, dataFlowInfo, bindingContext) - } - - private fun possibleReceiverTypeNames(receiverValues: Collection, dataFlowInfo: DataFlowInfo, bindingContext: BindingContext): Set { - val result = HashSet() - for (receiverValue in receiverValues) { - val smartCastManager = resolutionFacade.frontendService() - for (type in smartCastManager.getSmartCastVariants(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo)) { - result.addTypeNames(type) - } - } - return result + return findSuitableExtensions(declarations, receiverTypes, callTypeAndReceiver.callType) } private fun MutableCollection.addTypeNames(type: JetType) { @@ -131,47 +116,19 @@ public class KotlinIndicesHelper( constructor.getSupertypes().forEach { addTypeNames(it) } } - private fun receiverValues(expression: JetSimpleNameExpression, bindingContext: BindingContext): Pair, Collection> { - val callTypeAndReceiver = CallTypeAndReceiver.detect(expression) - val receiverValues = receiverValues(expression, callTypeAndReceiver.receiver, bindingContext) - return callTypeAndReceiver.callType to receiverValues - } - - private fun receiverValues( - expression: JetSimpleNameExpression, - receiverElement: TReceiver, - bindingContext: BindingContext - ): Collection { - if (receiverElement != null) { - if (receiverElement !is JetExpression) return emptyList() //TODO? - - val expressionType = bindingContext.getType(receiverElement) - if (expressionType == null || expressionType.isError) return emptyList() - - return listOf(ExpressionReceiver(receiverElement, expressionType)) - } - else { - val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, expression] ?: return emptyList() - return resolutionScope.getImplicitReceiversWithInstance().map { it.value } - } - } - /** * Check that function or property with the given qualified name can be resolved in given scope and called on given receiver */ private fun findSuitableExtensions( declarations: Sequence, - receiverValues: Pair, Collection>, - dataFlowInfo: DataFlowInfo, - bindingContext: BindingContext + receiverTypes: Collection, + callType: CallType<*> ): Collection { val result = LinkedHashSet() fun processDescriptor(descriptor: CallableDescriptor) { if (descriptorFilter(descriptor)) { - for (receiverValue in receiverValues.second) { - result.addAll(descriptor.substituteExtensionIfCallable(receiverValue, receiverValues.first, bindingContext, dataFlowInfo, moduleDescriptor)) - } + result.addAll(descriptor.substituteExtensionIfCallable(receiverTypes, callType)) } }