From bc580d2fd9acc9bfbf9cfc180decee610744ec69 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Wed, 11 Dec 2019 12:00:55 +0900 Subject: [PATCH] Introduce "Unused result of data class copy" inspection #KT-34121 Fixed --- .../messages/KotlinBundle.properties | 3 +- idea/resources/META-INF/inspections.xml | 8 +++++ .../UnusedDataClassCopyResult.html | 5 +++ .../UnusedDataClassCopyResultInspection.kt | 29 ++++++++++++++++ .../unusedDataClassCopyResult/.inspection | 1 + .../unusedDataClassCopyResult/basic.kt | 11 +++++++ .../unusedDataClassCopyResult/basic2.kt | 13 ++++++++ .../unusedDataClassCopyResult/used.kt | 11 +++++++ .../unusedDataClassCopyResult/used2.kt | 13 ++++++++ .../LocalInspectionTestGenerated.java | 33 +++++++++++++++++++ 10 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 idea/resources/inspectionDescriptions/UnusedDataClassCopyResult.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/UnusedDataClassCopyResultInspection.kt create mode 100644 idea/testData/inspectionsLocal/unusedDataClassCopyResult/.inspection create mode 100644 idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic.kt create mode 100644 idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic2.kt create mode 100644 idea/testData/inspectionsLocal/unusedDataClassCopyResult/used.kt create mode 100644 idea/testData/inspectionsLocal/unusedDataClassCopyResult/used2.kt diff --git a/idea/resources-en/messages/KotlinBundle.properties b/idea/resources-en/messages/KotlinBundle.properties index ed83745e44c..5862400902d 100644 --- a/idea/resources-en/messages/KotlinBundle.properties +++ b/idea/resources-en/messages/KotlinBundle.properties @@ -2216,4 +2216,5 @@ hints.codevision.inheritors.format={0, choice, 1#1 Inheritor|2#{0,number} Inheri hints.codevision.inheritors.to_many.format={0,number}+ Inheritors hints.codevision.overrides.format={0, choice, 1#1 Override|2#{0,number} Overrides} hints.codevision.overrides.to_many.format={0,number}+ Overrides -hints.codevision.settings=Settings... \ No newline at end of file +hints.codevision.settings=Settings... +inspection.unused.result.of.data.class.copy=Unused result of data class copy \ No newline at end of file diff --git a/idea/resources/META-INF/inspections.xml b/idea/resources/META-INF/inspections.xml index 56ac41ec34c..245c43ec04a 100644 --- a/idea/resources/META-INF/inspections.xml +++ b/idea/resources/META-INF/inspections.xml @@ -2563,6 +2563,14 @@ language="kotlin" key="inspection.simplifiable.scope.function.display.name" bundle="messages.KotlinBundle"/> + + \ No newline at end of file diff --git a/idea/resources/inspectionDescriptions/UnusedDataClassCopyResult.html b/idea/resources/inspectionDescriptions/UnusedDataClassCopyResult.html new file mode 100644 index 00000000000..5052b0bb0f9 --- /dev/null +++ b/idea/resources/inspectionDescriptions/UnusedDataClassCopyResult.html @@ -0,0 +1,5 @@ + + +This inspection reports unused result of data class copy function call. + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedDataClassCopyResultInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedDataClassCopyResultInspection.kt new file mode 100644 index 00000000000..5108f22a08d --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedDataClassCopyResultInspection.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.codeInspection.ProblemsHolder +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.psi.callExpressionVisitor +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver + +class UnusedDataClassCopyResultInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = callExpressionVisitor(fun(call) { + val callee = call.calleeExpression ?: return + if (callee.text != "copy") return + val context = call.analyze(BodyResolveMode.PARTIAL_WITH_CFA) + val descriptor = call.getResolvedCall(context)?.resultingDescriptor ?: return + val receiver = descriptor.dispatchReceiverParameter ?: descriptor.extensionReceiverParameter ?: return + if ((receiver.value as? ImplicitClassReceiver)?.classDescriptor?.isData != true) return + if (call.getQualifiedExpressionForSelectorOrThis().isUsedAsExpression(context)) return + holder.registerProblem(callee, KotlinBundle.message("inspection.unused.result.of.data.class.copy")) + }) +} diff --git a/idea/testData/inspectionsLocal/unusedDataClassCopyResult/.inspection b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/.inspection new file mode 100644 index 00000000000..62f6f9fd501 --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.UnusedDataClassCopyResultInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic.kt b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic.kt new file mode 100644 index 00000000000..ba37e893788 --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic.kt @@ -0,0 +1,11 @@ +// FIX: none +// WITH_RUNTIME +fun main() { + val o = Foo("") + o.copy(prop = "New") + bar(o) +} + +data class Foo(val prop: String) + +fun bar(foo: Foo) {} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic2.kt b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic2.kt new file mode 100644 index 00000000000..b474ac52b36 --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic2.kt @@ -0,0 +1,13 @@ +// FIX: none +// WITH_RUNTIME +fun main() { + val o = Foo("") + o.run { + copy(prop = "New") + bar(o) + } +} + +data class Foo(val prop: String) + +fun bar(foo: Foo) {} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/unusedDataClassCopyResult/used.kt b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/used.kt new file mode 100644 index 00000000000..9052cdfba63 --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/used.kt @@ -0,0 +1,11 @@ +// PROBLEM: none +// WITH_RUNTIME +fun main() { + val o = Foo("") + val o2 = o.copy(prop = "New") + bar(o2) +} + +data class Foo(val prop: String) + +fun bar(foo: Foo) {} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/unusedDataClassCopyResult/used2.kt b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/used2.kt new file mode 100644 index 00000000000..3b67ee410ca --- /dev/null +++ b/idea/testData/inspectionsLocal/unusedDataClassCopyResult/used2.kt @@ -0,0 +1,13 @@ +// PROBLEM: none +// WITH_RUNTIME +fun main() { + val o = Foo("") + o.run { + val o2 = copy(prop = "New") + bar(o2) + } +} + +data class Foo(val prop: String) + +fun bar(foo: Foo) {} \ 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 62ed03a94b8..d052704719d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -14026,6 +14026,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/unusedDataClassCopyResult") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class UnusedDataClassCopyResult extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInUnusedDataClassCopyResult() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/inspectionsLocal/unusedDataClassCopyResult"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + runTest("idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic.kt"); + } + + @TestMetadata("basic2.kt") + public void testBasic2() throws Exception { + runTest("idea/testData/inspectionsLocal/unusedDataClassCopyResult/basic2.kt"); + } + + @TestMetadata("used.kt") + public void testUsed() throws Exception { + runTest("idea/testData/inspectionsLocal/unusedDataClassCopyResult/used.kt"); + } + + @TestMetadata("used2.kt") + public void testUsed2() throws Exception { + runTest("idea/testData/inspectionsLocal/unusedDataClassCopyResult/used2.kt"); + } + } + @TestMetadata("idea/testData/inspectionsLocal/unusedEquals") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)