Callable reference completion for non-imported callables
This commit is contained in:
+12
-10
@@ -116,8 +116,8 @@ public class ReferenceVariantsHelper(
|
|||||||
else -> throw RuntimeException() //TODO: see KT-9394
|
else -> throw RuntimeException() //TODO: see KT-9394
|
||||||
}
|
}
|
||||||
|
|
||||||
val resolutionScope = resolutionScope(expression) ?: return emptyList()
|
val resolutionScope = resolutionScope(expression, context) ?: return emptyList()
|
||||||
val dataFlowInfo = dataFlowInfo(expression)
|
val dataFlowInfo = dataFlowInfo(expression, context)
|
||||||
val containingDeclaration = resolutionScope.getContainingDeclaration()
|
val containingDeclaration = resolutionScope.getContainingDeclaration()
|
||||||
|
|
||||||
val smartCastManager = resolutionFacade.frontendService<SmartCastManager>()
|
val smartCastManager = resolutionFacade.frontendService<SmartCastManager>()
|
||||||
@@ -334,14 +334,16 @@ public class ReferenceVariantsHelper(
|
|||||||
return resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.PACKAGES, nameFilter).filter(visibilityFilter)
|
return resolutionScope.getDescriptorsFiltered(DescriptorKindFilter.PACKAGES, nameFilter).filter(visibilityFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: drop these methods
|
companion object {
|
||||||
public fun resolutionScope(expression: JetSimpleNameExpression): JetScope? {
|
//TODO: drop these methods
|
||||||
val parent = expression.parent
|
public fun resolutionScope(expression: JetExpression, bindingContext: BindingContext): JetScope? {
|
||||||
return context[BindingContext.RESOLUTION_SCOPE, if (parent is JetCallableReferenceExpression) parent else expression]
|
val parent = expression.parent
|
||||||
}
|
return bindingContext[BindingContext.RESOLUTION_SCOPE, if (parent is JetCallableReferenceExpression) parent else expression]
|
||||||
|
}
|
||||||
|
|
||||||
public fun dataFlowInfo(expression: JetSimpleNameExpression): DataFlowInfo {
|
public fun dataFlowInfo(expression: JetExpression, bindingContext: BindingContext): DataFlowInfo {
|
||||||
val parent = expression.parent
|
val parent = expression.parent
|
||||||
return context.getDataFlowInfo(if (parent is JetCallableReferenceExpression) parent else expression)
|
return bindingContext.getDataFlowInfo(if (parent is JetCallableReferenceExpression) parent else expression)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,22 @@ package org.jetbrains.kotlin.idea.util
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens
|
import org.jetbrains.kotlin.lexer.JetTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression
|
import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isPackageDirectiveExpression
|
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.DescriptorKindExclude
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
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<TReceiver : JetElement?>(val descriptorKindFilter: DescriptorKindFilter) {
|
public sealed class CallType<TReceiver : JetElement?>(val descriptorKindFilter: DescriptorKindFilter) {
|
||||||
object UNKNOWN : CallType<Nothing?>(DescriptorKindFilter.ALL)
|
object UNKNOWN : CallType<Nothing?>(DescriptorKindFilter.ALL)
|
||||||
@@ -140,3 +148,58 @@ public sealed class CallTypeAndReceiver<TReceiver : JetElement?, TCallType : Cal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun CallTypeAndReceiver<*, *>.receiverTypes(
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
position: JetExpression,
|
||||||
|
moduleDescriptor: ModuleDescriptor,
|
||||||
|
predictableSmartCastsOnly: Boolean
|
||||||
|
): Collection<JetType>? {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,25 +35,20 @@ import org.jetbrains.kotlin.idea.core.*
|
|||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
|
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
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.CallType
|
||||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||||
import org.jetbrains.kotlin.idea.util.ShadowedDeclarationsFilter
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
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.JetType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
|
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
|
||||||
|
|
||||||
class CompletionSessionConfiguration(
|
class CompletionSessionConfiguration(
|
||||||
val completeNonImportedDeclarations: Boolean,
|
val completeNonImportedDeclarations: Boolean,
|
||||||
@@ -327,58 +322,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
|||||||
|
|
||||||
val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression)
|
val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression)
|
||||||
|
|
||||||
val receiverExpression: JetExpression?
|
var receiverTypes = callTypeAndReceiver.receiverTypes(
|
||||||
when (callTypeAndReceiver) {
|
bindingContext, nameExpression, moduleDescriptor,
|
||||||
is CallTypeAndReceiver.CALLABLE_REFERENCE -> {
|
predictableSmartCastsOnly = true /* we don't include smart cast receiver types for "unpredictable" receiver value to mark members grayed */)
|
||||||
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<SmartCastManager>()
|
|
||||||
.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listOf(receiverValue.type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callTypeAndReceiver is CallTypeAndReceiver.SAFE) {
|
if (callTypeAndReceiver is CallTypeAndReceiver.SAFE) {
|
||||||
receiverTypes = receiverTypes.map { it.makeNotNullable() }
|
receiverTypes = receiverTypes!!.map { it.makeNotNullable() }
|
||||||
}
|
}
|
||||||
|
|
||||||
return callTypeAndReceiver to receiverTypes
|
return callTypeAndReceiver to receiverTypes
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package dependency
|
||||||
|
|
||||||
|
fun CharSequence.extFun(){}
|
||||||
|
|
||||||
|
fun Int.wrongExtFun(){}
|
||||||
|
|
||||||
|
fun topLevelFun(){}
|
||||||
|
|
||||||
|
val topLevelVal: Int = 1
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
fun String.foo() {
|
||||||
|
val v = ::<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// INVOCATION_COUNT: 2
|
||||||
|
// EXIST: extFun
|
||||||
|
// EXIST: topLevelFun
|
||||||
|
// EXIST: topLevelVal
|
||||||
|
// ABSENT: wrongExtFun
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package dependency
|
||||||
|
|
||||||
|
fun CharSequence.extFun(){}
|
||||||
|
|
||||||
|
val String.extVal: Int get() = 1
|
||||||
|
|
||||||
|
fun Int.wrongExtFun(){}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
val v = String::<caret>
|
||||||
|
|
||||||
|
// EXIST: extFun
|
||||||
|
// EXIST: extVal
|
||||||
|
// ABSENT: wrongExtFun
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
val v = ::tim<caret>
|
||||||
|
|
||||||
|
// INVOCATION_COUNT: 2
|
||||||
|
// ELEMENT: timerTask
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
import kotlin.concurrent.timerTask
|
||||||
|
|
||||||
|
val v = ::timerTask<caret>
|
||||||
|
|
||||||
|
// INVOCATION_COUNT: 2
|
||||||
|
// ELEMENT: timerTask
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
fun foo() {
|
fun foo() {
|
||||||
KtCla<caret>
|
KtCla<caret>
|
||||||
}
|
}
|
||||||
|
|
||||||
// ELEMENT: KtClass
|
|
||||||
|
|||||||
-2
@@ -3,5 +3,3 @@ import pack.KtClass
|
|||||||
fun foo() {
|
fun foo() {
|
||||||
KtClass<caret>
|
KtClass<caret>
|
||||||
}
|
}
|
||||||
|
|
||||||
// ELEMENT: KtClass
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo() {
|
||||||
|
val v = String::myExtFu<caret>
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package dependency
|
||||||
|
|
||||||
|
fun String.myExtFun(){}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
import dependency.myExtFun
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val v = String::myExtFun<caret>
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package dependency
|
||||||
|
|
||||||
|
fun String.extFun(){}
|
||||||
|
fun String.wrongExtFun(p: Int){}
|
||||||
|
|
||||||
|
fun topLevelFun(){}
|
||||||
|
fun wrongTopLevelFun(p: Int){}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
fun String.foo() {
|
||||||
|
bar(::<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(p: () -> Unit) { }
|
||||||
|
fun bar(p: String.() -> Unit) { }
|
||||||
|
|
||||||
|
|
||||||
|
// INVOCATION_COUNT: 2
|
||||||
|
// EXIST: extFun
|
||||||
|
// EXIST: topLevelFun
|
||||||
|
// ABSENT: wrongExtFun
|
||||||
|
// ABSENT: wrongTopLevelFun
|
||||||
+12
@@ -35,6 +35,18 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/multifile"), Pattern.compile("^([^\\.]+)$"), false);
|
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")
|
@TestMetadata("CallablesInExcludedPackage")
|
||||||
public void testCallablesInExcludedPackage() throws Exception {
|
public void testCallablesInExcludedPackage() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/CallablesInExcludedPackage/");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/CallablesInExcludedPackage/");
|
||||||
|
|||||||
+6
@@ -41,6 +41,12 @@ public class MultiFileSmartCompletionTestGenerated extends AbstractMultiFileSmar
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("CallableReferenceNotImported")
|
||||||
|
public void testCallableReferenceNotImported() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smartMultiFile/CallableReferenceNotImported/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("CallablesInExcludedPackage")
|
@TestMetadata("CallablesInExcludedPackage")
|
||||||
public void testCallablesInExcludedPackage() throws Exception {
|
public void testCallablesInExcludedPackage() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smartMultiFile/CallablesInExcludedPackage/");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smartMultiFile/CallablesInExcludedPackage/");
|
||||||
|
|||||||
+6
@@ -181,6 +181,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("Property.kt")
|
||||||
public void testProperty() throws Exception {
|
public void testProperty() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/Property.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/callableReference/Property.kt");
|
||||||
|
|||||||
+4
@@ -85,6 +85,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
|
|||||||
doTest('\t', "TestBundle.properties")
|
doTest('\t', "TestBundle.properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testNotImportedExtension() {
|
||||||
|
doTest()
|
||||||
|
}
|
||||||
|
|
||||||
fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
|
fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
|
||||||
val fileName = getTestName(false)
|
val fileName = getTestName(false)
|
||||||
|
|
||||||
|
|||||||
@@ -24,23 +24,19 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
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.stubindex.*
|
||||||
import org.jetbrains.kotlin.idea.util.CallType
|
import org.jetbrains.kotlin.idea.util.CallType
|
||||||
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
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.idea.util.substituteExtensionIfCallable
|
||||||
import org.jetbrains.kotlin.name.FqName
|
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.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.descriptorUtil.isAnnotatedAsHidden
|
||||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
|
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.types.JetType
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
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<CallableDescriptor> {
|
public fun getCallableTopLevelExtensions(nameFilter: (String) -> Boolean, expression: JetSimpleNameExpression, bindingContext: BindingContext): Collection<CallableDescriptor> {
|
||||||
val (callType, receiverValues) = receiverValues(expression, bindingContext)
|
val callTypeAndReceiver = CallTypeAndReceiver.detect(expression)
|
||||||
if (receiverValues.isEmpty()) return emptyList()
|
val receiverTypes = callTypeAndReceiver.receiverTypes(bindingContext, expression, moduleDescriptor, predictableSmartCastsOnly = false)
|
||||||
|
if (receiverTypes == null || receiverTypes.isEmpty()) return emptyList()
|
||||||
|
|
||||||
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
val receiverTypeNames = HashSet<String>()
|
||||||
|
receiverTypes.forEach { receiverTypeNames.addTypeNames(it) }
|
||||||
val receiverTypeNames = possibleReceiverTypeNames(receiverValues, dataFlowInfo, bindingContext)
|
|
||||||
|
|
||||||
val index = JetTopLevelExtensionsByReceiverTypeIndex.INSTANCE
|
val index = JetTopLevelExtensionsByReceiverTypeIndex.INSTANCE
|
||||||
|
|
||||||
@@ -111,18 +107,7 @@ public class KotlinIndicesHelper(
|
|||||||
}
|
}
|
||||||
.flatMap { index.get(it, project, scope).asSequence() }
|
.flatMap { index.get(it, project, scope).asSequence() }
|
||||||
|
|
||||||
return findSuitableExtensions(declarations, callType to receiverValues, dataFlowInfo, bindingContext)
|
return findSuitableExtensions(declarations, receiverTypes, callTypeAndReceiver.callType)
|
||||||
}
|
|
||||||
|
|
||||||
private fun possibleReceiverTypeNames(receiverValues: Collection<ReceiverValue>, dataFlowInfo: DataFlowInfo, bindingContext: BindingContext): Set<String> {
|
|
||||||
val result = HashSet<String>()
|
|
||||||
for (receiverValue in receiverValues) {
|
|
||||||
val smartCastManager = resolutionFacade.frontendService<SmartCastManager>()
|
|
||||||
for (type in smartCastManager.getSmartCastVariants(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo)) {
|
|
||||||
result.addTypeNames(type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableCollection<String>.addTypeNames(type: JetType) {
|
private fun MutableCollection<String>.addTypeNames(type: JetType) {
|
||||||
@@ -131,47 +116,19 @@ public class KotlinIndicesHelper(
|
|||||||
constructor.getSupertypes().forEach { addTypeNames(it) }
|
constructor.getSupertypes().forEach { addTypeNames(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun receiverValues(expression: JetSimpleNameExpression, bindingContext: BindingContext): Pair<CallType<*>, Collection<ReceiverValue>> {
|
|
||||||
val callTypeAndReceiver = CallTypeAndReceiver.detect(expression)
|
|
||||||
val receiverValues = receiverValues(expression, callTypeAndReceiver.receiver, bindingContext)
|
|
||||||
return callTypeAndReceiver.callType to receiverValues
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <TReceiver : JetElement?> receiverValues(
|
|
||||||
expression: JetSimpleNameExpression,
|
|
||||||
receiverElement: TReceiver,
|
|
||||||
bindingContext: BindingContext
|
|
||||||
): Collection<ReceiverValue> {
|
|
||||||
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
|
* Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
|
||||||
*/
|
*/
|
||||||
private fun findSuitableExtensions(
|
private fun findSuitableExtensions(
|
||||||
declarations: Sequence<JetCallableDeclaration>,
|
declarations: Sequence<JetCallableDeclaration>,
|
||||||
receiverValues: Pair<CallType<*>, Collection<ReceiverValue>>,
|
receiverTypes: Collection<JetType>,
|
||||||
dataFlowInfo: DataFlowInfo,
|
callType: CallType<*>
|
||||||
bindingContext: BindingContext
|
|
||||||
): Collection<CallableDescriptor> {
|
): Collection<CallableDescriptor> {
|
||||||
val result = LinkedHashSet<CallableDescriptor>()
|
val result = LinkedHashSet<CallableDescriptor>()
|
||||||
|
|
||||||
fun processDescriptor(descriptor: CallableDescriptor) {
|
fun processDescriptor(descriptor: CallableDescriptor) {
|
||||||
if (descriptorFilter(descriptor)) {
|
if (descriptorFilter(descriptor)) {
|
||||||
for (receiverValue in receiverValues.second) {
|
result.addAll(descriptor.substituteExtensionIfCallable(receiverTypes, callType))
|
||||||
result.addAll(descriptor.substituteExtensionIfCallable(receiverValue, receiverValues.first, bindingContext, dataFlowInfo, moduleDescriptor))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user