diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 2d624ccabe5..b5bb8e97122 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -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) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt index 76c94da377b..f018ce9fb6b 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt @@ -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("CALLABLE_WEIGHT_KEY") diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt index f3c657e0e8b..bca88cf5c9d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt @@ -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 } } diff --git a/idea/idea-completion/testData/basic/common/AfterNullableAutoCast.kt b/idea/idea-completion/testData/basic/common/AfterNullableAutoCast.kt index ac64bb9b10f..bf2a071df2c 100644 --- a/idea/idea-completion/testData/basic/common/AfterNullableAutoCast.kt +++ b/idea/idea-completion/testData/basic/common/AfterNullableAutoCast.kt @@ -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: "" } diff --git a/idea/idea-completion/testData/basic/common/InExtensionForNullable.kt b/idea/idea-completion/testData/basic/common/InExtensionForNullable.kt new file mode 100644 index 00000000000..d2b1b2da879 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/InExtensionForNullable.kt @@ -0,0 +1,11 @@ +fun String.forString(){} + +fun globalFun(){} + +fun String?.foo() { + +} + +// EXIST: { lookupString: "globalFun", attributes: "" } +// EXIST: { lookupString: "compareTo", attributes: "grayed" } +// EXIST: { lookupString: "forString", attributes: "grayed" } diff --git a/idea/idea-completion/testData/basic/common/NonPredictableSmartCast.kt b/idea/idea-completion/testData/basic/common/NonPredictableSmartCast.kt new file mode 100644 index 00000000000..57d2508257c --- /dev/null +++ b/idea/idea-completion/testData/basic/common/NonPredictableSmartCast.kt @@ -0,0 +1,15 @@ +fun String.forString(){} +fun Any.forAny(){} + +fun T.forT() {} + +fun f(pair: Pair) { + if (pair.first !is String) return + pair.first. +} + +// 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" } diff --git a/idea/idea-completion/testData/basic/common/SafeCallAfterNullable.kt b/idea/idea-completion/testData/basic/common/SafeCallAfterNullable.kt index 1e4b1b89792..baa4cf512b8 100644 --- a/idea/idea-completion/testData/basic/common/SafeCallAfterNullable.kt +++ b/idea/idea-completion/testData/basic/common/SafeCallAfterNullable.kt @@ -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: "" } diff --git a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers1.kt b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers1.kt index 26294df498a..e59ecce0bdf 100644 --- a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers1.kt +++ b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers1.kt @@ -16,7 +16,7 @@ fun foo(d: Derived) { d. } -// EXIST: { itemText: "fromTrait", attributes: "bold" } +// EXIST: { itemText: "fromTrait", attributes: "" } // EXIST: { itemText: "fromDerived", attributes: "bold" } // EXIST: { itemText: "fromBase", attributes: "" } // EXIST: { itemText: "hashCode", attributes: "" } diff --git a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers2.kt b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers2.kt index c09d06275c5..ac4dae6c718 100644 --- a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers2.kt +++ b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers2.kt @@ -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: "" } diff --git a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers4.kt b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers4.kt index 4c34d7bdd47..2daa6f8f8cf 100644 --- a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers4.kt +++ b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers4.kt @@ -11,6 +11,6 @@ fun foo(b: B) { b. } -// EXIST: { itemText: "foo1", attributes: "bold" } +// EXIST: { itemText: "foo1", attributes: "" } // EXIST: { itemText: "foo2", attributes: "" } // EXIST: { itemText: "equals", attributes: "" } diff --git a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers5.kt b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers5.kt index 94faa90d7f8..342ea6c64d9 100644 --- a/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers5.kt +++ b/idea/idea-completion/testData/basic/common/immediateMembers/ImmediateMembers5.kt @@ -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: "" } diff --git a/idea/idea-completion/testData/basic/java/ImmediateMembersForPlatformType.kt b/idea/idea-completion/testData/basic/java/ImmediateMembersForPlatformType.kt index ff1c4b5436a..92928d5c5f1 100644 --- a/idea/idea-completion/testData/basic/java/ImmediateMembersForPlatformType.kt +++ b/idea/idea-completion/testData/basic/java/ImmediateMembersForPlatformType.kt @@ -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: "" } diff --git a/idea/idea-completion/testData/smart/ImmediateMembers.kt b/idea/idea-completion/testData/smart/ImmediateMembers.kt index 699e22aab21..85165c9a9e6 100644 --- a/idea/idea-completion/testData/smart/ImmediateMembers.kt +++ b/idea/idea-completion/testData/smart/ImmediateMembers.kt @@ -16,7 +16,7 @@ fun foo(d: Derived): String { return d. } -// EXIST: { itemText: "fromTrait", attributes: "bold" } +// EXIST: { itemText: "fromTrait", attributes: "" } // EXIST: { itemText: "fromDerived", attributes: "bold" } // EXIST: { itemText: "fromBase", attributes: "" } // EXIST: { itemText: "toString", attributes: "" } diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index 46c6dca1fe2..f86e4b0cbfb 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -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"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 60b2884dd9f..67e2363c86b 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -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");