diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt index 9cf91b4d3be..82bb53c0dd5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt @@ -8,14 +8,8 @@ package org.jetbrains.kotlin.idea.inspections import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemsHolder -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.popup.JBPopupFactory -import com.intellij.openapi.ui.popup.PopupStep -import com.intellij.openapi.ui.popup.util.BaseListPopupStep import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.core.ShortenReferences @@ -23,8 +17,8 @@ import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.inspections.collections.isCalling import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.util.ImportInsertHelper -import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.callExpressionVisitor @@ -38,53 +32,34 @@ class ReplaceJavaStaticMethodWithKotlinAnalogInspection : AbstractKotlinInspecti override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = callExpressionVisitor(fun(call) { val callee = call.calleeExpression ?: return val dotQualified = call.getStrictParentOfType() ?: return - val calleeText = callee.text - val replacements = REPLACEMENTS[calleeText]?.let { list -> - val callDescriptor = call.getResolvedCall(call.analyze(BodyResolveMode.PARTIAL)) ?: return - list.filter { callDescriptor.isCalling(FqName(it.javaMethodFqName)) } - } ?: return + val replacements = REPLACEMENTS[callee.text] + ?.filter { it.filter(call) } + ?.takeIf { it.isNotEmpty() } + ?.let { list -> + val callDescriptor = call.getResolvedCall(call.analyze(BodyResolveMode.PARTIAL)) ?: return + list.filter { + callDescriptor.isCalling(FqName(it.javaMethodFqName)) && (!it.toExtensionFunction || call.valueArguments.isNotEmpty()) + } + } + ?.takeIf { it.isNotEmpty() } + ?.map { ReplaceWithKotlinAnalogFunction(it) } + ?.toTypedArray() ?: return - val replacement = replacements.firstOrNull() ?: return - if (replacement.toExtensionFunction && call.valueArguments.isEmpty()) return holder.registerProblem( dotQualified, TextRange(0, callee.endOffset - dotQualified.startOffset), - "Should be replaced with '${replacement.kotlinFunctionShortName}()'", - ReplaceWithKotlinAnalogFunction(replacements) + "Should be replaced with Kotlin function", + *replacements ) }) - private class ReplaceWithKotlinAnalogFunction(private val replacements: List) : LocalQuickFix { - override fun getName() = "Replace with '${replacements.first().kotlinFunctionShortName}()'" + private class ReplaceWithKotlinAnalogFunction(private val replacement: Replacement) : LocalQuickFix { + override fun getName() = "Replace with `${replacement.kotlinFunctionShortName}` function" - override fun getFamilyName() = name + override fun getFamilyName() = "Replace with Kotlin analog" override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val element = descriptor.psiElement ?: return - val editor = element.findExistingEditor() - if (editor != null && replacements.size > 1 && !ApplicationManager.getApplication().isUnitTestMode) { - chooseAndApplyFix(editor, project, element) - } else { - applyFix(replacements.first(), project, element) - } - } - - private fun chooseAndApplyFix(editor: Editor, project: Project, element: PsiElement) { - JBPopupFactory.getInstance().createListPopup(object : BaseListPopupStep("Choose function", replacements) { - override fun onChosen(selectedValue: Replacement?, finalChoice: Boolean): PopupStep? { - if (selectedValue == null || project.isDisposed) return null - project.executeWriteCommand(name) { - applyFix(selectedValue, project, element) - } - return null - } - - override fun getTextFor(value: Replacement) = value.kotlinFunctionFqName - }).showInBestPositionFor(editor) - } - - private fun applyFix(replacement: Replacement, project: Project, element: PsiElement) { - val dotQualified = element as? KtDotQualifiedExpression ?: return + val dotQualified = descriptor.psiElement as? KtDotQualifiedExpression ?: return val call = dotQualified.callExpression ?: return val file = dotQualified.containingKtFile val psiFactory = KtPsiFactory(call) @@ -110,7 +85,8 @@ class ReplaceJavaStaticMethodWithKotlinAnalogInspection : AbstractKotlinInspecti private data class Replacement( val javaMethodFqName: String, val kotlinFunctionFqName: String, - val toExtensionFunction: Boolean = false + val toExtensionFunction: Boolean = false, + val filter: (KtCallExpression) -> Boolean = { true } ) { private fun String.shortName() = takeLastWhile { it != '.' } @@ -130,7 +106,16 @@ class ReplaceJavaStaticMethodWithKotlinAnalogInspection : AbstractKotlinInspecti "Float" to "Float" ).flatMap { listOf( - Replacement("java.lang.${it.first}.toString", "kotlin.text.toString", toExtensionFunction = true), + Replacement( + "java.lang.${it.first}.toString", + "kotlin.text.toString", + toExtensionFunction = true + ) { call -> call.valueArguments.size == 2 }, + Replacement( + "java.lang.${it.first}.toString", + "kotlin.primitives.${it.second}.toString", + toExtensionFunction = true + ) { call -> call.valueArguments.size == 1 }, Replacement("java.lang.${it.first}.compare", "kotlin.primitives.${it.second}.compareTo", toExtensionFunction = true) ) } diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt index 604eb001e37..8887fc5eb9c 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt @@ -1,4 +1,5 @@ // WITH_RUNTIME +// FIX: Replace with `listOf` function import java.util.Arrays fun test() { diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt.after b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt.after index 63a52096845..d540f2df478 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt.after +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/asList.kt.after @@ -1,4 +1,5 @@ // WITH_RUNTIME +// FIX: Replace with `listOf` function import java.util.Arrays fun test() { diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableListOf.kt b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableListOf.kt new file mode 100644 index 00000000000..aedd313e691 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableListOf.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// FIX: Replace with `mutableListOf` function +import java.util.Arrays + +fun test() { + val a = Arrays.asList(1, 3, null) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableListOf.kt.after b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableListOf.kt.after new file mode 100644 index 00000000000..c28f5790964 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableListOf.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// FIX: Replace with `mutableListOf` function +import java.util.Arrays + +fun test() { + val a = mutableListOf(1, 3, null) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableSetOf.kt b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableSetOf.kt new file mode 100644 index 00000000000..6fe2a67576e --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableSetOf.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// MIN_JAVA_VERSION: 9 +// FIX: Replace with `mtableSetOf` function + +fun test() { + val a = java.util.Set.of() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableSetOf.kt.after b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableSetOf.kt.after new file mode 100644 index 00000000000..c4ff629c518 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableSetOf.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// MIN_JAVA_VERSION: 9 +// FIX: Replace with `mtableSetOf` function + +fun test() { + val a = mutableSetOf() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt index 539b7280f25..a2822ff24ed 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt @@ -1,5 +1,6 @@ // WITH_RUNTIME // MIN_JAVA_VERSION: 9 +// FIX: Replace with `setOf` function fun test() { val a = java.util.Set.of() diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt.after b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt.after index 57a9be11bbe..1a36ae6b43a 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt.after +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf.kt.after @@ -1,5 +1,6 @@ // WITH_RUNTIME // MIN_JAVA_VERSION: 9 +// FIX: Replace with `setOf` function fun test() { val a = setOf() diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt index 236d038205e..75392a166bf 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt @@ -1,5 +1,6 @@ // WITH_RUNTIME // MIN_JAVA_VERSION: 9 +// FIX: Replace with `setOf` function fun test() { val a = java.util.Set.of("sfsf", 25) diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt.after b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt.after index 2c6ea878621..f02c70c4906 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt.after +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/setOf2.kt.after @@ -1,5 +1,6 @@ // WITH_RUNTIME // MIN_JAVA_VERSION: 9 +// FIX: Replace with `setOf` function fun test() { val a = setOf("sfsf", 25) diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/round.kt b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt similarity index 59% rename from idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/round.kt rename to idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt index c488a3267bc..c21feb0bfac 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/round.kt +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt @@ -1,3 +1,4 @@ +// FIX: Replace with `roundToInt` function // WITH_RUNTIME fun test(x: Double) { Math.round(x) diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt.after b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt.after new file mode 100644 index 00000000000..726fbc55b52 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt.after @@ -0,0 +1,7 @@ +import kotlin.math.roundToInt + +// FIX: Replace with `roundToInt` function +// WITH_RUNTIME +fun test(x: Double) { + x.roundToInt() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt new file mode 100644 index 00000000000..313b8fd5599 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt @@ -0,0 +1,5 @@ +// FIX: Replace with `roundToLong` function +// WITH_RUNTIME +fun test(x: Double) { + Math.round(x) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/round.kt.after b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt.after similarity index 67% rename from idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/round.kt.after rename to idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt.after index 863c44182d4..f0665bb0e75 100644 --- a/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/round.kt.after +++ b/idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt.after @@ -1,5 +1,6 @@ import kotlin.math.roundToLong +// FIX: Replace with `roundToLong` function // WITH_RUNTIME fun test(x: Double) { x.roundToLong() diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 57b2d48a1e2..9c4697f955a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -7172,6 +7172,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOf.kt"); } + @TestMetadata("mutableListOf.kt") + public void testMutableListOf() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableListOf.kt"); + } + + @TestMetadata("mutableSetOf.kt") + public void testMutableSetOf() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/mutableSetOf.kt"); + } + @TestMetadata("nonArraysCopyOf.kt") public void testNonArraysCopyOf() throws Exception { runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/nonArraysCopyOf.kt"); @@ -7406,9 +7416,14 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/rint.kt"); } - @TestMetadata("round.kt") - public void testRound() throws Exception { - runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/round.kt"); + @TestMetadata("roundToInt.kt") + public void testRoundToInt() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt"); + } + + @TestMetadata("roundToLong.kt") + public void testRoundToLong() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt"); } @TestMetadata("signum.kt")