diff --git a/ChangeLog.md b/ChangeLog.md index fd932532f4b..452ee641198 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -124,6 +124,7 @@ These artifacts include extensions for the types available in the latter JDKs, s - [`KT-13553`](https://youtrack.jetbrains.com/issue/KT-13553) Move: Do not show directory selection dialog if target directory is already specified by drag-and-drop - [`KT-8867`](https://youtrack.jetbrains.com/issue/KT-8867) Rename: Rename all overridden members if user chooses to refactor base declaration(s) - Pull Up: Drop 'override' modifier if moved member doesn't override anything +- [`KT-13660`](https://youtrack.jetbrains.com/issue/KT-13660) Move: Do not drop object receivers when calling variable of extension functional type ##### New features diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt index 2301e7ac648..b45c5092237 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver @@ -428,13 +429,17 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT val selector = element.selectorExpression ?: return Skip val callee = selector.getCalleeExpressionIfAny() as? KtReferenceExpression ?: return Skip val targets = callee.targets(bindingContext) + val varAsFunResolvedCall = callee.getResolvedCall(bindingContext) as? VariableAsFunctionResolvedCall if (targets.isEmpty()) return Skip val scope = element.getResolutionScope(bindingContext, resolutionFacade) val selectorCopy = selector.copy() as KtReferenceExpression val newContext = selectorCopy.analyzeInContext(scope, selector) - val targetsWhenShort = (selectorCopy.getCalleeExpressionIfAny() as KtReferenceExpression).targets(newContext) + val newCallee = selectorCopy.getCalleeExpressionIfAny() as KtReferenceExpression + val targetsWhenShort = newCallee.targets(newContext) + val varAsFunResolvedCallWhenShort = newCallee.getResolvedCall(newContext) as? VariableAsFunctionResolvedCall val targetsMatch = targetsMatch(targets, targetsWhenShort) + && (varAsFunResolvedCall == null || resolvedCallsMatch(varAsFunResolvedCall, varAsFunResolvedCallWhenShort)) if (receiver is KtThisExpression) { if (!targetsMatch) return Skip @@ -471,6 +476,11 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT } } + private fun resolvedCallsMatch(rc1: VariableAsFunctionResolvedCall?, rc2: VariableAsFunctionResolvedCall?): Boolean { + return rc1?.variableCall?.candidateDescriptor?.asString() == rc2?.variableCall?.candidateDescriptor?.asString() && + rc1?.functionCall?.candidateDescriptor?.asString() == rc2?.functionCall?.candidateDescriptor?.asString() + } + override fun shortenElement(element: KtDotQualifiedExpression): KtElement { return element.replace(element.selectorExpression!!) as KtElement } diff --git a/idea/testData/shortenRefs/extensionFunctionVarInvokedWithQualifier.kt b/idea/testData/shortenRefs/extensionFunctionVarInvokedWithQualifier.kt new file mode 100644 index 00000000000..0d7c09d91e2 --- /dev/null +++ b/idea/testData/shortenRefs/extensionFunctionVarInvokedWithQualifier.kt @@ -0,0 +1,7 @@ +object X{ } + +class Y { + fun f(op: X.()->Unit) { + X.op() + } +} \ No newline at end of file diff --git a/idea/testData/shortenRefs/extensionFunctionVarInvokedWithQualifier.kt.after b/idea/testData/shortenRefs/extensionFunctionVarInvokedWithQualifier.kt.after new file mode 100644 index 00000000000..c762411a3b0 --- /dev/null +++ b/idea/testData/shortenRefs/extensionFunctionVarInvokedWithQualifier.kt.after @@ -0,0 +1,7 @@ +object X{ } + +class Y { + fun f(op: X.()->Unit) { + X.op() + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java b/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java index efe158b1d5e..df84d686a4b 100644 --- a/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/shortenRefs/ShortenRefsTestGenerated.java @@ -59,6 +59,12 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest { doTest(fileName); } + @TestMetadata("extensionFunctionVarInvokedWithQualifier.kt") + public void testExtensionFunctionVarInvokedWithQualifier() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/shortenRefs/extensionFunctionVarInvokedWithQualifier.kt"); + doTest(fileName); + } + @TestMetadata("InterfaceInExpression.kt") public void testInterfaceInExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/shortenRefs/InterfaceInExpression.kt");