Graying members from smart cast receiver types that require explicit receiver cast
+ changed immediate member criteria (override member is not counted as immediate anymore) + fixed a bug in graying members
This commit is contained in:
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
|
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
|
||||||
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
|
||||||
@@ -142,8 +143,14 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
|||||||
if (receiversData != null) {
|
if (receiversData != null) {
|
||||||
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
||||||
|
|
||||||
var receiverTypes = receiversData.receivers.flatMap {
|
var receiverTypes = receiversData.receivers.flatMap { receiverValue ->
|
||||||
SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(it, bindingContext, moduleDescriptor, dataFlowInfo)
|
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
|
||||||
|
SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listOf(receiverValue.type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receiversData.callType == CallType.SAFE) {
|
if (receiversData.callType == CallType.SAFE) {
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ enum class CallableWeight {
|
|||||||
baseClassMember,
|
baseClassMember,
|
||||||
thisTypeExtension,
|
thisTypeExtension,
|
||||||
baseTypeExtension,
|
baseTypeExtension,
|
||||||
global, // global non-extension
|
globalOrStatic, // global non-extension
|
||||||
notApplicableReceiverNullable
|
receiverCastRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
val CALLABLE_WEIGHT_KEY = Key<CallableWeight>("CALLABLE_WEIGHT_KEY")
|
val CALLABLE_WEIGHT_KEY = Key<CallableWeight>("CALLABLE_WEIGHT_KEY")
|
||||||
|
|||||||
+18
-35
@@ -36,12 +36,12 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
|
|||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
|
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||||
import org.jetbrains.kotlin.types.JetType
|
import org.jetbrains.kotlin.types.JetType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
import org.jetbrains.kotlin.types.typeUtil.nullability
|
|
||||||
|
|
||||||
public data class PackageLookupObject(val fqName: FqName) : DeclarationLookupObject {
|
public data class PackageLookupObject(val fqName: FqName) : DeclarationLookupObject {
|
||||||
override val psiElement: PsiElement? get() = null
|
override val psiElement: PsiElement? get() = null
|
||||||
@@ -87,7 +87,7 @@ public class LookupElementFactory(
|
|||||||
private fun LookupElement.boldIfImmediate(weight: CallableWeight?): LookupElement {
|
private fun LookupElement.boldIfImmediate(weight: CallableWeight?): LookupElement {
|
||||||
val style = when (weight) {
|
val style = when (weight) {
|
||||||
CallableWeight.thisClassMember, CallableWeight.thisTypeExtension -> Style.BOLD
|
CallableWeight.thisClassMember, CallableWeight.thisTypeExtension -> Style.BOLD
|
||||||
CallableWeight.notApplicableReceiverNullable -> Style.GRAYED
|
CallableWeight.receiverCastRequired -> Style.GRAYED
|
||||||
else -> Style.NORMAL
|
else -> Style.NORMAL
|
||||||
}
|
}
|
||||||
return if (style != Style.NORMAL) {
|
return if (style != Style.NORMAL) {
|
||||||
@@ -327,41 +327,24 @@ public class LookupElementFactory(
|
|||||||
private fun callableWeight(descriptor: DeclarationDescriptor): CallableWeight? {
|
private fun callableWeight(descriptor: DeclarationDescriptor): CallableWeight? {
|
||||||
if (descriptor !is CallableDescriptor) return null
|
if (descriptor !is CallableDescriptor) return null
|
||||||
|
|
||||||
val isReceiverNullable = receiverTypes.isNotEmpty() && receiverTypes.all { it.nullability() == TypeNullability.NULLABLE }
|
val overridden = descriptor.overriddenDescriptors
|
||||||
val receiverParameter = descriptor.getExtensionReceiverParameter()
|
if (overridden.isNotEmpty()) {
|
||||||
|
return overridden.map { callableWeight(it)!! }.min()!!
|
||||||
if (receiverParameter != null) {
|
|
||||||
val receiverParamType = receiverParameter.getType()
|
|
||||||
return if (isReceiverNullable && receiverParamType.nullability() == TypeNullability.NOT_NULL)
|
|
||||||
CallableWeight.notApplicableReceiverNullable
|
|
||||||
else if (receiverTypes.any { TypeUtils.equalTypes(it, receiverParamType) })
|
|
||||||
CallableWeight.thisTypeExtension
|
|
||||||
else
|
|
||||||
CallableWeight.baseTypeExtension
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (isReceiverNullable) {
|
|
||||||
return CallableWeight.notApplicableReceiverNullable
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (descriptor !is CallableMemberDescriptor) {
|
|
||||||
return CallableWeight.local
|
|
||||||
}
|
|
||||||
|
|
||||||
val container = descriptor.getContainingDeclaration()
|
val receiverParameter = descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter
|
||||||
return when (container) {
|
if (receiverParameter != null) {
|
||||||
is PackageFragmentDescriptor -> CallableWeight.global
|
return if (receiverTypes.any { TypeUtils.equalTypes(it, receiverParameter.type) })
|
||||||
|
if (descriptor.isExtension) CallableWeight.thisTypeExtension else CallableWeight.thisClassMember
|
||||||
|
else if (receiverTypes.any { it.isSubtypeOf(receiverParameter.type) })
|
||||||
|
if (descriptor.isExtension) CallableWeight.baseTypeExtension else CallableWeight.baseClassMember
|
||||||
|
else
|
||||||
|
CallableWeight.receiverCastRequired
|
||||||
|
}
|
||||||
|
|
||||||
is ClassifierDescriptor -> {
|
return when (descriptor.containingDeclaration) {
|
||||||
if (descriptor.getKind() == CallableMemberDescriptor.Kind.DECLARATION)
|
is PackageFragmentDescriptor, is ClassifierDescriptor -> CallableWeight.globalOrStatic
|
||||||
CallableWeight.thisClassMember
|
else -> CallableWeight.local
|
||||||
else
|
|
||||||
CallableWeight.baseClassMember
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> CallableWeight.local
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ fun foo(o: Any?) {
|
|||||||
// EXIST: { lookupString: "forNullableAny", attributes: "" }
|
// EXIST: { lookupString: "forNullableAny", attributes: "" }
|
||||||
// EXIST: { lookupString: "forString", attributes: "bold" }
|
// EXIST: { lookupString: "forString", attributes: "bold" }
|
||||||
// EXIST: { lookupString: "forAny", attributes: "" }
|
// EXIST: { lookupString: "forAny", attributes: "" }
|
||||||
// EXIST: { lookupString: "compareTo", attributes: "bold" }
|
// EXIST: { lookupString: "compareTo", attributes: "" }
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fun String.forString(){}
|
||||||
|
|
||||||
|
fun globalFun(){}
|
||||||
|
|
||||||
|
fun String?.foo() {
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "globalFun", attributes: "" }
|
||||||
|
// EXIST: { lookupString: "compareTo", attributes: "grayed" }
|
||||||
|
// EXIST: { lookupString: "forString", attributes: "grayed" }
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
fun String.forString(){}
|
||||||
|
fun Any.forAny(){}
|
||||||
|
|
||||||
|
fun <T> T.forT() {}
|
||||||
|
|
||||||
|
fun f(pair: Pair<out Any, out Any>) {
|
||||||
|
if (pair.first !is String) return
|
||||||
|
pair.first.<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: { lookupString: "length", attributes: "grayed" }
|
||||||
|
// EXIST: { lookupString: "hashCode", attributes: "bold" }
|
||||||
|
// EXIST: { lookupString: "forString", attributes: "grayed" }
|
||||||
|
// EXIST: { lookupString: "forAny", attributes: "bold" }
|
||||||
|
// EXIST: { lookupString: "forT", attributes: "bold" }
|
||||||
@@ -11,4 +11,4 @@ fun foo(s: String?) {
|
|||||||
// EXIST: { lookupString: "forNullableAny", attributes: "" }
|
// EXIST: { lookupString: "forNullableAny", attributes: "" }
|
||||||
// EXIST: { lookupString: "forString", attributes: "bold" }
|
// EXIST: { lookupString: "forString", attributes: "bold" }
|
||||||
// EXIST: { lookupString: "forAny", attributes: "" }
|
// EXIST: { lookupString: "forAny", attributes: "" }
|
||||||
// EXIST: { lookupString: "compareTo", attributes: "bold" }
|
// EXIST: { lookupString: "compareTo", attributes: "" }
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ fun foo(d: Derived) {
|
|||||||
d.<caret>
|
d.<caret>
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXIST: { itemText: "fromTrait", attributes: "bold" }
|
// EXIST: { itemText: "fromTrait", attributes: "" }
|
||||||
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
||||||
// EXIST: { itemText: "fromBase", attributes: "" }
|
// EXIST: { itemText: "fromBase", attributes: "" }
|
||||||
// EXIST: { itemText: "hashCode", attributes: "" }
|
// EXIST: { itemText: "hashCode", attributes: "" }
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ fun foo(o: Any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXIST: { itemText: "fromTrait", attributes: "bold" }
|
// EXIST: { itemText: "fromTrait", attributes: "" }
|
||||||
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
||||||
// EXIST: { itemText: "fromBase", attributes: "" }
|
// EXIST: { itemText: "fromBase", attributes: "" }
|
||||||
// EXIST: { itemText: "hashCode", attributes: "" }
|
// EXIST: { itemText: "hashCode", attributes: "" }
|
||||||
|
|||||||
+1
-1
@@ -11,6 +11,6 @@ fun foo(b: B) {
|
|||||||
b.<caret>
|
b.<caret>
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXIST: { itemText: "foo1", attributes: "bold" }
|
// EXIST: { itemText: "foo1", attributes: "" }
|
||||||
// EXIST: { itemText: "foo2", attributes: "" }
|
// EXIST: { itemText: "foo2", attributes: "" }
|
||||||
// EXIST: { itemText: "equals", attributes: "" }
|
// EXIST: { itemText: "equals", attributes: "" }
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ class Derived : Base() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EXIST: { itemText: "foo", attributes: "bold" }
|
// EXIST: { itemText: "foo", attributes: "bold" }
|
||||||
// EXIST: { itemText: "fromTrait", attributes: "bold" }
|
// EXIST: { itemText: "fromTrait", attributes: "" }
|
||||||
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
||||||
// EXIST: { itemText: "fromBase", attributes: "" }
|
// EXIST: { itemText: "fromBase", attributes: "" }
|
||||||
// EXIST: { itemText: "hashCode", attributes: "" }
|
// EXIST: { itemText: "hashCode", attributes: "" }
|
||||||
|
|||||||
+1
-1
@@ -11,4 +11,4 @@ class C {
|
|||||||
// EXIST: { itemText: "extFunForString", attributes: "bold" }
|
// EXIST: { itemText: "extFunForString", attributes: "bold" }
|
||||||
// EXIST: { itemText: "extFunForAny", attributes: "" }
|
// EXIST: { itemText: "extFunForAny", attributes: "" }
|
||||||
// EXIST: { itemText: "extFunForStringNullable", attributes: "bold" }
|
// EXIST: { itemText: "extFunForStringNullable", attributes: "bold" }
|
||||||
// EXIST: { itemText: "charAt", attributes: "bold" }
|
// EXIST: { itemText: "charAt", attributes: "" }
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ fun foo(d: Derived): String {
|
|||||||
return d.<caret>
|
return d.<caret>
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXIST: { itemText: "fromTrait", attributes: "bold" }
|
// EXIST: { itemText: "fromTrait", attributes: "" }
|
||||||
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
// EXIST: { itemText: "fromDerived", attributes: "bold" }
|
||||||
// EXIST: { itemText: "fromBase", attributes: "" }
|
// EXIST: { itemText: "fromBase", attributes: "" }
|
||||||
// EXIST: { itemText: "toString", attributes: "" }
|
// EXIST: { itemText: "toString", attributes: "" }
|
||||||
|
|||||||
+12
@@ -325,6 +325,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("InExtensionForNullable.kt")
|
||||||
|
public void testInExtensionForNullable() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InExtensionForNullable.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("InFileWithMultiDeclaration.kt")
|
@TestMetadata("InFileWithMultiDeclaration.kt")
|
||||||
public void testInFileWithMultiDeclaration() throws Exception {
|
public void testInFileWithMultiDeclaration() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InFileWithMultiDeclaration.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InFileWithMultiDeclaration.kt");
|
||||||
@@ -715,6 +721,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("NonPredictableSmartCast.kt")
|
||||||
|
public void testNonPredictableSmartCast() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/NonPredictableSmartCast.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ObjectInTypePosition.kt")
|
@TestMetadata("ObjectInTypePosition.kt")
|
||||||
public void testObjectInTypePosition() throws Exception {
|
public void testObjectInTypePosition() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ObjectInTypePosition.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ObjectInTypePosition.kt");
|
||||||
|
|||||||
+12
@@ -325,6 +325,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("InExtensionForNullable.kt")
|
||||||
|
public void testInExtensionForNullable() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InExtensionForNullable.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("InFileWithMultiDeclaration.kt")
|
@TestMetadata("InFileWithMultiDeclaration.kt")
|
||||||
public void testInFileWithMultiDeclaration() throws Exception {
|
public void testInFileWithMultiDeclaration() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InFileWithMultiDeclaration.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InFileWithMultiDeclaration.kt");
|
||||||
@@ -715,6 +721,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("NonPredictableSmartCast.kt")
|
||||||
|
public void testNonPredictableSmartCast() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/NonPredictableSmartCast.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ObjectInTypePosition.kt")
|
@TestMetadata("ObjectInTypePosition.kt")
|
||||||
public void testObjectInTypePosition() throws Exception {
|
public void testObjectInTypePosition() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ObjectInTypePosition.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ObjectInTypePosition.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user