diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt index 816c4dedc75..01295c0b5c6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt @@ -156,6 +156,9 @@ fun KtExpression.getQualifiedExpressionForReceiverOrThis(): KtExpression { fun KtExpression.isDotReceiver(): Boolean = (parent as? KtDotQualifiedExpression)?.receiverExpression == this +fun KtExpression.getPossiblyQualifiedCallExpression(): KtCallExpression? = + ((this as? KtQualifiedExpression)?.selectorExpression ?: this) as? KtCallExpression + // ---------- Block expression ------------------------------------------------------------------------------------------------------------- fun KtElement.blockExpressionsOrSingle(): Sequence = diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.kt index 83b0d8fa7ca..cfa58011013 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.kt @@ -20,12 +20,14 @@ import com.google.common.collect.Lists import com.intellij.psi.MultiRangeReference import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.core.canMoveLambdaOutsideParentheses +import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses +import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtArrayAccessExpression -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.buildExpression +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getPossiblyQualifiedCallExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_GET import org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_SET @@ -55,13 +57,27 @@ class KtArrayAccessReference( override fun handleElementRename(newElementName: String?): PsiElement? { val arrayAccessExpression = expression if (OperatorNameConventions.INVOKE.asString() == newElementName) { - val callExpression = KtPsiFactory(arrayAccessExpression.project).buildExpression { - appendExpression(arrayAccessExpression.arrayExpression) + val replacement = KtPsiFactory(arrayAccessExpression.project).buildExpression { + val arrayExpression = arrayAccessExpression.arrayExpression + if (arrayExpression is KtQualifiedExpression) { + appendExpression(arrayExpression.receiverExpression) + appendFixedText(arrayExpression.operationSign.value) + appendExpression(arrayExpression.selectorExpression) + } + else { + appendExpression(arrayExpression) + } + appendFixedText("(") appendExpressions(arrayAccessExpression.indexExpressions, ",") appendFixedText(")") } - return arrayAccessExpression.replace(callExpression) + val fullCallExpression = arrayAccessExpression.replaced(replacement) + val callExpression = fullCallExpression.getPossiblyQualifiedCallExpression() + if (callExpression != null && callExpression.canMoveLambdaOutsideParentheses()) { + callExpression.moveFunctionLiteralOutsideParentheses() + } + return fullCallExpression } return this.renameImplicitConventionalCall(newElementName) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtInvokeFunctionReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtInvokeFunctionReference.kt index 2f7ceaa41d9..86173bc77c5 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtInvokeFunctionReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtInvokeFunctionReference.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -95,14 +96,19 @@ class KtInvokeFunctionReference(expression: KtCallExpression) : KtSimpleReferenc override fun handleElementRename(newElementName: String?): PsiElement? { val callExpression = expression + val fullCallExpression = callExpression.getQualifiedExpressionForSelectorOrThis() if (newElementName == OperatorNameConventions.GET.asString() && callExpression.typeArguments.isEmpty()) { val arrayAccessExpression = KtPsiFactory(callExpression).buildExpression { + if (fullCallExpression is KtQualifiedExpression) { + appendExpression(fullCallExpression.receiverExpression) + appendFixedText(fullCallExpression.operationSign.value) + } appendExpression(callExpression.calleeExpression) appendFixedText("[") appendExpressions(callExpression.valueArguments.map { it.getArgumentExpression() }) appendFixedText("]") } - return callExpression.replace(arrayAccessExpression) + return fullCallExpression.replace(arrayAccessExpression) } return renameImplicitConventionalCall(newElementName) diff --git a/idea/testData/refactoring/rename/inplace/GetToInvokeWithQualifiedExpr.kt b/idea/testData/refactoring/rename/inplace/GetToInvokeWithQualifiedExpr.kt new file mode 100644 index 00000000000..03a4f0de3e2 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToInvokeWithQualifiedExpr.kt @@ -0,0 +1,17 @@ +class WInvoke { + operator fun get(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second, body: () -> Unit) { } + +fun foo(s: Second) { + boo(s) { + s.testInvoke[{ + "Hello" + }] + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToInvokeWithQualifiedExpr.kt.after b/idea/testData/refactoring/rename/inplace/GetToInvokeWithQualifiedExpr.kt.after new file mode 100644 index 00000000000..7566c151e5d --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToInvokeWithQualifiedExpr.kt.after @@ -0,0 +1,17 @@ +class WInvoke { + operator fun invoke(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second, body: () -> Unit) { } + +fun foo(s: Second) { + boo(s) { + s.testInvoke { + "Hello" + } + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToInvokeWithSafeQualifiedExpr.kt b/idea/testData/refactoring/rename/inplace/GetToInvokeWithSafeQualifiedExpr.kt new file mode 100644 index 00000000000..4bd22b99318 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToInvokeWithSafeQualifiedExpr.kt @@ -0,0 +1,17 @@ +class WInvoke { + operator fun get(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second?, body: () -> Unit) { } + +fun foo(s: Second?) { + boo(s) { + s?.testInvoke[{ + "Hello" + }] + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToInvokeWithSafeQualifiedExpr.kt.after b/idea/testData/refactoring/rename/inplace/GetToInvokeWithSafeQualifiedExpr.kt.after new file mode 100644 index 00000000000..e952caa876a --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToInvokeWithSafeQualifiedExpr.kt.after @@ -0,0 +1,17 @@ +class WInvoke { + operator fun invoke(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second?, body: () -> Unit) { } + +fun foo(s: Second?) { + boo(s) { + s?.testInvoke { + "Hello" + } + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToGetWithQualifiedExpr.kt b/idea/testData/refactoring/rename/inplace/InvokeToGetWithQualifiedExpr.kt new file mode 100644 index 00000000000..ffc22623d27 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToGetWithQualifiedExpr.kt @@ -0,0 +1,17 @@ +class WInvoke { + operator fun invoke(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second, body: () -> Unit) { } + +fun foo(s: Second) { + boo(s) { + s.testInvoke { + "Hello" + } + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToGetWithQualifiedExpr.kt.after b/idea/testData/refactoring/rename/inplace/InvokeToGetWithQualifiedExpr.kt.after new file mode 100644 index 00000000000..06125e12e12 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToGetWithQualifiedExpr.kt.after @@ -0,0 +1,17 @@ +class WInvoke { + operator fun get(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second, body: () -> Unit) { } + +fun foo(s: Second) { + boo(s) { + s.testInvoke[{ + "Hello" + }] + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToGetWithSafeQualifiedExpr.kt b/idea/testData/refactoring/rename/inplace/InvokeToGetWithSafeQualifiedExpr.kt new file mode 100644 index 00000000000..a1e4f68bcee --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToGetWithSafeQualifiedExpr.kt @@ -0,0 +1,17 @@ +class WInvoke { + operator fun invoke(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second?, body: () -> Unit) { } + +fun foo(s: Second?) { + boo(s) { + s?.testInvoke { + "Hello" + } + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToGetWithSafeQualifiedExpr.kt.after b/idea/testData/refactoring/rename/inplace/InvokeToGetWithSafeQualifiedExpr.kt.after new file mode 100644 index 00000000000..a0ff1b1ef26 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToGetWithSafeQualifiedExpr.kt.after @@ -0,0 +1,17 @@ +class WInvoke { + operator fun get(body: () -> Unit) { } +} + +class Second { + val testInvoke = WInvoke() +} + +fun boo(s: Second?, body: () -> Unit) { } + +fun foo(s: Second?) { + boo(s) { + s?.testInvoke[{ + "Hello" + }] + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt index dbfede78f5d..b4c57226586 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt @@ -94,6 +94,14 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() { doTestMemberInplaceRename("get") } + fun testInvokeToGetWithQualifiedExpr() { + doTestMemberInplaceRename("get") + } + + fun testInvokeToGetWithSafeQualifiedExpr() { + doTestMemberInplaceRename("get") + } + fun testInvokeToPlus() { doTestMemberInplaceRename("plus") } @@ -106,6 +114,14 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() { doTestMemberInplaceRename("invoke") } + fun testGetToInvokeWithQualifiedExpr() { + doTestMemberInplaceRename("invoke") + } + + fun testGetToInvokeWithSafeQualifiedExpr() { + doTestMemberInplaceRename("invoke") + } + fun testGetToPlus() { doTestMemberInplaceRename("plus") }