ExpressionReplacementPerformer: handle string templates accurately
This commit is contained in:
@@ -17,13 +17,12 @@
|
|||||||
package org.jetbrains.kotlin.idea.codeInliner
|
package org.jetbrains.kotlin.idea.codeInliner
|
||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention
|
import org.jetbrains.kotlin.idea.intentions.ConvertToBlockBodyIntention
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -66,6 +65,36 @@ internal class ExpressionReplacementPerformer(
|
|||||||
expressionToBeReplaced: KtExpression
|
expressionToBeReplaced: KtExpression
|
||||||
) : ReplacementPerformer<KtExpression>(codeToInline, expressionToBeReplaced) {
|
) : ReplacementPerformer<KtExpression>(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? {
|
override fun doIt(postProcessing: (PsiChildRange) -> PsiChildRange): KtExpression? {
|
||||||
val insertedStatements = ArrayList<KtExpression>()
|
val insertedStatements = ArrayList<KtExpression>()
|
||||||
for (statement in codeToInline.statementsBefore) {
|
for (statement in codeToInline.statementsBefore) {
|
||||||
@@ -79,20 +108,22 @@ internal class ExpressionReplacementPerformer(
|
|||||||
insertedStatements.add(inserted)
|
insertedStatements.add(inserted)
|
||||||
}
|
}
|
||||||
|
|
||||||
val replaced = if (codeToInline.mainExpression != null) {
|
val mainExpression = codeToInline.mainExpression
|
||||||
elementToBeReplaced.replaced(codeToInline.mainExpression!!)
|
val replaced: PsiElement? = when (mainExpression) {
|
||||||
}
|
is KtStringTemplateExpression -> elementToBeReplaced.replacedWithStringTemplate(mainExpression)
|
||||||
else {
|
is KtExpression -> elementToBeReplaced.replaced(mainExpression)
|
||||||
// NB: Unit is never used as expression
|
else -> {
|
||||||
val stub = elementToBeReplaced.replaced(psiFactory.createExpression("0"))
|
// NB: Unit is never used as expression
|
||||||
val bindingContext = stub.analyze()
|
val stub = elementToBeReplaced.replaced(psiFactory.createExpression("0"))
|
||||||
val canDropElementToBeReplaced = !stub.isUsedAsExpression(bindingContext)
|
val bindingContext = stub.analyze()
|
||||||
if (canDropElementToBeReplaced) {
|
val canDropElementToBeReplaced = !stub.isUsedAsExpression(bindingContext)
|
||||||
stub.delete()
|
if (canDropElementToBeReplaced) {
|
||||||
null
|
stub.delete()
|
||||||
}
|
null
|
||||||
else {
|
}
|
||||||
stub.replace(psiFactory.createExpression("Unit"))
|
else {
|
||||||
|
stub.replace(psiFactory.createExpression("Unit"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
fun foo() {
|
||||||
|
fun bar() = ""
|
||||||
|
val y = "!!x=${<caret>bar()}!!"
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo() {
|
||||||
|
val y = "!!x=!!"
|
||||||
|
}
|
||||||
@@ -226,6 +226,12 @@ public class InlineTestGenerated extends AbstractInlineTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("WithReference.kt")
|
||||||
public void testWithReference() throws Exception {
|
public void testWithReference() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/WithReference.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/WithReference.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user