Completion and import popup for bound callable references
This commit is contained in:
+25
-13
@@ -152,8 +152,7 @@ class ReferenceVariantsHelper(
|
||||
}
|
||||
|
||||
is CallTypeAndReceiver.CALLABLE_REFERENCE -> {
|
||||
val resolutionScope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
||||
return getVariantsForCallableReference(callTypeAndReceiver.receiver, resolutionScope, kindFilter, nameFilter)
|
||||
return getVariantsForCallableReference(callTypeAndReceiver, contextElement, useReceiverType, kindFilter, nameFilter)
|
||||
}
|
||||
|
||||
is CallTypeAndReceiver.DEFAULT -> receiverExpression = null
|
||||
@@ -227,24 +226,37 @@ class ReferenceVariantsHelper(
|
||||
}
|
||||
|
||||
private fun getVariantsForCallableReference(
|
||||
receiverExpression: KtExpression?,
|
||||
resolutionScope: LexicalScope,
|
||||
callTypeAndReceiver: CallTypeAndReceiver.CALLABLE_REFERENCE,
|
||||
contextElement: PsiElement,
|
||||
useReceiverType: KotlinType?,
|
||||
kindFilter: DescriptorKindFilter,
|
||||
nameFilter: (Name) -> Boolean
|
||||
): Collection<DeclarationDescriptor> {
|
||||
val descriptors = LinkedHashSet<DeclarationDescriptor>()
|
||||
if (receiverExpression != null) {
|
||||
val type =
|
||||
(bindingContext.get(BindingContext.DOUBLE_COLON_LHS, receiverExpression) as? DoubleColonLHS.Type)?.type
|
||||
?: return emptyList()
|
||||
|
||||
descriptors.addNonExtensionMembers(listOf(type), kindFilter, nameFilter, constructorFilter = { true })
|
||||
val resolutionScope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
|
||||
|
||||
descriptors.addScopeAndSyntheticExtensions(resolutionScope, listOf(type), CallType.CALLABLE_REFERENCE, kindFilter, nameFilter)
|
||||
val receiver = callTypeAndReceiver.receiver
|
||||
if (receiver != null) {
|
||||
val isStatic = bindingContext[BindingContext.DOUBLE_COLON_LHS, receiver] is DoubleColonLHS.Type
|
||||
|
||||
val staticScope = (type.constructor.declarationDescriptor as? ClassDescriptor)?.staticScope
|
||||
if (staticScope != null) {
|
||||
descriptors.addAll(staticScope.getDescriptorsFiltered(kindFilter, nameFilter))
|
||||
val explicitReceiverTypes = if (useReceiverType != null) {
|
||||
listOf(useReceiverType)
|
||||
}
|
||||
else {
|
||||
callTypeAndReceiver.receiverTypes(bindingContext, contextElement, moduleDescriptor, resolutionFacade, predictableSmartCastsOnly = false)!!
|
||||
}
|
||||
|
||||
val constructorFilter = { descriptor: ClassDescriptor -> if (isStatic) true else descriptor.isInner }
|
||||
descriptors.addNonExtensionMembers(explicitReceiverTypes, kindFilter, nameFilter, constructorFilter)
|
||||
|
||||
descriptors.addScopeAndSyntheticExtensions(resolutionScope, explicitReceiverTypes, CallType.CALLABLE_REFERENCE, kindFilter, nameFilter)
|
||||
|
||||
if (isStatic) {
|
||||
explicitReceiverTypes
|
||||
.map { (it.constructor.declarationDescriptor as? ClassDescriptor)?.staticScope }
|
||||
.filterNotNull()
|
||||
.flatMapTo(descriptors) { scope -> scope.getDescriptorsFiltered(kindFilter, nameFilter) }
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -26,6 +26,7 @@ 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.bindingContextUtil.getDataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
|
||||
@@ -34,6 +35,7 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||
import org.jetbrains.kotlin.util.supertypesWithAny
|
||||
@@ -216,9 +218,20 @@ fun CallTypeAndReceiver<*, *>.receiverTypes(
|
||||
val receiverExpression: KtExpression?
|
||||
when (this) {
|
||||
is CallTypeAndReceiver.CALLABLE_REFERENCE -> {
|
||||
return receiver?.let {
|
||||
(bindingContext[BindingContext.DOUBLE_COLON_LHS, it] as? DoubleColonLHS.Type)?.type
|
||||
}.singletonOrEmptyList()
|
||||
if (receiver != null) {
|
||||
val lhs = bindingContext[BindingContext.DOUBLE_COLON_LHS, receiver] ?: return emptyList()
|
||||
when (lhs) {
|
||||
is DoubleColonLHS.Type -> return listOf(lhs.type)
|
||||
|
||||
is DoubleColonLHS.Expression -> {
|
||||
val receiverValue = ExpressionReceiver.create(receiver, lhs.type, bindingContext)
|
||||
return receiverValueTypes(receiverValue, lhs.dataFlowInfo, bindingContext, moduleDescriptor, predictableSmartCastsOnly)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
is CallTypeAndReceiver.DEFAULT -> receiverExpression = null
|
||||
@@ -267,13 +280,23 @@ fun CallTypeAndReceiver<*, *>.receiverTypes(
|
||||
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfo(contextElement)
|
||||
|
||||
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)
|
||||
}
|
||||
return receiverValues.flatMap {
|
||||
receiverValueTypes(it, dataFlowInfo, bindingContext, moduleDescriptor, predictableSmartCastsOnly)
|
||||
}
|
||||
}
|
||||
|
||||
private fun receiverValueTypes(
|
||||
receiverValue: ReceiverValue,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
bindingContext: BindingContext,
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
predictableSmartCastsOnly: Boolean
|
||||
): List<KotlinType> {
|
||||
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiverValue, bindingContext, moduleDescriptor)
|
||||
return 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)
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
abstract class A {
|
||||
abstract fun memberFunInA()
|
||||
abstract val memberValInA: Int
|
||||
|
||||
inner class InnerInA
|
||||
class NestedInA
|
||||
}
|
||||
|
||||
abstract class B : A() {
|
||||
abstract fun memberFunInB()
|
||||
abstract val memberValInB: Int
|
||||
|
||||
inner class InnerInB
|
||||
class NestedInB
|
||||
}
|
||||
|
||||
fun A.extensionFun(){}
|
||||
|
||||
val A.extensionVal: Int
|
||||
get() = 1
|
||||
|
||||
fun B.extensionFunForB(){}
|
||||
|
||||
fun Any.anyExtensionFun(){}
|
||||
fun String.wrongExtensionFun(){}
|
||||
|
||||
fun globalFun(p: Int) {}
|
||||
val globalVal = 1
|
||||
|
||||
class C {
|
||||
fun memberFun(){}
|
||||
|
||||
val memberVal = 1
|
||||
|
||||
fun A.memberExtensionFun(){}
|
||||
|
||||
fun foo(a: A) {
|
||||
fun localFun(){}
|
||||
|
||||
if (a is B) {
|
||||
val v = a::<caret>
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun companionObjectFun(){}
|
||||
|
||||
fun A.companionExtension(){}
|
||||
}
|
||||
}
|
||||
|
||||
// ABSENT: class
|
||||
// ABSENT: class.java
|
||||
// EXIST: { itemText: "memberFunInA", attributes: "" }
|
||||
// EXIST: { itemText: "memberValInA", attributes: "" }
|
||||
|
||||
/*TODO!*/
|
||||
// ABSENT: { itemText: "InnerInA", attributes: "" }
|
||||
|
||||
// ABSENT: NestedInA
|
||||
// EXIST: { itemText: "extensionFun", attributes: "" }
|
||||
// EXIST: { itemText: "extensionVal", attributes: "" }
|
||||
// EXIST: { itemText: "anyExtensionFun", attributes: "" }
|
||||
// ABSENT: wrongExtensionFun
|
||||
// ABSENT: globalFun
|
||||
// ABSENT: globalVal
|
||||
// ABSENT: memberFun
|
||||
// ABSENT: memberVal
|
||||
// ABSENT: memberExtensionFun
|
||||
// ABSENT: localFun
|
||||
// ABSENT: companionObjectFun
|
||||
// ABSENT: companionExtension
|
||||
|
||||
// EXIST: { itemText: "memberFunInB", attributes: "bold" }
|
||||
// EXIST: { itemText: "memberValInB", attributes: "bold" }
|
||||
// EXIST: { itemText: "InnerInB", attributes: "bold" }
|
||||
// ABSENT: NestedInB
|
||||
// EXIST: { itemText: "extensionFunForB", attributes: "bold" }
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import java.io.File
|
||||
|
||||
fun f(file: File) {
|
||||
val v = file::<caret>
|
||||
}
|
||||
|
||||
// EXIST_JAVA_ONLY: { itemText: "getFreeSpace", tailText: "()", attributes: "bold" }
|
||||
// ABSENT: freeSpace
|
||||
// EXIST_JAVA_ONLY: { itemText: "isFile", tailText: "()", attributes: "bold" }
|
||||
// ABSENT: { itemText: "isFile", tailText: " (from isFile())" }
|
||||
+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 = "a"::<caret>
|
||||
|
||||
// EXIST: extFun
|
||||
// EXIST: extVal
|
||||
// ABSENT: wrongExtFun
|
||||
+12
@@ -1200,6 +1200,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExpressionQualifier.kt")
|
||||
public void testExpressionQualifier() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/ExpressionQualifier.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("HigherOrderFunction.kt")
|
||||
public void testHigherOrderFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/HigherOrderFunction.kt");
|
||||
@@ -1217,6 +1223,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SyntheticExtensions2.kt")
|
||||
public void testSyntheticExtensions2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues")
|
||||
|
||||
+12
@@ -1200,6 +1200,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("ExpressionQualifier.kt")
|
||||
public void testExpressionQualifier() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/ExpressionQualifier.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("HigherOrderFunction.kt")
|
||||
public void testHigherOrderFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/HigherOrderFunction.kt");
|
||||
@@ -1217,6 +1223,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SyntheticExtensions2.kt")
|
||||
public void testSyntheticExtensions2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/callableReference/SyntheticExtensions2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/idea-completion/testData/basic/common/extensionFunctionTypeValues")
|
||||
|
||||
+6
@@ -47,6 +47,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("CallableReferenceNotImportedExtension2")
|
||||
public void testCallableReferenceNotImportedExtension2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/CallableReferenceNotImportedExtension2/");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("CallablesInExcludedPackage")
|
||||
public void testCallablesInExcludedPackage() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/CallablesInExcludedPackage/");
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import dependency.extensionFun
|
||||
|
||||
// "Import" "true"
|
||||
// ERROR: Unresolved reference: extensionFun
|
||||
val v = "a"::extensionFun<caret>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package dependency
|
||||
|
||||
fun String.extensionFun(){}
|
||||
@@ -0,0 +1,3 @@
|
||||
// "Import" "true"
|
||||
// ERROR: Unresolved reference: extensionFun
|
||||
val v = "a"::extensionFun<caret>
|
||||
@@ -71,6 +71,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
doTestWithExtraFile(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callableReferenceExtension2.before.Main.kt")
|
||||
public void testCallableReferenceExtension2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/callableReferenceExtension2.before.Main.kt");
|
||||
doTestWithExtraFile(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callableReferenceTopLevel.before.Main.kt")
|
||||
public void testCallableReferenceTopLevel() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/callableReferenceTopLevel.before.Main.kt");
|
||||
|
||||
Reference in New Issue
Block a user