From 5c2ad4837553ef204015f512960e1b736ffd30d7 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 --- ChangeLog.md | 1 + .../KotlinIntroduceVariableHandler.kt | 11 +++++++++ .../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 +++++++ .../introduce/ExtractionTestGenerated.java | 24 +++++++++++++++++++ 11 files changed, 98 insertions(+) create mode 100644 idea/testData/refactoring/introduceVariable/arrayAssignment.kt create mode 100644 idea/testData/refactoring/introduceVariable/arrayAssignment.kt.after create mode 100644 idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt create mode 100644 idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts create mode 100644 idea/testData/refactoring/introduceVariable/selectorAssignment.kt create mode 100644 idea/testData/refactoring/introduceVariable/selectorAssignment.kt.after create mode 100644 idea/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt create mode 100644 idea/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt.after diff --git a/ChangeLog.md b/ChangeLog.md index b0426b7de50..7176e9c4a42 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -252,6 +252,7 @@ These artifacts include extensions for the types available in the latter JDKs, s - [`KT-14182`](https://youtrack.jetbrains.com/issue/KT-14182) Move: Show error message on applying to enum entries - Extract Function: Support implicit abnormal exits via Nothing-typed expressions - [`KT-14285`](https://youtrack.jetbrains.com/issue/KT-14285) Rename: Forbid on backing field reference +- [`KT-14240`](https://youtrack.jetbrains.com/issue/KT-14240) Introduce Variable: Do not replace assignment left-hand sides ##### New features diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt index 8cd45fe570d..70d95740104 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.kt +++ b/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/idea/testData/refactoring/introduceVariable/arrayAssignment.kt b/idea/testData/refactoring/introduceVariable/arrayAssignment.kt new file mode 100644 index 00000000000..c6e23d765c9 --- /dev/null +++ b/idea/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/idea/testData/refactoring/introduceVariable/arrayAssignment.kt.after b/idea/testData/refactoring/introduceVariable/arrayAssignment.kt.after new file mode 100644 index 00000000000..dd0fb2b50ef --- /dev/null +++ b/idea/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/idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt b/idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt new file mode 100644 index 00000000000..bf040348c46 --- /dev/null +++ b/idea/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/idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts b/idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts new file mode 100644 index 00000000000..072400d731d --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/onAssignmentLHS.kt.conflicts @@ -0,0 +1 @@ +Cannot perform refactoring without an expression diff --git a/idea/testData/refactoring/introduceVariable/selectorAssignment.kt b/idea/testData/refactoring/introduceVariable/selectorAssignment.kt new file mode 100644 index 00000000000..15e4f68d77a --- /dev/null +++ b/idea/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/idea/testData/refactoring/introduceVariable/selectorAssignment.kt.after b/idea/testData/refactoring/introduceVariable/selectorAssignment.kt.after new file mode 100644 index 00000000000..359661e7ea8 --- /dev/null +++ b/idea/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/idea/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt b/idea/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt new file mode 100644 index 00000000000..aaaa8932d41 --- /dev/null +++ b/idea/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/idea/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt.after b/idea/testData/refactoring/introduceVariable/skipUsageInAssignmentLHS.kt.after new file mode 100644 index 00000000000..29adf8b3076 --- /dev/null +++ b/idea/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 diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java index 89f1ea49c13..693c59b4ecc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/ExtractionTestGenerated.java +++ b/idea/tests/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");