From f35af114239184f2ca15301445307461b2f04fe9 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 30 Jan 2018 17:41:00 +0300 Subject: [PATCH] Rename: Make improvements for "by-convention" calls - Convert between conventions call for 'get' and 'invoke' - Drop 'operator' when converting 'get'/'invoke' to something other than 'invoke'/'get' respectively #KT-12365 Fixed --- .../kotlin/psi/KtNamedDeclarationStub.java | 22 +++++++++++--- .../references/KtArrayAccessReference.java | 19 ++++++++++-- .../references/KtInvokeFunctionReference.kt | 19 +++++++++--- .../refactoring/rename/inplace/GetToFoo.kt | 7 +++++ .../rename/inplace/GetToFoo.kt.after | 7 +++++ .../refactoring/rename/inplace/GetToInvoke.kt | 7 +++++ .../rename/inplace/GetToInvoke.kt.after | 7 +++++ .../refactoring/rename/inplace/GetToPlus.kt | 7 +++++ .../rename/inplace/GetToPlus.kt.after | 7 +++++ .../refactoring/rename/inplace/InvokeToFoo.kt | 7 +++++ .../rename/inplace/InvokeToFoo.kt.after | 7 +++++ .../refactoring/rename/inplace/InvokeToGet.kt | 7 +++++ .../rename/inplace/InvokeToGet.kt.after | 7 +++++ .../rename/inplace/InvokeToPlus.kt | 7 +++++ .../rename/inplace/InvokeToPlus.kt.after | 7 +++++ .../idea/refactoring/InplaceRenameTest.kt | 30 ++++++++++++++++++- 16 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 idea/testData/refactoring/rename/inplace/GetToFoo.kt create mode 100644 idea/testData/refactoring/rename/inplace/GetToFoo.kt.after create mode 100644 idea/testData/refactoring/rename/inplace/GetToInvoke.kt create mode 100644 idea/testData/refactoring/rename/inplace/GetToInvoke.kt.after create mode 100644 idea/testData/refactoring/rename/inplace/GetToPlus.kt create mode 100644 idea/testData/refactoring/rename/inplace/GetToPlus.kt.after create mode 100644 idea/testData/refactoring/rename/inplace/InvokeToFoo.kt create mode 100644 idea/testData/refactoring/rename/inplace/InvokeToFoo.kt.after create mode 100644 idea/testData/refactoring/rename/inplace/InvokeToGet.kt create mode 100644 idea/testData/refactoring/rename/inplace/InvokeToGet.kt.after create mode 100644 idea/testData/refactoring/rename/inplace/InvokeToPlus.kt create mode 100644 idea/testData/refactoring/rename/inplace/InvokeToPlus.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtNamedDeclarationStub.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtNamedDeclarationStub.java index 6c45b49b15a..81d48c9e434 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtNamedDeclarationStub.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtNamedDeclarationStub.java @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.psi; +import com.google.common.collect.ImmutableSet; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.search.LocalSearchScope; @@ -32,6 +33,9 @@ import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt; import org.jetbrains.kotlin.psi.stubs.KotlinStubWithFqName; import org.jetbrains.kotlin.types.expressions.OperatorConventions; +import org.jetbrains.kotlin.util.OperatorNameConventions; + +import java.util.Set; import static org.jetbrains.kotlin.psi.KtPsiFactoryKt.KtPsiFactory; @@ -78,16 +82,26 @@ abstract class KtNamedDeclarationStub> extends return findChildByType(KtTokens.IDENTIFIER); } + private static final Set FUNCTIONLIKE_CONVENTIONS = ImmutableSet.of( + OperatorNameConventions.INVOKE.asString(), + OperatorNameConventions.GET.asString() + ); + + private static boolean shouldDropOperatorKeyword(String oldName, String newName) { + return !OperatorConventions.isConventionName(Name.identifier(newName)) || + FUNCTIONLIKE_CONVENTIONS.contains(oldName) != FUNCTIONLIKE_CONVENTIONS.contains(newName); + } + @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException { PsiElement identifier = getNameIdentifier(); if (identifier == null) return null; KtModifierList modifierList = getModifierList(); - if (modifierList != null && - modifierList.hasModifier(KtTokens.OPERATOR_KEYWORD) && - !OperatorConventions.isConventionName(Name.identifier(name))) { - removeModifier(KtTokens.OPERATOR_KEYWORD); + if (modifierList != null && modifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) { + if (shouldDropOperatorKeyword(getName(), name)) { + removeModifier(KtTokens.OPERATOR_KEYWORD); + } } PsiElement newIdentifier = KtPsiFactory(this).createNameIdentifierIfPossible(name); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.java index 61420f9087a..22170fba5c7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtArrayAccessReference.java @@ -27,8 +27,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.name.Name; -import org.jetbrains.kotlin.psi.KtArrayAccessExpression; -import org.jetbrains.kotlin.psi.KtContainerNode; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.util.OperatorNameConventions; @@ -98,6 +97,22 @@ public class KtArrayAccessReference extends KtSimpleReference { + pattern.appendExpression(arrayAccessExpression.getArrayExpression()); + pattern.appendFixedText("("); + pattern.appendExpressions(arrayAccessExpression.getIndexExpressions(), ","); + pattern.appendFixedText(")"); + return null; + } + ); + return arrayAccessExpression.replace(callExpression); + } + return ReferenceUtilKt.renameImplicitConventionalCall(this, 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 7d1b53c3179..2f7ceaa41d9 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 @@ -23,9 +23,7 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.Call -import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.psi.unpackFunctionLiteral +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -95,7 +93,20 @@ class KtInvokeFunctionReference(expression: KtCallExpression) : KtSimpleReferenc override fun canRename(): Boolean = true - override fun handleElementRename(newElementName: String?): PsiElement? = renameImplicitConventionalCall(newElementName) + override fun handleElementRename(newElementName: String?): PsiElement? { + val callExpression = expression + if (newElementName == OperatorNameConventions.GET.asString() && callExpression.typeArguments.isEmpty()) { + val arrayAccessExpression = KtPsiFactory(callExpression).buildExpression { + appendExpression(callExpression.calleeExpression) + appendFixedText("[") + appendExpressions(callExpression.valueArguments.map { it.getArgumentExpression() }) + appendFixedText("]") + } + return callExpression.replace(arrayAccessExpression) + } + + return renameImplicitConventionalCall(newElementName) + } companion object { diff --git a/idea/testData/refactoring/rename/inplace/GetToFoo.kt b/idea/testData/refactoring/rename/inplace/GetToFoo.kt new file mode 100644 index 00000000000..bcb08d757d5 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToFoo.kt @@ -0,0 +1,7 @@ +class A { + operator fun get(n: Int, s: String) = 1 +} + +fun test() { + A()[1, "2"] +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToFoo.kt.after b/idea/testData/refactoring/rename/inplace/GetToFoo.kt.after new file mode 100644 index 00000000000..d6bc7260abd --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToFoo.kt.after @@ -0,0 +1,7 @@ +class A { + fun foo(n: Int, s: String) = 1 +} + +fun test() { + A().foo(1, "2") +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToInvoke.kt b/idea/testData/refactoring/rename/inplace/GetToInvoke.kt new file mode 100644 index 00000000000..bcb08d757d5 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToInvoke.kt @@ -0,0 +1,7 @@ +class A { + operator fun get(n: Int, s: String) = 1 +} + +fun test() { + A()[1, "2"] +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToInvoke.kt.after b/idea/testData/refactoring/rename/inplace/GetToInvoke.kt.after new file mode 100644 index 00000000000..f578d178efd --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToInvoke.kt.after @@ -0,0 +1,7 @@ +class A { + operator fun invoke(n: Int, s: String) = 1 +} + +fun test() { + A()(1, "2") +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToPlus.kt b/idea/testData/refactoring/rename/inplace/GetToPlus.kt new file mode 100644 index 00000000000..bcb08d757d5 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToPlus.kt @@ -0,0 +1,7 @@ +class A { + operator fun get(n: Int, s: String) = 1 +} + +fun test() { + A()[1, "2"] +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/GetToPlus.kt.after b/idea/testData/refactoring/rename/inplace/GetToPlus.kt.after new file mode 100644 index 00000000000..b1f4819c4e2 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/GetToPlus.kt.after @@ -0,0 +1,7 @@ +class A { + fun plus(n: Int, s: String) = 1 +} + +fun test() { + A().plus(1, "2") +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToFoo.kt b/idea/testData/refactoring/rename/inplace/InvokeToFoo.kt new file mode 100644 index 00000000000..d3db7af3fd2 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToFoo.kt @@ -0,0 +1,7 @@ +class A { + operator fun invoke(n: Int, s: String) = 1 +} + +fun test() { + A()(1, "2") +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToFoo.kt.after b/idea/testData/refactoring/rename/inplace/InvokeToFoo.kt.after new file mode 100644 index 00000000000..d6bc7260abd --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToFoo.kt.after @@ -0,0 +1,7 @@ +class A { + fun foo(n: Int, s: String) = 1 +} + +fun test() { + A().foo(1, "2") +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToGet.kt b/idea/testData/refactoring/rename/inplace/InvokeToGet.kt new file mode 100644 index 00000000000..d3db7af3fd2 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToGet.kt @@ -0,0 +1,7 @@ +class A { + operator fun invoke(n: Int, s: String) = 1 +} + +fun test() { + A()(1, "2") +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToGet.kt.after b/idea/testData/refactoring/rename/inplace/InvokeToGet.kt.after new file mode 100644 index 00000000000..817535cb00f --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToGet.kt.after @@ -0,0 +1,7 @@ +class A { + operator fun get(n: Int, s: String) = 1 +} + +fun test() { + A()[1, "2"] +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToPlus.kt b/idea/testData/refactoring/rename/inplace/InvokeToPlus.kt new file mode 100644 index 00000000000..d3db7af3fd2 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToPlus.kt @@ -0,0 +1,7 @@ +class A { + operator fun invoke(n: Int, s: String) = 1 +} + +fun test() { + A()(1, "2") +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/inplace/InvokeToPlus.kt.after b/idea/testData/refactoring/rename/inplace/InvokeToPlus.kt.after new file mode 100644 index 00000000000..b1f4819c4e2 --- /dev/null +++ b/idea/testData/refactoring/rename/inplace/InvokeToPlus.kt.after @@ -0,0 +1,7 @@ +class A { + fun plus(n: Int, s: String) = 1 +} + +fun test() { + A().plus(1, "2") +} \ 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 f1a095a4e1f..4a4c4f06820 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt @@ -83,7 +83,31 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() { } fun testNoReformat() { - doTestInplaceRename("subject2", MemberInplaceRenameHandler()) + doTestMemberInplaceRename("subject2") + } + + fun testInvokeToFoo() { + doTestMemberInplaceRename("foo") + } + + fun testInvokeToGet() { + doTestMemberInplaceRename("get") + } + + fun testInvokeToPlus() { + doTestMemberInplaceRename("plus") + } + + fun testGetToFoo() { + doTestMemberInplaceRename("foo") + } + + fun testGetToInvoke() { + doTestMemberInplaceRename("invoke") + } + + fun testGetToPlus() { + doTestMemberInplaceRename("plus") } private fun doTestImplicitLambdaParameter(newName: String) { @@ -136,6 +160,10 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() { checkResultByFile(getTestName(false) + ".kt.after") } + private fun doTestMemberInplaceRename(newName: String?) { + doTestInplaceRename(newName, MemberInplaceRenameHandler()) + } + private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = VariableInplaceRenameHandler()) { configureByFile(getTestName(false) + ".kt") val element = TargetElementUtil.findTargetElement(