From 665904df4075ea77794bc86e2a0984e23aa754d3 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 12 Oct 2016 11:42:45 +0300 Subject: [PATCH] Introduce Variable: Do not replace assignment left-hand sides #KT-14240 Fixed GitOrigin-RevId: f2af7166af5662f5b49c7a5418a03afc67812f30 --- .../KotlinIntroduceVariableHandler.kt | 11 +++++++++ .../introduce/ExtractionTestGenerated.java | 24 +++++++++++++++++++ .../introduceVariable/arrayAssignment.kt | 8 +++++++ .../arrayAssignment.kt.after | 9 +++++++ .../introduceVariable/onAssignmentLHS.kt | 8 +++++++ .../onAssignmentLHS.kt.conflicts | 1 + .../introduceVariable/selectorAssignment.kt | 9 +++++++ .../selectorAssignment.kt.after | 10 ++++++++ .../skipUsageInAssignmentLHS.kt | 8 +++++++ .../skipUsageInAssignmentLHS.kt.after | 9 +++++++ 10 files changed, 97 insertions(+) create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt create mode 100644 plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt.after diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt index 8cd45fe570da..70d957401046 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt @@ -320,11 +320,18 @@ object KotlinIntroduceVariableHandler : RefactoringActionHandler { return commonContainer.allChildren.lastOrNull { it.textRange.contains(startOffset) } ?: return null } + private fun PsiElement.isAssignmentLHS(): Boolean { + return parents.any { KtPsiUtil.isAssignment(it) && (it as KtBinaryExpression).left == this } + } + private fun KtExpression.findOccurrences(occurrenceContainer: PsiElement): List { return toRange() .match(occurrenceContainer, KotlinPsiUnifier.DEFAULT) .mapNotNull { val candidate = it.range.elements.first() + + if (candidate.isAssignmentLHS()) return@mapNotNull null + when (candidate) { is KtExpression -> candidate is KtStringTemplateEntryWithExpression -> candidate.expression @@ -694,6 +701,10 @@ object KotlinIntroduceVariableHandler : RefactoringActionHandler { val expression = expressionToExtract?.let { KtPsiUtil.safeDeparenthesize(it) } ?: return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression")) + if (expression.isAssignmentLHS()) { + return showErrorHint(project, editor, KotlinRefactoringBundle.message("cannot.refactor.no.expression")) + } + val physicalExpression = expression.substringContextOrThis val resolutionFacade = physicalExpression.getResolutionFacade() diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java index 89f1ea49c13d..693c59b4ecc9 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java @@ -43,6 +43,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { doIntroduceVariableTest(fileName); } + @TestMetadata("arrayAssignment.kt") + public void testArrayAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/arrayAssignment.kt"); + doIntroduceVariableTest(fileName); + } + @TestMetadata("callUnderSmartCast.kt") public void testCallUnderSmartCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/callUnderSmartCast.kt"); @@ -337,6 +343,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { doIntroduceVariableTest(fileName); } + @TestMetadata("onAssignmentLHS.kt") + public void testOnAssignmentLHS() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt"); + doIntroduceVariableTest(fileName); + } + @TestMetadata("OneExplicitReceiver.kt") public void testOneExplicitReceiver() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/OneExplicitReceiver.kt"); @@ -367,6 +379,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { doIntroduceVariableTest(fileName); } + @TestMetadata("selectorAssignment.kt") + public void testSelectorAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/selectorAssignment.kt"); + doIntroduceVariableTest(fileName); + } + @TestMetadata("Simple.kt") public void testSimple() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/Simple.kt"); @@ -385,6 +403,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest { doIntroduceVariableTest(fileName); } + @TestMetadata("skipUsageInAssignmentLHS.kt") + public void testSkipUsageInAssignmentLHS() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt"); + doIntroduceVariableTest(fileName); + } + @TestMetadata("StringInjection.kt") public void testStringInjection() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/StringInjection.kt"); diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt new file mode 100644 index 000000000000..c6e23d765c9f --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME +class Foo(var bar: IntArray) + +fun test() { + val foo = Foo(IntArray(1) { 1 }) + println(foo.bar) + foo.bar[0] = foo.bar[0] + 1 +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt.after b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt.after new file mode 100644 index 000000000000..dd0fb2b50eff --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/arrayAssignment.kt.after @@ -0,0 +1,9 @@ +// WITH_RUNTIME +class Foo(var bar: IntArray) + +fun test() { + val foo = Foo(IntArray(1) { 1 }) + val message = foo.bar + println(message) + message[0] = message[0] + 1 +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt new file mode 100644 index 000000000000..bf040348c461 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME +class Foo(var bar: Int) + +fun test() { + val foo = Foo(1) + println(foo.bar) + foo.bar = foo.bar + 1 +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts new file mode 100644 index 000000000000..072400d731d7 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt new file mode 100644 index 000000000000..15e4f68d77a1 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME +class Foo(var bar: Bar) +class Bar(var baz: Int) + +fun test() { + val foo = Foo(Bar(1)) + println(foo.bar) + foo.bar.baz = foo.bar.baz + 1 +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt.after b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt.after new file mode 100644 index 000000000000..359661e7ea85 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/selectorAssignment.kt.after @@ -0,0 +1,10 @@ +// WITH_RUNTIME +class Foo(var bar: Bar) +class Bar(var baz: Int) + +fun test() { + val foo = Foo(Bar(1)) + val message = foo.bar + println(message) + message.baz = message.baz + 1 +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt new file mode 100644 index 000000000000..aaaa8932d419 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME +class Foo(var bar: Int) + +fun test() { + val foo = Foo(1) + println(foo.bar) + foo.bar = foo.bar + 1 +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt.after b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt.after new file mode 100644 index 000000000000..29adf8b30765 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt.after @@ -0,0 +1,9 @@ +// WITH_RUNTIME +class Foo(var bar: Int) + +fun test() { + val foo = Foo(1) + val message = foo.bar + println(message) + foo.bar = message + 1 +} \ No newline at end of file