From 91f7f199027c1e1f1a4f8ffd612862b626b8962f Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 13 Apr 2017 13:36:27 +0300 Subject: [PATCH] ExpressionReplacementPerformer: handle string templates accurately --- .../idea/codeInliner/ReplacementPerformer.kt | 65 ++++++++++++++----- .../function/expressionBody/StringTemplate.kt | 4 ++ .../expressionBody/StringTemplate.kt.after | 3 + .../inline/InlineTestGenerated.java | 6 ++ 4 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt create mode 100644 idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt index f5ba69cd4e4..35a710017b5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/ReplacementPerformer.kt @@ -17,13 +17,12 @@ package org.jetbrains.kotlin.idea.codeInliner import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange -import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType -import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import java.util.* @@ -66,6 +65,36 @@ internal class ExpressionReplacementPerformer( expressionToBeReplaced: KtExpression ) : ReplacementPerformer(codeToInline, expressionToBeReplaced) { + fun KtExpression.replacedWithStringTemplate(templateExpression: KtStringTemplateExpression): KtExpression? { + val parent = this.parent + + return if (parent is KtStringTemplateEntryWithExpression + // Do not mix raw and non-raw templates + && parent.parent.firstChild.text == templateExpression.firstChild.text) { + + val entriesToAdd = templateExpression.entries + val grandParentTemplateExpression = parent.parent as KtStringTemplateExpression + val result = if (entriesToAdd.isNotEmpty()) { + grandParentTemplateExpression.addRangeBefore(entriesToAdd.first(), entriesToAdd.last(), parent) + val lastNewEntry = parent.prevSibling + val nextElement = parent.nextSibling + if (lastNewEntry is KtSimpleNameStringTemplateEntry && + lastNewEntry.expression != null && + !canPlaceAfterSimpleNameEntry(nextElement)) { + lastNewEntry.replace(KtPsiFactory(this).createBlockStringTemplateEntry(lastNewEntry.expression!!)) + } + grandParentTemplateExpression + } + else null + + parent.delete() + result + } + else { + replaced(templateExpression) + } + } + override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtExpression? { val insertedStatements = ArrayList() for (statement in codeToInline.statementsBefore) { @@ -79,20 +108,22 @@ internal class ExpressionReplacementPerformer( insertedStatements.add(inserted) } - val replaced = if (codeToInline.mainExpression != null) { - elementToBeReplaced.replaced(codeToInline.mainExpression!!) - } - else { - // NB: Unit is never used as expression - val stub = elementToBeReplaced.replaced(psiFactory.createExpression("0")) - val bindingContext = stub.analyze() - val canDropElementToBeReplaced = !stub.isUsedAsExpression(bindingContext) - if (canDropElementToBeReplaced) { - stub.delete() - null - } - else { - stub.replace(psiFactory.createExpression("Unit")) + val mainExpression = codeToInline.mainExpression + val replaced: PsiElement? = when (mainExpression) { + is KtStringTemplateExpression -> elementToBeReplaced.replacedWithStringTemplate(mainExpression) + is KtExpression -> elementToBeReplaced.replaced(mainExpression) + else -> { + // NB: Unit is never used as expression + val stub = elementToBeReplaced.replaced(psiFactory.createExpression("0")) + val bindingContext = stub.analyze() + val canDropElementToBeReplaced = !stub.isUsedAsExpression(bindingContext) + if (canDropElementToBeReplaced) { + stub.delete() + null + } + else { + stub.replace(psiFactory.createExpression("Unit")) + } } } diff --git a/idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt b/idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt new file mode 100644 index 00000000000..22032f4f0ec --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt @@ -0,0 +1,4 @@ +fun foo() { + fun bar() = "" + val y = "!!x=${bar()}!!" +} diff --git a/idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt.after b/idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt.after new file mode 100644 index 00000000000..97b4b04dcb4 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val y = "!!x=!!" +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java index 583bc92db2c..c4d366afe43 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -226,6 +226,12 @@ public class InlineTestGenerated extends AbstractInlineTest { doTest(fileName); } + @TestMetadata("StringTemplate.kt") + public void testStringTemplate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/StringTemplate.kt"); + doTest(fileName); + } + @TestMetadata("WithReference.kt") public void testWithReference() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/WithReference.kt");