diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java b/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java index 7663295a17e..40d0631dc66 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/inline/KotlinInlineLocalHandler.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.plugin.refactoring.inline; +import com.google.common.collect.Sets; import com.intellij.codeInsight.highlighting.HighlightManager; import com.intellij.lang.Language; import com.intellij.lang.refactoring.InlineActionHandler; @@ -18,11 +19,14 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringMessageDialog; import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.plugin.JetLanguage; import java.util.Collection; +import java.util.Set; public class KotlinInlineLocalHandler extends InlineActionHandler { @Override @@ -36,7 +40,7 @@ public class KotlinInlineLocalHandler extends InlineActionHandler { return false; } JetProperty property = (JetProperty) element; - return property.getInitializer() != null && !property.isVar(); + return !property.isVar(); } @Override @@ -44,10 +48,40 @@ public class KotlinInlineLocalHandler extends InlineActionHandler { final JetProperty val = (JetProperty) element; String name = val.getName(); - final JetExpression initializer = val.getInitializer(); - assert initializer != null : element.getText(); + JetExpression initializerInDeclaration = val.getInitializer(); final Collection references = ReferencesSearch.search(val, GlobalSearchScope.allScope(project), false).findAll(); + + final Set assignments = Sets.newHashSet(); + for (PsiReference ref : references) { + PsiElement refElement = ref.getElement(); + PsiElement parent = refElement.getParent(); + if (parent instanceof JetBinaryExpression && + ((JetBinaryExpression) parent).getOperationToken() == JetTokens.EQ && + ((JetBinaryExpression) parent).getLeft() == refElement) { + assignments.add(parent); + } + } + + final JetExpression initializer; + if (initializerInDeclaration != null) { + initializer = initializerInDeclaration; + } + else { + if (assignments.size() == 1) { + initializer = ((JetBinaryExpression) assignments.iterator().next()).getRight(); + } + else { + initializer = null; + } + if (initializer == null) { + String key = assignments.isEmpty() ? "variable.has.no.initializer" : "variable.has.no.dominating.definition"; + String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message(key, name)); + CommonRefactoringUtil.showErrorHint(project, editor, message, RefactoringBundle.message("inline.variable.title"), HelpID.INLINE_VARIABLE); + return; + } + } + PsiReference[] referencesArray = references.toArray(references.toArray(new PsiReference[references.size()])); if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) { @@ -80,6 +114,10 @@ public class KotlinInlineLocalHandler extends InlineActionHandler { public void run() { for (PsiReference reference : references) { PsiElement referenceElement = reference.getElement(); + if (assignments.contains(referenceElement.getParent())) { + continue; + } + if (referenceElement.getParent() instanceof JetSimpleNameStringTemplateEntry && !(initializer instanceof JetSimpleNameExpression)) { referenceElement.getParent().replace(JetPsiFactory.createBlockStringTemplateEntry(project, initializer)); @@ -89,6 +127,10 @@ public class KotlinInlineLocalHandler extends InlineActionHandler { } } + for (PsiElement assignment : assignments) { + assignment.delete(); + } + val.delete(); } }); diff --git a/idea/testData/refactoring/inline/MultipleInitializers.kt b/idea/testData/refactoring/inline/MultipleInitializers.kt new file mode 100644 index 00000000000..87ae1d63e79 --- /dev/null +++ b/idea/testData/refactoring/inline/MultipleInitializers.kt @@ -0,0 +1,12 @@ +// ERROR: Cannot perform refactoring.\nCannot find a single definition to inline. + +fun f() { + val v: Int + if (true) { + v = 239 + } + else { + v = 30 + } + println(v) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/SeparateInitializer.kt b/idea/testData/refactoring/inline/SeparateInitializer.kt new file mode 100644 index 00000000000..73ecccec837 --- /dev/null +++ b/idea/testData/refactoring/inline/SeparateInitializer.kt @@ -0,0 +1,5 @@ +fun f() { + val v: Int + v = 239 + println(v) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/SeparateInitializer.kt.after b/idea/testData/refactoring/inline/SeparateInitializer.kt.after new file mode 100644 index 00000000000..2dbc7327bef --- /dev/null +++ b/idea/testData/refactoring/inline/SeparateInitializer.kt.after @@ -0,0 +1,3 @@ +fun f() { + println(239) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/SeparateInitializerInTry.kt b/idea/testData/refactoring/inline/SeparateInitializerInTry.kt new file mode 100644 index 00000000000..958487296e5 --- /dev/null +++ b/idea/testData/refactoring/inline/SeparateInitializerInTry.kt @@ -0,0 +1,8 @@ +fun f() { + val v: Int + try { + v = 239 + } finally { + } + println(v) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/SeparateInitializerInTry.kt.after b/idea/testData/refactoring/inline/SeparateInitializerInTry.kt.after new file mode 100644 index 00000000000..cf86435d599 --- /dev/null +++ b/idea/testData/refactoring/inline/SeparateInitializerInTry.kt.after @@ -0,0 +1,6 @@ +fun f() { + try { + } finally { + } + println(239) +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/UsedInAssignment.kt b/idea/testData/refactoring/inline/UsedInAssignment.kt new file mode 100644 index 00000000000..ee900db5a10 --- /dev/null +++ b/idea/testData/refactoring/inline/UsedInAssignment.kt @@ -0,0 +1,4 @@ +fun f() { + val v = 239 + vv = v +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/UsedInAssignment.kt.after b/idea/testData/refactoring/inline/UsedInAssignment.kt.after new file mode 100644 index 00000000000..ad6b52b0e87 --- /dev/null +++ b/idea/testData/refactoring/inline/UsedInAssignment.kt.after @@ -0,0 +1,3 @@ +fun f() { + vv = 239 +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/ValWithoutInitializer.kt b/idea/testData/refactoring/inline/ValWithoutInitializer.kt index 72a2abc129c..c6694b92555 100644 --- a/idea/testData/refactoring/inline/ValWithoutInitializer.kt +++ b/idea/testData/refactoring/inline/ValWithoutInitializer.kt @@ -1,3 +1,5 @@ +// ERROR: Cannot perform refactoring.\nVariable v has no initializer. + fun f() { val v: Int println(v) diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java index 7c511cd162b..95f28755736 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/AbstractInlineTest.java @@ -4,11 +4,14 @@ import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.io.FileUtil; import com.intellij.psi.PsiElement; +import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.InTextDirectivesUtils; import java.io.File; import java.io.IOException; +import java.util.List; import static com.intellij.codeInsight.TargetElementUtilBase.ELEMENT_NAME_ACCEPTED; import static com.intellij.codeInsight.TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED; @@ -25,17 +28,27 @@ public abstract class AbstractInlineTest extends LightCodeInsightFixtureTestCase TargetElementUtilBase.findTargetElement(myFixture.getEditor(), ELEMENT_NAME_ACCEPTED | REFERENCED_ELEMENT_ACCEPTED); final KotlinInlineLocalHandler handler = new KotlinInlineLocalHandler(); - assertEquals(afterFileExists, handler.canInlineElement(targetElement)); - if (!afterFileExists) { - return; - } + List expectedErrors = InTextDirectivesUtils.findLinesWithPrefixesRemoved(myFixture.getFile().getText(), "// ERROR: "); + if (handler.canInlineElement(targetElement)) { + try { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + handler.inlineElement(myFixture.getProject(), myFixture.getEditor(), targetElement); + } + }); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - handler.inlineElement(myFixture.getProject(), myFixture.getEditor(), targetElement); + assertTrue(afterFileExists); + assertEmpty(expectedErrors); + myFixture.checkResult(FileUtil.loadFile(afterFile)); + } catch (CommonRefactoringUtil.RefactoringErrorHintException e) { + assertFalse(afterFileExists); + assertEquals(1, expectedErrors.size()); + assertEquals(expectedErrors.get(0).replace("\\n", "\n"), e.getMessage()); } - }); - myFixture.checkResult(FileUtil.loadFile(afterFile)); + } + else { + assertFalse(afterFileExists); + } } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java index 0829a04fcb8..67eba4c9a4e 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/inline/InlineTestGenerated.java @@ -57,6 +57,11 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest("idea/testData/refactoring/inline/MethodReference.kt"); } + @TestMetadata("MultipleInitializers.kt") + public void testMultipleInitializers() throws Exception { + doTest("idea/testData/refactoring/inline/MultipleInitializers.kt"); + } + @TestMetadata("MultipleUsages.kt") public void testMultipleUsages() throws Exception { doTest("idea/testData/refactoring/inline/MultipleUsages.kt"); @@ -67,6 +72,21 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest("idea/testData/refactoring/inline/Parameter.kt"); } + @TestMetadata("SeparateInitializer.kt") + public void testSeparateInitializer() throws Exception { + doTest("idea/testData/refactoring/inline/SeparateInitializer.kt"); + } + + @TestMetadata("SeparateInitializerInTry.kt") + public void testSeparateInitializerInTry() throws Exception { + doTest("idea/testData/refactoring/inline/SeparateInitializerInTry.kt"); + } + + @TestMetadata("UsedInAssignment.kt") + public void testUsedInAssignment() throws Exception { + doTest("idea/testData/refactoring/inline/UsedInAssignment.kt"); + } + @TestMetadata("ValWithoutInitializer.kt") public void testValWithoutInitializer() throws Exception { doTest("idea/testData/refactoring/inline/ValWithoutInitializer.kt");