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.resolve.BindingContext
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.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -142,8 +143,14 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
if (receiversData != null) {
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
var receiverTypes = receiversData.receivers.flatMap {
SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(it, bindingContext, moduleDescriptor, dataFlowInfo)
var receiverTypes = receiversData.receivers.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
SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, moduleDescriptor, dataFlowInfo)
}
else {
listOf(receiverValue.type)
}
}
if (receiversData.callType == CallType.SAFE) {
@@ -134,8 +134,8 @@ enum class CallableWeight {
baseClassMember,
thisTypeExtension,
baseTypeExtension,
global, // global non-extension
notApplicableReceiverNullable
globalOrStatic, // global non-extension
receiverCastRequired
}
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.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.nullability
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
public data class PackageLookupObject(val fqName: FqName) : DeclarationLookupObject {
override val psiElement: PsiElement? get() = null
@@ -87,7 +87,7 @@ public class LookupElementFactory(
private fun LookupElement.boldIfImmediate(weight: CallableWeight?): LookupElement {
val style = when (weight) {
CallableWeight.thisClassMember, CallableWeight.thisTypeExtension -> Style.BOLD
CallableWeight.notApplicableReceiverNullable -> Style.GRAYED
CallableWeight.receiverCastRequired -> Style.GRAYED
else -> Style.NORMAL
}
return if (style != Style.NORMAL) {
@@ -327,41 +327,24 @@ public class LookupElementFactory(
private fun callableWeight(descriptor: DeclarationDescriptor): CallableWeight? {
if (descriptor !is CallableDescriptor) return null
val isReceiverNullable = receiverTypes.isNotEmpty() && receiverTypes.all { it.nullability() == TypeNullability.NULLABLE }
val receiverParameter = descriptor.getExtensionReceiverParameter()
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
val overridden = descriptor.overriddenDescriptors
if (overridden.isNotEmpty()) {
return overridden.map { callableWeight(it)!! }.min()!!
}
else {
if (isReceiverNullable) {
return CallableWeight.notApplicableReceiverNullable
}
else {
if (descriptor !is CallableMemberDescriptor) {
return CallableWeight.local
}
val container = descriptor.getContainingDeclaration()
return when (container) {
is PackageFragmentDescriptor -> CallableWeight.global
val receiverParameter = descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter
if (receiverParameter != null) {
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 -> {
if (descriptor.getKind() == CallableMemberDescriptor.Kind.DECLARATION)
CallableWeight.thisClassMember
else
CallableWeight.baseClassMember
}
else -> CallableWeight.local
}
}
return when (descriptor.containingDeclaration) {
is PackageFragmentDescriptor, is ClassifierDescriptor -> CallableWeight.globalOrStatic
else -> CallableWeight.local
}
}
@@ -13,4 +13,4 @@ fun foo(o: Any?) {
// EXIST: { lookupString: "forNullableAny", attributes: "" }
// EXIST: { lookupString: "forString", attributes: "bold" }
// 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: "forString", attributes: "bold" }
// EXIST: { lookupString: "forAny", attributes: "" }
// EXIST: { lookupString: "compareTo", attributes: "bold" }
// EXIST: { lookupString: "compareTo", attributes: "" }
@@ -16,7 +16,7 @@ fun foo(d: Derived) {
d.<caret>
}
// EXIST: { itemText: "fromTrait", attributes: "bold" }
// EXIST: { itemText: "fromTrait", attributes: "" }
// EXIST: { itemText: "fromDerived", attributes: "bold" }
// EXIST: { itemText: "fromBase", 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: "fromBase", attributes: "" }
// EXIST: { itemText: "hashCode", attributes: "" }
@@ -11,6 +11,6 @@ fun foo(b: B) {
b.<caret>
}
// EXIST: { itemText: "foo1", attributes: "bold" }
// EXIST: { itemText: "foo1", attributes: "" }
// EXIST: { itemText: "foo2", attributes: "" }
// EXIST: { itemText: "equals", attributes: "" }
@@ -19,7 +19,7 @@ class Derived : Base() {
}
// EXIST: { itemText: "foo", attributes: "bold" }
// EXIST: { itemText: "fromTrait", attributes: "bold" }
// EXIST: { itemText: "fromTrait", attributes: "" }
// EXIST: { itemText: "fromDerived", attributes: "bold" }
// EXIST: { itemText: "fromBase", attributes: "" }
// EXIST: { itemText: "hashCode", attributes: "" }
@@ -11,4 +11,4 @@ class C {
// EXIST: { itemText: "extFunForString", attributes: "bold" }
// EXIST: { itemText: "extFunForAny", attributes: "" }
// 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>
}
// EXIST: { itemText: "fromTrait", attributes: "bold" }
// EXIST: { itemText: "fromTrait", attributes: "" }
// EXIST: { itemText: "fromDerived", attributes: "bold" }
// EXIST: { itemText: "fromBase", attributes: "" }
// EXIST: { itemText: "toString", attributes: "" }
@@ -325,6 +325,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
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")
public void testInFileWithMultiDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InFileWithMultiDeclaration.kt");
@@ -715,6 +721,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
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")
public void testObjectInTypePosition() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ObjectInTypePosition.kt");
@@ -325,6 +325,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
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")
public void testInFileWithMultiDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/InFileWithMultiDeclaration.kt");
@@ -715,6 +721,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
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")
public void testObjectInTypePosition() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ObjectInTypePosition.kt");