Fixed completion for extension function after removing members for implicit receiver from scope.

This commit is contained in:
Stanislav Erokhin
2015-08-18 15:52:08 +03:00
parent a1274f91ba
commit ec43f49ab4
9 changed files with 209 additions and 77 deletions
@@ -29,12 +29,9 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
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.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
@@ -110,6 +107,10 @@ public class ReferenceVariantsHelper(
val dataFlowInfo = context.getDataFlowInfo(expression) val dataFlowInfo = context.getDataFlowInfo(expression)
val implicitReceiverTypes = resolutionScope.getImplicitReceiversWithInstance().flatMap {
SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(it.value, context, containingDeclaration, dataFlowInfo)
}.toSet()
val pair = getExplicitReceiverData(expression) val pair = getExplicitReceiverData(expression)
if (pair != null) { if (pair != null) {
val (receiverExpression, callType) = pair val (receiverExpression, callType) = pair
@@ -126,29 +127,22 @@ public class ReferenceVariantsHelper(
context.getType(receiverExpression) context.getType(receiverExpression)
if (expressionType != null && !expressionType.isError()) { if (expressionType != null && !expressionType.isError()) {
val receiverValue = ExpressionReceiver(receiverExpression, expressionType) val receiverValue = ExpressionReceiver(receiverExpression, expressionType)
descriptors.addMembersFromReceiverAndSyntheticExtensions(receiverValue, callType, kindFilter, nameFilter, resolutionScope, dataFlowInfo) val explicitReceiverTypes = SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, context, containingDeclaration, dataFlowInfo)
descriptors.addCallableExtensions(resolutionScope, receiverValue, dataFlowInfo, callType, kindFilter, nameFilter) descriptors.processAll(implicitReceiverTypes, explicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter)
} }
} }
else { else {
// process instance members that can be called via implicit receiver's instances descriptors.processAll(implicitReceiverTypes, implicitReceiverTypes, resolutionScope, CallType.NORMAL, kindFilter, nameFilter)
val receivers = resolutionScope.getImplicitReceiversWithInstance()
val receiverValues = receivers.map { it.getValue() }
for (receiverValue in receiverValues) {
descriptors.addMembersFromReceiverAndSyntheticExtensions(receiverValue, CallType.NORMAL, kindFilter, nameFilter, resolutionScope, dataFlowInfo)
}
// process extensions and non-instance members // process non-instance members
for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) { for (descriptor in resolutionScope.getDescriptorsFiltered(kindFilter, nameFilter)) {
if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) { if (descriptor is CallableDescriptor) {
val dispatchReceiver = descriptor.getDispatchReceiverParameter() assert(descriptor.dispatchReceiverParameter == null) {
if (dispatchReceiver == null || dispatchReceiver in receivers) { "Resolution scope with member descriptor: $descriptor. Scope structure: ${JetScopeUtils.printStructure(resolutionScope)}"
descriptors.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, CallType.NORMAL, containingDeclaration))
} }
} }
else { if (!descriptor.isExtension) {
if (descriptor is CallableDescriptor && descriptor.getDispatchReceiverParameter() != null) continue // should already be processed via implicit receivers
descriptors.add(descriptor) descriptors.add(descriptor)
} }
} }
@@ -157,25 +151,47 @@ public class ReferenceVariantsHelper(
return descriptors return descriptors
} }
private fun MutableSet<DeclarationDescriptor>.addMembersFromReceiverAndSyntheticExtensions( private fun MutableSet<DeclarationDescriptor>.processAll(
receiverValue: ReceiverValue, implicitReceiverTypes: Collection<JetType>,
receiverTypes: Collection<JetType>,
resolutionScope: JetScope,
callType: CallType, callType: CallType,
kindFilter: DescriptorKindFilter, kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean, nameFilter: (Name) -> Boolean
resolutionScope: JetScope, ) {
dataFlowInfo: DataFlowInfo addNonExtensionMembers(receiverTypes, callType, kindFilter, nameFilter)
addMemberExtensions(implicitReceiverTypes, receiverTypes, callType, kindFilter, nameFilter)
addScopeAndSyntheticExtensions(resolutionScope, receiverTypes, callType, kindFilter, nameFilter)
}
private fun MutableSet<DeclarationDescriptor>.addMemberExtensions(
dispatchReceiverTypes: Collection<JetType>,
extensionReceiverTypes: Collection<JetType>,
callType: CallType,
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
) {
val memberFilter = kindFilter exclude DescriptorKindExclude.NonExtensions
for (dispatchReceiverType in dispatchReceiverTypes) {
for (member in dispatchReceiverType.memberScope.getDescriptorsFiltered(memberFilter, nameFilter)) {
addAll((member as CallableDescriptor).substituteExtensionIfCallable(extensionReceiverTypes, callType))
}
}
}
private fun MutableSet<DeclarationDescriptor>.addNonExtensionMembers(
receiverTypes: Collection<JetType>,
callType: CallType,
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
) { ) {
val memberFilter = kindFilter exclude DescriptorKindExclude.Extensions val memberFilter = kindFilter exclude DescriptorKindExclude.Extensions
val containingDeclaration = resolutionScope.getContainingDeclaration()
val receiverTypes = SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, context, containingDeclaration, dataFlowInfo)
for (receiverType in receiverTypes) { for (receiverType in receiverTypes) {
val members = receiverType.getMemberScope().getDescriptorsFiltered(DescriptorKindFilter.ALL, nameFilter) // filter by kind later because of constructors val members = receiverType.memberScope.getDescriptorsFiltered(DescriptorKindFilter.ALL, nameFilter) // filter by kind later because of constructors
for (member in members) { for (member in members) {
if (member is ClassDescriptor) { if (member is ClassDescriptor) {
if (member.isInner()) { if (member.isInner) {
member.getConstructors().filterTo(this) { callType.canCall(it) && memberFilter.accepts(it) } member.constructors.filterTo(this) { callType.canCall(it) && memberFilter.accepts(it) }
} }
} }
else if (callType.canCall(member) && memberFilter.accepts(member)) { else if (callType.canCall(member) && memberFilter.accepts(member)) {
@@ -183,24 +199,42 @@ public class ReferenceVariantsHelper(
} }
} }
} }
}
if (!kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) { private fun MutableSet<DeclarationDescriptor>.addScopeAndSyntheticExtensions(
fun processExtension(extension: CallableDescriptor) { resolutionScope: JetScope,
if (nameFilter(extension.name) && kindFilter.accepts(extension)) { receiverTypes: Collection<JetType>,
addAll(extension.substituteExtensionIfCallable(receiverValue, callType, context, dataFlowInfo, containingDeclaration)) callType: CallType,
} kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
) {
if (kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) return
fun process(extension: CallableDescriptor) {
if (nameFilter(extension.name) && kindFilter.accepts(extension)) {
addAll(extension.substituteExtensionIfCallable(receiverTypes, callType))
} }
}
if (kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK)) { for (descriptor in resolutionScope.getDescriptors(kindFilter exclude DescriptorKindExclude.NonExtensions, nameFilter)) {
for (extension in resolutionScope.getSyntheticExtensionProperties(receiverTypes)) { assert(descriptor !is CallableDescriptor || descriptor.dispatchReceiverParameter == null) {
processExtension(extension) "Resolution scope with member descriptor: $descriptor. Scope structure: ${JetScopeUtils.printStructure(resolutionScope)}"
}
} }
if (descriptor.isExtension) {
process(descriptor as CallableDescriptor)
}
}
if (kindFilter.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK)) { if (kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK)) {
for (extension in resolutionScope.getSyntheticExtensionFunctions(receiverTypes)) { for (extension in resolutionScope.getSyntheticExtensionProperties(receiverTypes)) {
processExtension(extension) process(extension)
} }
}
if (kindFilter.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK)) {
for (extension in resolutionScope.getSyntheticExtensionFunctions(receiverTypes)) {
process(extension)
} }
} }
} }
@@ -227,35 +261,6 @@ public class ReferenceVariantsHelper(
return type return type
} }
private fun MutableCollection<DeclarationDescriptor>.addCallableExtensions(
resolutionScope: JetScope,
receiver: ReceiverValue,
dataFlowInfo: DataFlowInfo,
callType: CallType,
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
) {
if (kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) return
val extensionsFilter = kindFilter.exclude(DescriptorKindExclude.NonExtensions)
fun processExtension(descriptor: DeclarationDescriptor) {
addAll((descriptor as CallableDescriptor).substituteExtensionIfCallable(receiver, callType, context, dataFlowInfo, resolutionScope.getContainingDeclaration()))
}
// process member extensions from implicit receivers separately to filter out ones from implicit receivers with no instance
for (implicitReceiver in resolutionScope.getImplicitReceiversWithInstance()) {
for (extension in implicitReceiver.getType().getMemberScope().getDescriptorsFiltered(extensionsFilter, nameFilter)) {
processExtension(extension)
}
}
for (extension in resolutionScope.getDescriptorsFiltered(extensionsFilter, nameFilter)) {
if ((extension as CallableDescriptor).getDispatchReceiverParameter() == null) { // otherwise it should already be processed via implicit receivers
processExtension(extension)
}
}
}
public fun getPackageReferenceVariants( public fun getPackageReferenceVariants(
expression: JetSimpleNameExpression, expression: JetSimpleNameExpression,
nameFilter: (Name) -> Boolean nameFilter: (Name) -> Boolean
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.typeUtil.nullability import org.jetbrains.kotlin.types.typeUtil.nullability
@@ -82,10 +83,18 @@ public fun CallableDescriptor.substituteExtensionIfCallable(
containingDeclarationOrModule: DeclarationDescriptor containingDeclarationOrModule: DeclarationDescriptor
): Collection<CallableDescriptor> { ): Collection<CallableDescriptor> {
if (!receiver.exists()) return listOf() if (!receiver.exists()) return listOf()
var types = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, containingDeclarationOrModule, dataFlowInfo)
return substituteExtensionIfCallable(types, callType)
}
public fun CallableDescriptor.substituteExtensionIfCallable(
receiverTypes: Collection<JetType>,
callType: CallType
): Collection<CallableDescriptor> {
if (!callType.canCall(this)) return listOf() if (!callType.canCall(this)) return listOf()
var types = SmartCastUtils.getSmartCastVariants(receiver, bindingContext, containingDeclarationOrModule, dataFlowInfo).asSequence() var types = receiverTypes.asSequence()
if (callType == CallType.SAFE) { if (callType == CallType.SAFE) {
types = types.map { it.makeNotNullable() } types = types.map { it.makeNotNullable() }
} }
@@ -12,4 +12,6 @@ fun f(pair: Pair<out Any, out Any>) {
// EXIST: { lookupString: "hashCode", attributes: "bold" } // EXIST: { lookupString: "hashCode", attributes: "bold" }
// EXIST: { lookupString: "forString", attributes: "grayed" } // EXIST: { lookupString: "forString", attributes: "grayed" }
// EXIST: { lookupString: "forAny", attributes: "bold" } // EXIST: { lookupString: "forAny", attributes: "bold" }
// EXIST: { lookupString: "forT", attributes: "" }
/*TODO: { lookupString: "forT", attributes: "" }*/
// EXIST: { lookupString: "forT", attributes: "grayed" }
@@ -0,0 +1,15 @@
interface A {
fun foo(): Any
fun bar()
}
interface B: A {
override fun foo(): String
}
fun f(pair: Pair<out A, out Any>) {
if (pair.first !is B) return
pair.first.<caret>
}
/*TODO: { lookupString: "foo", "typeText":"Any", attributes: "bold" } */
// EXIST: { lookupString: "foo", "typeText":"String", attributes: "bold" }
// EXIST: { lookupString: "bar", attributes: "bold" }
@@ -0,0 +1,20 @@
class B {
fun A.extFunInBForA() {}
fun B.extFunInBForB() {}
}
class A {
fun B.extFunInAForB() {}
fun A.extFunInAForA() {}
}
fun A.test() {
with(B()) {
<caret>
}
}
// EXIST: extFunInAForA
// EXIST: extFunInBForA
// EXIST: extFunInAForB
// EXIST: extFunInBForB
@@ -0,0 +1,12 @@
class A {
fun String.foo() {}
}
fun Any.test() { // see KT-8865 Member extension after this smart cast not working
if (this is A) {
"".<caret>
}
}
// EXIST: foo
@@ -0,0 +1,21 @@
interface A {
fun memberA()
}
interface B {
fun memberB()
}
interface C {
fun memberC()
}
fun C.test(a: Any) {
if (a is A && a is B) {
a.<caret>
}
}
// EXIST: memberA
// EXIST: memberB
// ABSENT: memberC
@@ -1110,6 +1110,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("NonPredictableSmartCastWithWrongReturnType.kt")
public void testNonPredictableSmartCastWithWrongReturnType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/NonPredictableSmartCastWithWrongReturnType.kt");
doTest(fileName);
}
@TestMetadata("SafeCallAfterNullable.kt") @TestMetadata("SafeCallAfterNullable.kt")
public void testSafeCallAfterNullable() throws Exception { public void testSafeCallAfterNullable() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/SafeCallAfterNullable.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/SafeCallAfterNullable.kt");
@@ -1215,6 +1221,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("MemberExtensionWithoutExplicitReceiver.kt")
public void testMemberExtensionWithoutExplicitReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/MemberExtensionWithoutExplicitReceiver.kt");
doTest(fileName);
}
@TestMetadata("MemberExtensionsFromMemberExtension.kt") @TestMetadata("MemberExtensionsFromMemberExtension.kt")
public void testMemberExtensionsFromMemberExtension() throws Exception { public void testMemberExtensionsFromMemberExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/MemberExtensionsFromMemberExtension.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/MemberExtensionsFromMemberExtension.kt");
@@ -1800,6 +1812,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/smartCast"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/smartCast"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@TestMetadata("MemberExtensionAfterThisSmartCast.kt")
public void testMemberExtensionAfterThisSmartCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/MemberExtensionAfterThisSmartCast.kt");
doTest(fileName);
}
@TestMetadata("MembersAfterSmartCast.kt")
public void testMembersAfterSmartCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/MembersAfterSmartCast.kt");
doTest(fileName);
}
@TestMetadata("SmartCastAfterIf.kt") @TestMetadata("SmartCastAfterIf.kt")
public void testSmartCastAfterIf() throws Exception { public void testSmartCastAfterIf() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/SmartCastAfterIf.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/SmartCastAfterIf.kt");
@@ -1110,6 +1110,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("NonPredictableSmartCastWithWrongReturnType.kt")
public void testNonPredictableSmartCastWithWrongReturnType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/NonPredictableSmartCastWithWrongReturnType.kt");
doTest(fileName);
}
@TestMetadata("SafeCallAfterNullable.kt") @TestMetadata("SafeCallAfterNullable.kt")
public void testSafeCallAfterNullable() throws Exception { public void testSafeCallAfterNullable() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/SafeCallAfterNullable.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/boldOrGrayed/SafeCallAfterNullable.kt");
@@ -1215,6 +1221,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("MemberExtensionWithoutExplicitReceiver.kt")
public void testMemberExtensionWithoutExplicitReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/MemberExtensionWithoutExplicitReceiver.kt");
doTest(fileName);
}
@TestMetadata("MemberExtensionsFromMemberExtension.kt") @TestMetadata("MemberExtensionsFromMemberExtension.kt")
public void testMemberExtensionsFromMemberExtension() throws Exception { public void testMemberExtensionsFromMemberExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/MemberExtensionsFromMemberExtension.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/MemberExtensionsFromMemberExtension.kt");
@@ -1800,6 +1812,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/smartCast"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/smartCast"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@TestMetadata("MemberExtensionAfterThisSmartCast.kt")
public void testMemberExtensionAfterThisSmartCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/MemberExtensionAfterThisSmartCast.kt");
doTest(fileName);
}
@TestMetadata("MembersAfterSmartCast.kt")
public void testMembersAfterSmartCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/MembersAfterSmartCast.kt");
doTest(fileName);
}
@TestMetadata("SmartCastAfterIf.kt") @TestMetadata("SmartCastAfterIf.kt")
public void testSmartCastAfterIf() throws Exception { public void testSmartCastAfterIf() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/SmartCastAfterIf.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/smartCast/SmartCastAfterIf.kt");