Add more cases for ReplaceJavaStaticMethodWithTopLevelFunction from java.lang.Math

This commit is contained in:
Dmitry Gridin
2019-04-10 21:29:00 +07:00
parent 69120c0614
commit 50b89dd587
8 changed files with 93 additions and 10 deletions
@@ -8,8 +8,14 @@ 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
@@ -17,6 +23,7 @@ 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.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
@@ -32,25 +39,50 @@ class ReplaceJavaStaticMethodWithTopLevelFunctionInspection : AbstractKotlinInsp
val callee = call.calleeExpression ?: return
val dotQualified = call.getStrictParentOfType<KtDotQualifiedExpression>() ?: return
val calleeText = callee.text
val replacement = replacements[calleeText]
?.takeIf { call.getResolvedCall(call.analyze(BodyResolveMode.PARTIAL))?.isCalling(FqName(it.javaMethodFqName)) == true }
?: return
val replacements = replacements[calleeText] ?: return
val replacement = replacements.first()
if (call.getResolvedCall(call.analyze(BodyResolveMode.PARTIAL))?.isCalling(FqName(replacement.javaMethodFqName)) != true) return
if (replacement.toExtensionFunction && call.valueArguments.isEmpty()) return
holder.registerProblem(
dotQualified,
TextRange(0, callee.endOffset - dotQualified.startOffset),
"Should be replaced with '${replacement.kotlinFunctionShortName}()'",
ReplaceWithTopLevelFunction(replacement)
ReplaceWithTopLevelFunction(replacements)
)
})
private class ReplaceWithTopLevelFunction(private val replacement: Replacement) : LocalQuickFix {
override fun getName() = "Replace with '${replacement.kotlinFunctionShortName}()'"
private class ReplaceWithTopLevelFunction(private val replacements: List<Replacement>) : LocalQuickFix {
override fun getName() = "Replace with '${replacements.first().kotlinFunctionShortName}()'"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val dotQualified = descriptor.psiElement as? KtDotQualifiedExpression ?: return
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<Replacement>("Choose function", replacements) {
override fun onChosen(selectedValue: Replacement?, finalChoice: Boolean): PopupStep<String>? {
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 call = dotQualified.callExpression ?: return
val file = dotQualified.containingKtFile
val psiFactory = KtPsiFactory(call)
@@ -71,7 +103,7 @@ class ReplaceJavaStaticMethodWithTopLevelFunctionInspection : AbstractKotlinInsp
}
private data class Replacement(
val javaMethodFqName: String,
val javaMethodFqName: String,
val kotlinFunctionFqName: String,
val toExtensionFunction: Boolean = false
) {
@@ -80,6 +112,8 @@ class ReplaceJavaStaticMethodWithTopLevelFunctionInspection : AbstractKotlinInsp
val javaMethodShortName = javaMethodFqName.shortName()
val kotlinFunctionShortName = kotlinFunctionFqName.shortName()
override fun toString(): String = kotlinFunctionFqName
}
companion object {
@@ -100,6 +134,8 @@ class ReplaceJavaStaticMethodWithTopLevelFunctionInspection : AbstractKotlinInsp
Replacement("java.lang.Math.floor", "kotlin.math.floor"),
Replacement("java.lang.Math.hypot", "kotlin.math.hypot"),
Replacement("java.lang.Math.IEEEremainder", "kotlin.math.IEEErem", toExtensionFunction = true),
Replacement("java.lang.Math.log", "kotlin.math.ln"),
Replacement("java.lang.Math.log1p", "kotlin.math.ln1p"),
Replacement("java.lang.Math.log10", "kotlin.math.log10"),
Replacement("java.lang.Math.max", "kotlin.math.max"),
Replacement("java.lang.Math.min", "kotlin.math.min"),
@@ -109,12 +145,14 @@ class ReplaceJavaStaticMethodWithTopLevelFunctionInspection : AbstractKotlinInsp
Replacement("java.lang.Math.pow", "kotlin.math.pow", toExtensionFunction = true),
Replacement("java.lang.Math.rint", "kotlin.math.round"),
Replacement("java.lang.Math.round", "kotlin.math.roundToLong", toExtensionFunction = true),
Replacement("java.lang.Math.round", "kotlin.math.roundToInt", toExtensionFunction = true),
Replacement("java.lang.Math.signum", "kotlin.math.sign"),
Replacement("java.lang.Math.sin", "kotlin.math.sin"),
Replacement("java.lang.Math.sinh", "kotlin.math.sinh"),
Replacement("java.lang.Math.sqrt", "kotlin.math.sqrt"),
Replacement("java.lang.Math.tan", "kotlin.math.tan"),
Replacement("java.lang.Math.tanh", "kotlin.math.tanh")
).associateBy { it.javaMethodShortName }
Replacement("java.lang.Math.tanh", "kotlin.math.tanh"),
Replacement("java.lang.Math.copySign", "kotlin.math.withSign", toExtensionFunction = true)
).groupBy { it.javaMethodShortName }
}
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test(x: Double, y: Double) {
<caret>Math.copySign(x, y)
}
@@ -0,0 +1,6 @@
import kotlin.math.withSign
// WITH_RUNTIME
fun test(x: Double, y: Double) {
x.withSign(y)
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test(x: Double) {
<caret>Math.log(x)
}
@@ -0,0 +1,6 @@
import kotlin.math.ln
// WITH_RUNTIME
fun test(x: Double) {
<caret>ln(x)
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun test(x: Double) {
<caret>Math.log1p(x)
}
@@ -0,0 +1,6 @@
import kotlin.math.ln1p
// WITH_RUNTIME
fun test(x: Double) {
<caret>ln1p(x)
}
@@ -7258,6 +7258,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/ceil.kt");
}
@TestMetadata("copySign.kt")
public void testCopySign() throws Exception {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/copySign.kt");
}
@TestMetadata("cos.kt")
public void testCos() throws Exception {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/cos.kt");
@@ -7293,11 +7298,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/IEEEremainder.kt");
}
@TestMetadata("log.kt")
public void testLog() throws Exception {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/log.kt");
}
@TestMetadata("log10.kt")
public void testLog10() throws Exception {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/log10.kt");
}
@TestMetadata("log1p.kt")
public void testLog1p() throws Exception {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/log1p.kt");
}
@TestMetadata("max.kt")
public void testMax() throws Exception {
runTest("idea/testData/inspectionsLocal/replaceJavaStaticMethodWithTopLevelFunction/math/max.kt");