From 2cdc246a274ff3fff92a7c880a905945ed6c0833 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Tue, 21 Nov 2017 23:46:25 +0900 Subject: [PATCH] Inspection to highlight usages of Collections.sort() and replace them with .sort() method from Kotlin stdlib #KT-11023 Fixed --- .../JavaCollectionsStaticMethod.html | 5 + idea/src/META-INF/plugin.xml | 8 ++ ...vaCollectionsStaticMethodCallInspection.kt | 109 ++++++++++++++++++ .../javaCollectionsStaticMethod/.inspection | 1 + .../javaCollectionsStaticMethod/fill.kt | 7 ++ .../javaCollectionsStaticMethod/fill.kt.after | 7 ++ .../javaCollectionsStaticMethod/reverse.kt | 7 ++ .../reverse.kt.after | 7 ++ .../reverseImmutableList.kt | 8 ++ .../javaCollectionsStaticMethod/shuffle.kt | 7 ++ .../shuffle.kt.after | 7 ++ .../shuffleRandom.kt | 7 ++ .../shuffleRandom.kt.after | 7 ++ .../javaCollectionsStaticMethod/sort.kt | 7 ++ .../javaCollectionsStaticMethod/sort.kt.after | 7 ++ .../sortImmutableList.kt | 8 ++ .../javaCollectionsStaticMethod/sortWith.kt | 7 ++ .../sortWith.kt.after | 7 ++ .../LocalInspectionTestGenerated.java | 57 +++++++++ 19 files changed, 280 insertions(+) create mode 100644 idea/resources/inspectionDescriptions/JavaCollectionsStaticMethod.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/.inspection create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt.after create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt.after create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverseImmutableList.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt.after create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt.after create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt.after create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortImmutableList.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt create mode 100644 idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt.after diff --git a/idea/resources/inspectionDescriptions/JavaCollectionsStaticMethod.html b/idea/resources/inspectionDescriptions/JavaCollectionsStaticMethod.html new file mode 100644 index 00000000000..ed39c8fac5d --- /dev/null +++ b/idea/resources/inspectionDescriptions/JavaCollectionsStaticMethod.html @@ -0,0 +1,5 @@ + + +This inspection reports Java Collections static method call replaceable by Kotlin stdlib, e.g. Collections.sort(list). + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 9051626d0eb..ab9a6c75de3 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2673,6 +2673,14 @@ language="kotlin" /> + ): Boolean { + if (!fqName.startsWith("java.util.Collections.")) return false + val size = args.size + return when (fqName) { + "java.util.Collections.fill" -> checkApiVersion(ApiVersion.KOTLIN_1_2, expression) && size == 2 + "java.util.Collections.reverse" -> size == 1 + "java.util.Collections.shuffle" -> checkApiVersion(ApiVersion.KOTLIN_1_2, expression) && (size == 1 || size == 2) + "java.util.Collections.sort" -> { + size == 1 || (size == 2 && args.getOrNull(1)?.getArgumentExpression() is KtLambdaExpression) + } + else -> false + } + } + + private fun checkApiVersion(requiredVersion: ApiVersion, expression: KtDotQualifiedExpression): Boolean { + val module = ModuleUtilCore.findModuleForPsiElement(expression) ?: return true + return module.languageVersionSettings.apiVersion >= requiredVersion + } + +} + +private class ReplaceWithStdLibFix(private val methodName: String, private val receiver: String) : LocalQuickFix { + override fun getName() = "Replace with $receiver.$methodName" + + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val expression = descriptor.psiElement as? KtDotQualifiedExpression ?: return + val callExpression = expression.callExpression ?: return + val valueArguments = callExpression.valueArguments + val firstArg = valueArguments.getOrNull(0)?.getArgumentExpression() ?: return + val secondArg = valueArguments.getOrNull(1)?.getArgumentExpression() + val factory = KtPsiFactory(project) + val newExpression = if (secondArg != null) { + if (methodName == "sort") + factory.createExpressionByPattern("$0.sortWith(Comparator $1)", firstArg, secondArg.text) + else + factory.createExpressionByPattern("$0.$methodName($1)", firstArg, secondArg) + } + else + factory.createExpressionByPattern("$0.$methodName()", firstArg) + expression.replace(newExpression) + } +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/.inspection b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/.inspection new file mode 100644 index 00000000000..04352475766 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.JavaCollectionsStaticMethodInspection diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt new file mode 100644 index 00000000000..3a3d6434754 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + Collections.fill(mutableList, 3) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt.after new file mode 100644 index 00000000000..a9d633b316e --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + mutableList.fill(3) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt new file mode 100644 index 00000000000..f8f62644c3b --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + Collections.reverse(mutableList) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt.after new file mode 100644 index 00000000000..3ea363f97db --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + mutableList.reverse() +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverseImmutableList.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverseImmutableList.kt new file mode 100644 index 00000000000..11019b7179a --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverseImmutableList.kt @@ -0,0 +1,8 @@ +// RUNTIME_WITH_FULL_JDK +// PROBLEM: none +import java.util.Collections + +fun test() { + val list = listOf(1, 2) + Collections.reverse(list) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt new file mode 100644 index 00000000000..c723ed256c6 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + Collections.shuffle(mutableList) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt.after new file mode 100644 index 00000000000..7848f22f3f3 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + mutableList.shuffle() +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt new file mode 100644 index 00000000000..7b376146af0 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test() { + val mutableList = mutableListOf(1, 2) + Collections.shuffle(mutableList, Random(1)) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt.after new file mode 100644 index 00000000000..dfc67cd5abe --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test() { + val mutableList = mutableListOf(1, 2) + mutableList.shuffle(Random(1)) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt new file mode 100644 index 00000000000..d3656e6c5a5 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + Collections.sort(mutableList) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt.after new file mode 100644 index 00000000000..c69a69b2eed --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + mutableList.sort() +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortImmutableList.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortImmutableList.kt new file mode 100644 index 00000000000..b5722c3be20 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortImmutableList.kt @@ -0,0 +1,8 @@ +// RUNTIME_WITH_FULL_JDK +// PROBLEM: none +import java.util.Collections + +fun test() { + val list = listOf(1, 2) + Collections.sort(list) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt new file mode 100644 index 00000000000..835a0ea3cf1 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + Collections.sort(mutableList, { a, b -> a.compareTo(b) }) +} diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt.after new file mode 100644 index 00000000000..8fd0b727b88 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.Collections + +fun test() { + val mutableList = mutableListOf(1, 2) + mutableList.sortWith(Comparator { a, b -> a.compareTo(b) }) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 532eb9f5737..764da6cc293 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -816,6 +816,63 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaCollectionsStaticMethod extends AbstractLocalInspectionTest { + public void testAllFilesPresentInJavaCollectionsStaticMethod() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/javaCollectionsStaticMethod"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("fill.kt") + public void testFill() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/fill.kt"); + doTest(fileName); + } + + @TestMetadata("reverse.kt") + public void testReverse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverse.kt"); + doTest(fileName); + } + + @TestMetadata("reverseImmutableList.kt") + public void testReverseImmutableList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/reverseImmutableList.kt"); + doTest(fileName); + } + + @TestMetadata("shuffle.kt") + public void testShuffle() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffle.kt"); + doTest(fileName); + } + + @TestMetadata("shuffleRandom.kt") + public void testShuffleRandom() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/shuffleRandom.kt"); + doTest(fileName); + } + + @TestMetadata("sort.kt") + public void testSort() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sort.kt"); + doTest(fileName); + } + + @TestMetadata("sortImmutableList.kt") + public void testSortImmutableList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortImmutableList.kt"); + doTest(fileName); + } + + @TestMetadata("sortWith.kt") + public void testSortWith() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/inspectionsLocal/kdocMissingDocumentation") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)