diff --git a/idea/resources/inspectionDescriptions/ReplaceToStringWithStringTemplate.html b/idea/resources/inspectionDescriptions/ReplaceToStringWithStringTemplate.html new file mode 100644 index 00000000000..1d1d6dae28a --- /dev/null +++ b/idea/resources/inspectionDescriptions/ReplaceToStringWithStringTemplate.html @@ -0,0 +1,5 @@ + + +This inspection reports toString function calls replaceable with string template. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 4211a54f10d..d07033c49ff 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2900,6 +2900,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.172 b/idea/src/META-INF/plugin.xml.172 index ff371b58ad9..0b6483e17f2 100644 --- a/idea/src/META-INF/plugin.xml.172 +++ b/idea/src/META-INF/plugin.xml.172 @@ -2898,6 +2898,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.173 b/idea/src/META-INF/plugin.xml.173 index 0134669dc32..37720d79315 100644 --- a/idea/src/META-INF/plugin.xml.173 +++ b/idea/src/META-INF/plugin.xml.173 @@ -2898,6 +2898,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.182 b/idea/src/META-INF/plugin.xml.182 index 89fe893b8a4..329fc96bf59 100644 --- a/idea/src/META-INF/plugin.xml.182 +++ b/idea/src/META-INF/plugin.xml.182 @@ -2899,6 +2899,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.as31 b/idea/src/META-INF/plugin.xml.as31 index bd56da26a6a..3b3cadef1c6 100644 --- a/idea/src/META-INF/plugin.xml.as31 +++ b/idea/src/META-INF/plugin.xml.as31 @@ -2898,6 +2898,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.as32 b/idea/src/META-INF/plugin.xml.as32 index ceace052246..73934004f5c 100644 --- a/idea/src/META-INF/plugin.xml.as32 +++ b/idea/src/META-INF/plugin.xml.as32 @@ -2898,6 +2898,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceToStringWithStringTemplateInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceToStringWithStringTemplateInspection.kt new file mode 100644 index 00000000000..550b9e5dcc8 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceToStringWithStringTemplateInspection.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.inspections + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations +import org.jetbrains.kotlin.idea.intentions.toResolvedCall +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +class ReplaceToStringWithStringTemplateInspection : AbstractApplicabilityBasedInspection( + KtDotQualifiedExpression::class.java +) { + override fun isApplicable(element: KtDotQualifiedExpression): Boolean { + if (element.receiverExpression !is KtReferenceExpression) return false + if (element.parent is KtBlockStringTemplateEntry) return false + return element.isToString() + } + + override fun applyTo(element: PsiElement, project: Project, editor: Editor?) { + val expression = element.getParentOfType(strict = false) ?: return + val variable = expression.receiverExpression.text + element.replace(KtPsiFactory(element).createExpression("\"$$variable\"")) + } + + override fun inspectionText(element: KtDotQualifiedExpression) = "Should be replaced 'toString' with string template" + + override fun inspectionTarget(element: KtDotQualifiedExpression) = element + + override val defaultFixText = "Replace 'toString' with string template" + + private fun KtDotQualifiedExpression.isToString(): Boolean { + val resolvedCall = toResolvedCall(BodyResolveMode.PARTIAL) ?: return false + val callableDescriptor = resolvedCall.resultingDescriptor as? CallableMemberDescriptor ?: return false + return callableDescriptor.getDeepestSuperDeclarations().any { it.fqNameUnsafe.asString() == "kotlin.Any.toString" } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/.inspection b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/.inspection new file mode 100644 index 00000000000..6b03c3c7f04 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.ReplaceToStringWithStringTemplateInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/nonReference.kt b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/nonReference.kt new file mode 100644 index 00000000000..5fc21684259 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/nonReference.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME +// PROBLEM: none +fun test(): String { + return 1.toString() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/simple.kt b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/simple.kt new file mode 100644 index 00000000000..bda00904af8 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/simple.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME +fun test(): String { + val x = 1 + return x.toString() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/simple.kt.after b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/simple.kt.after new file mode 100644 index 00000000000..e15a79eaf50 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/simple.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME +fun test(): String { + val x = 1 + return "$x" +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/stringTemplate.kt b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/stringTemplate.kt new file mode 100644 index 00000000000..aae8802c343 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/stringTemplate.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME +// PROBLEM: none +fun test(): String { + val x = 1 + return "Foo: ${x.toString()}" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 86c06e1af8f..050dbba6df6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -4206,6 +4206,34 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/replaceToStringWithStringTemplate") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ReplaceToStringWithStringTemplate extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInReplaceToStringWithStringTemplate() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/replaceToStringWithStringTemplate"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("nonReference.kt") + public void testNonReference() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/nonReference.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/simple.kt"); + } + + @TestMetadata("stringTemplate.kt") + public void testStringTemplate() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceToStringWithStringTemplate/stringTemplate.kt"); + } + } + @TestMetadata("idea/testData/inspectionsLocal/replaceToWithInfixForm") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)