diff --git a/idea/resources/inspectionDescriptions/RemoveSingleExpressionStringTemplate.html b/idea/resources/inspectionDescriptions/RemoveSingleExpressionStringTemplate.html new file mode 100644 index 00000000000..25a5f780a57 --- /dev/null +++ b/idea/resources/inspectionDescriptions/RemoveSingleExpressionStringTemplate.html @@ -0,0 +1,5 @@ + + +This inspection reports single-expression String template that can be safely removed + + diff --git a/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/after.kt.template b/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/after.kt.template new file mode 100644 index 00000000000..465df185e8a --- /dev/null +++ b/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/after.kt.template @@ -0,0 +1,4 @@ +fun foo(x: Int, y: String) { + val z = y + val w = x.toString() +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/before.kt.template b/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/before.kt.template new file mode 100644 index 00000000000..b2246682813 --- /dev/null +++ b/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/before.kt.template @@ -0,0 +1,4 @@ +fun foo(x: Int, y: String) { + val z = "$y" + val w = "$x" +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/description.html b/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/description.html new file mode 100644 index 00000000000..b53507c1360 --- /dev/null +++ b/idea/resources/intentionDescriptions/RemoveSingleExpressionStringTemplateIntention/description.html @@ -0,0 +1,5 @@ + + +This intention replaces single-expression string templates with expression itself, with toString() if necessary + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 81070fed4e0..5b3cb0709ce 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1273,6 +1273,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.RemoveSingleExpressionStringTemplateIntention + Kotlin + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveSingleExpressionStringTemplateIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveSingleExpressionStringTemplateIntention.kt new file mode 100644 index 00000000000..36c19296684 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveSingleExpressionStringTemplateIntention.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.intentions + +import com.intellij.openapi.editor.Editor +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtStringTemplateExpression +import org.jetbrains.kotlin.psi.createExpressionByPattern +import org.jetbrains.kotlin.resolve.calls.callUtil.getType + +private fun KtStringTemplateExpression.singleExpressionOrNull() = + children.singleOrNull()?.children?.firstOrNull() as? KtExpression + +class RemoveSingleExpressionStringTemplateInspection : IntentionBasedInspection( + RemoveSingleExpressionStringTemplateIntention() +) { + override val problemText = "Single-expression string template" +} + +class RemoveSingleExpressionStringTemplateIntention : SelfTargetingOffsetIndependentIntention( + KtStringTemplateExpression::class.java, + "Remove single-expression string template" +) { + override fun isApplicableTo(element: KtStringTemplateExpression) = + element.singleExpressionOrNull() != null + + override fun applyTo(element: KtStringTemplateExpression, editor: Editor?) { + val expression = element.singleExpressionOrNull() ?: return + val type = expression.getType(expression.analyze()) + val newElement = + if (KotlinBuiltIns.isString(type)) expression + else KtPsiFactory(element).createExpressionByPattern("$0.$1()", expression, "toString") + element.replace(newElement) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/.intention b/idea/testData/intentions/removeSingleExpressionStringTemplate/.intention new file mode 100644 index 00000000000..9520a89c788 --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.RemoveSingleExpressionStringTemplateIntention \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/emptyString.kt b/idea/testData/intentions/removeSingleExpressionStringTemplate/emptyString.kt new file mode 100644 index 00000000000..8e9da61b35c --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/emptyString.kt @@ -0,0 +1,3 @@ +// IS_APPLICABLE: FALSE + +val bar = "" \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/justString.kt b/idea/testData/intentions/removeSingleExpressionStringTemplate/justString.kt new file mode 100644 index 00000000000..048cd6d7db2 --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/justString.kt @@ -0,0 +1,3 @@ +// IS_APPLICABLE: FALSE + +val bar = "Hello" \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/multipleStringTemplate.kt b/idea/testData/intentions/removeSingleExpressionStringTemplate/multipleStringTemplate.kt new file mode 100644 index 00000000000..92ed7d7a091 --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/multipleStringTemplate.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: FALSE + +val foo = "foo" +val bar = "$foo$foo" \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplate.kt b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplate.kt new file mode 100644 index 00000000000..48c6500b724 --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplate.kt @@ -0,0 +1,4 @@ +// INTENTION_TEXT: Remove single-expression string template + +val foo = "foo" +val bar = "$foo" \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplate.kt.after b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplate.kt.after new file mode 100644 index 00000000000..bfa18b703b2 --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplate.kt.after @@ -0,0 +1,4 @@ +// INTENTION_TEXT: Remove single-expression string template + +val foo = "foo" +val bar = foo \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithMethodCall.kt b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithMethodCall.kt new file mode 100644 index 00000000000..1e85a3420f5 --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithMethodCall.kt @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Remove single-expression string template + +val bar = "${1.hashCode().toString()}" \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithMethodCall.kt.after b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithMethodCall.kt.after new file mode 100644 index 00000000000..25143a708fe --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithMethodCall.kt.after @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Remove single-expression string template + +val bar = 1.hashCode().toString() \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithNonStringType.kt b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithNonStringType.kt new file mode 100644 index 00000000000..366d5f4fd95 --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithNonStringType.kt @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Remove single-expression string template + +val bar = "${1.hashCode()}" \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithNonStringType.kt.after b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithNonStringType.kt.after new file mode 100644 index 00000000000..25143a708fe --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithNonStringType.kt.after @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Remove single-expression string template + +val bar = 1.hashCode().toString() \ No newline at end of file diff --git a/idea/testData/intentions/removeSingleExpressionStringTemplate/stringTemplateWithText.kt b/idea/testData/intentions/removeSingleExpressionStringTemplate/stringTemplateWithText.kt new file mode 100644 index 00000000000..d3ddbf1a3de --- /dev/null +++ b/idea/testData/intentions/removeSingleExpressionStringTemplate/stringTemplateWithText.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: FALSE + +val foo = "foo" +val bar = "text$foo" \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index ce8a636d04f..df90a370410 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -8480,6 +8480,57 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RemoveSingleExpressionStringTemplate extends AbstractIntentionTest { + public void testAllFilesPresentInRemoveSingleExpressionStringTemplate() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/removeSingleExpressionStringTemplate"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("emptyString.kt") + public void testEmptyString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate/emptyString.kt"); + doTest(fileName); + } + + @TestMetadata("justString.kt") + public void testJustString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate/justString.kt"); + doTest(fileName); + } + + @TestMetadata("multipleStringTemplate.kt") + public void testMultipleStringTemplate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate/multipleStringTemplate.kt"); + doTest(fileName); + } + + @TestMetadata("singleExpressionStringTemplate.kt") + public void testSingleExpressionStringTemplate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplate.kt"); + doTest(fileName); + } + + @TestMetadata("singleExpressionStringTemplateWithMethodCall.kt") + public void testSingleExpressionStringTemplateWithMethodCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithMethodCall.kt"); + doTest(fileName); + } + + @TestMetadata("singleExpressionStringTemplateWithNonStringType.kt") + public void testSingleExpressionStringTemplateWithNonStringType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate/singleExpressionStringTemplateWithNonStringType.kt"); + doTest(fileName); + } + + @TestMetadata("stringTemplateWithText.kt") + public void testStringTemplateWithText() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeSingleExpressionStringTemplate/stringTemplateWithText.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/removeUnnecessaryParentheses") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)