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:
Valentin Kipyatkov
2015-08-12 21:51:24 +03:00
parent b21226ac1c
commit 9e26d0eacf
15 changed files with 87 additions and 47 deletions
@@ -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")
@@ -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: "" }
@@ -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: "" }
@@ -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: "" }
@@ -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: "" }
@@ -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: "" }
@@ -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
View File
@@ -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: "" }
@@ -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");
@@ -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");