From 615aa265f5fc2c32ecc11f7345ea1098e8a97d31 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 9 Jul 2019 16:58:35 +0300 Subject: [PATCH] Inline: do more precise check before adding this@Labeled #KT-30762 Fixed --- .../kotlin/idea/codeInliner/CodeInliner.kt | 2 +- .../kotlin/idea/codeInliner/CodeToInline.kt | 1 + .../idea/codeInliner/CodeToInlineBuilder.kt | 21 +++++++++++-------- .../refactoring/inline/function/Sequence.kt | 15 +++++++++++++ .../inline/function/Sequence.kt.after | 14 +++++++++++++ ...neAnonymousClassWithPrivateMethod.kt.after | 2 +- .../inline/InlineTestGenerated.java | 5 +++++ 7 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 idea/testData/refactoring/inline/function/Sequence.kt create mode 100644 idea/testData/refactoring/inline/function/Sequence.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt index 28331100987..82d188a1a5c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeInliner.kt @@ -99,7 +99,7 @@ class CodeInliner( receiver?.mark(RECEIVER_VALUE_KEY) - for (thisExpression in codeToInline.collectDescendantsOfType()) { + codeToInline.collectDescendantsOfType { !it[CodeToInline.SIDE_RECEIVER_USAGE_KEY] }.forEach { thisExpression -> // for this@ClassName we have only option to keep it as is (although it's sometimes incorrect but we have no other options) if (thisExpression.labelQualifier == null && receiver != null) { codeToInline.replaceExpression(thisExpression, receiver) diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInline.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInline.kt index 03c67a5b35c..ffbacdcc3ca 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInline.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInline.kt @@ -41,5 +41,6 @@ class CodeToInline( companion object { val PARAMETER_USAGE_KEY: Key = Key("PARAMETER_USAGE") val TYPE_PARAMETER_USAGE_KEY: Key = Key("TYPE_PARAMETER_USAGE") + val SIDE_RECEIVER_USAGE_KEY: Key = Key("SIDE_RECEIVER_USAGE") } } diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt index d146cfc5ad3..e10df49bc54 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.idea.codeInliner -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.core.asExpression @@ -41,6 +38,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.sure import java.util.* @@ -153,7 +151,9 @@ class CodeToInlineBuilder( } private fun processReferences(codeToInline: MutableCodeToInline, bindingContext: BindingContext, reformat: Boolean) { - val receiversToAdd = ArrayList>() + val receiversToAdd = ArrayList>() + val targetDispatchReceiverType = targetCallable.dispatchReceiverParameter?.value?.type + val targetExtensionReceiverType = targetCallable.extensionReceiverParameter?.value?.type codeToInline.forEachDescendantOfType { expression -> val target = bindingContext[BindingContext.REFERENCE_TARGET, expression] ?: return@forEachDescendantOfType @@ -180,7 +180,7 @@ class CodeToInlineBuilder( val resolutionScope = expression.getResolutionScope(bindingContext, resolutionFacade) val receiverExpression = receiver.asExpression(resolutionScope, psiFactory) if (receiverExpression != null) { - receiversToAdd.add(expression to receiverExpression) + receiversToAdd.add(Triple(expression, receiverExpression, receiver.type)) } } } @@ -188,15 +188,18 @@ class CodeToInlineBuilder( } // add receivers in reverse order because arguments of a call were processed after the callee's name - for ((expr, receiverExpression) in receiversToAdd.asReversed()) { + for ((expr, receiverExpression, receiverType) in receiversToAdd.asReversed()) { val expressionToReplace = expr.parent as? KtCallExpression ?: expr - codeToInline.replaceExpression( + val replaced = codeToInline.replaceExpression( expressionToReplace, psiFactory.createExpressionByPattern( "$0.$1", receiverExpression, expressionToReplace, reformat = reformat ) - ) + ) as? KtQualifiedExpression + if (receiverType != targetDispatchReceiverType && receiverType != targetExtensionReceiverType) { + replaced?.receiverExpression?.putCopyableUserData(CodeToInline.SIDE_RECEIVER_USAGE_KEY, Unit) + } } } } \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/Sequence.kt b/idea/testData/refactoring/inline/function/Sequence.kt new file mode 100644 index 00000000000..d98136c82bb --- /dev/null +++ b/idea/testData/refactoring/inline/function/Sequence.kt @@ -0,0 +1,15 @@ +interface MySeq { + fun yield(arg: T) +} + +fun mySeq(f: MySeq.() -> Unit): MySeq = null!! + +class Test { + fun repro() = mySeq { + inner() + } + + private fun inner() = mySeq { + yield(1.0) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/Sequence.kt.after b/idea/testData/refactoring/inline/function/Sequence.kt.after new file mode 100644 index 00000000000..fe66f6083dd --- /dev/null +++ b/idea/testData/refactoring/inline/function/Sequence.kt.after @@ -0,0 +1,14 @@ +interface MySeq { + fun yield(arg: T) +} + +fun mySeq(f: MySeq.() -> Unit): MySeq = null!! + +class Test { + fun repro() = mySeq { + mySeq { + yield(1.0) + } + } + +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/fromIntellij/InlineAnonymousClassWithPrivateMethod.kt.after b/idea/testData/refactoring/inline/function/fromIntellij/InlineAnonymousClassWithPrivateMethod.kt.after index 8eefb877dca..ea081d8e7c5 100644 --- a/idea/testData/refactoring/inline/function/fromIntellij/InlineAnonymousClassWithPrivateMethod.kt.after +++ b/idea/testData/refactoring/inline/function/fromIntellij/InlineAnonymousClassWithPrivateMethod.kt.after @@ -11,7 +11,7 @@ internal object Bar { @JvmStatic fun main(args: Array) { bar(object : Runnable { override fun run() { - Foo.doRun() + doRun() } private fun doRun() { diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java index 68ebbf4be3e..38f149ce343 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -86,6 +86,11 @@ public class InlineTestGenerated extends AbstractInlineTest { runTest("idea/testData/refactoring/inline/function/ReturnNotInTheEnd.kt"); } + @TestMetadata("Sequence.kt") + public void testSequence() throws Exception { + runTest("idea/testData/refactoring/inline/function/Sequence.kt"); + } + @TestMetadata("UnitReturnType.kt") public void testUnitReturnType() throws Exception { runTest("idea/testData/refactoring/inline/function/UnitReturnType.kt");