From 2099cf697d1605b0bcab1ea2a28297360409ddaf Mon Sep 17 00:00:00 2001 From: Alexey Belkov Date: Thu, 25 May 2023 16:52:10 +0400 Subject: [PATCH] [kotlin] "Java methods should be replaced with Kotlin analog": add special handling for functions that change semantics ^KTIJ-25643 Fixed ^KTIJ-25633 Fixed GitOrigin-RevId: f93cf6690484eeb10e3a6261c68da98c5dedc0f7 --- ...aStaticMethodWithKotlinAnalogInspection.kt | 28 +++++++++++++------ .../collections/copyOfRange.kt | 1 + .../collections/copyOfRange.kt.after | 1 + .../math/roundToInt.kt | 2 +- .../math/roundToInt.kt.after | 2 +- .../math/roundToLong.kt | 2 +- .../math/roundToLong.kt.after | 2 +- 7 files changed, 26 insertions(+), 12 deletions(-) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/jdk2k/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/jdk2k/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt index 8abf7c250d5e..23009e435ec9 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/jdk2k/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/jdk2k/ReplaceJavaStaticMethodWithKotlinAnalogInspection.kt @@ -1,21 +1,24 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.inspections.jdk2k import com.intellij.codeInsight.intention.FileModifier import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING +import com.intellij.codeInspection.ProblemHighlightType.INFORMATION import com.intellij.codeInspection.ProblemsHolder +import com.intellij.java.JavaBundle import com.intellij.openapi.project.Project import org.jetbrains.kotlin.config.ApiVersion.Companion.KOTLIN_1_8 import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings +import org.jetbrains.kotlin.idea.base.psi.textRangeIn import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.idea.inspections.collections.isCalling -import org.jetbrains.kotlin.idea.base.psi.textRangeIn import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression @@ -46,16 +49,24 @@ class ReplaceJavaStaticMethodWithKotlinAnalogInspection : AbstractKotlinInspecti ?.map(::ReplaceWithKotlinAnalogFunction) ?.toTypedArray() ?: return + val highlightType = if (replacements.any { it.mayChangeSemantics }) INFORMATION else GENERIC_ERROR_OR_WARNING holder.registerProblem( call, - callee.textRangeIn(call), KotlinBundle.message("should.be.replaced.with.kotlin.function"), + highlightType, + callee.textRangeIn(call), *replacements ) }) private class ReplaceWithKotlinAnalogFunction(private val replacement: Replacement) : LocalQuickFix { - override fun getName() = KotlinBundle.message("replace.with.kotlin.analog.function.text", replacement.kotlinFunctionShortName) + val mayChangeSemantics: Boolean + get() = replacement.mayChangeSemantics + + override fun getName(): String { + val suffix = if (mayChangeSemantics) JavaBundle.message("quickfix.text.suffix.may.change.semantics") else "" + return KotlinBundle.message("replace.with.kotlin.analog.function.text", replacement.kotlinFunctionShortName) + suffix + } override fun getFamilyName() = KotlinBundle.message("replace.with.kotlin.analog.function.family.name") @@ -144,8 +155,8 @@ class ReplaceJavaStaticMethodWithKotlinAnalogInspection : AbstractKotlinInspecti Replacement("java.lang.Math.nextUp", "kotlin.math.nextUp", ToExtensionFunctionWithNonNullableReceiver), Replacement("java.lang.Math.pow", "kotlin.math.pow", ToExtensionFunctionWithNonNullableReceiver), Replacement("java.lang.Math.rint", "kotlin.math.round"), - Replacement("java.lang.Math.round", "kotlin.math.roundToLong", ToExtensionFunctionWithNonNullableReceiver), - Replacement("java.lang.Math.round", "kotlin.math.roundToInt", ToExtensionFunctionWithNonNullableReceiver), + Replacement("java.lang.Math.round", "kotlin.math.roundToLong", ToExtensionFunctionWithNonNullableReceiver, mayChangeSemantics = true), + Replacement("java.lang.Math.round", "kotlin.math.roundToInt", ToExtensionFunctionWithNonNullableReceiver, mayChangeSemantics = 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"), @@ -160,7 +171,7 @@ class ReplaceJavaStaticMethodWithKotlinAnalogInspection : AbstractKotlinInspecti Replacement("java.util.Arrays.copyOf", "kotlin.collections.copyOf", ToExtensionFunctionWithNonNullableReceiver) { it.valueArguments.size == 2 }, - Replacement("java.util.Arrays.copyOfRange", "kotlin.collections.copyOfRange", ToExtensionFunctionWithNonNullableReceiver), + Replacement("java.util.Arrays.copyOfRange", "kotlin.collections.copyOfRange", ToExtensionFunctionWithNonNullableReceiver, mayChangeSemantics = true), Replacement("java.util.Arrays.equals", "kotlin.collections.contentEquals", ToExtensionFunctionWithNullableReceiver) { it.valueArguments.size == 2 }, @@ -185,7 +196,7 @@ class ReplaceJavaStaticMethodWithKotlinAnalogInspection : AbstractKotlinInspecti Replacement("java.util.List.of", "kotlin.collections.mutableListOf") ) - val REPLACEMENTS = (JAVA_MATH + JAVA_SYSTEM + JAVA_IO + JAVA_PRIMITIVES + JAVA_COLLECTIONS) + val REPLACEMENTS: Map> = (JAVA_MATH + JAVA_SYSTEM + JAVA_IO + JAVA_PRIMITIVES + JAVA_COLLECTIONS) .groupBy { it.javaMethodShortName } } } @@ -195,6 +206,7 @@ data class Replacement( val javaMethodFqName: String, val kotlinFunctionFqName: String, val transformation: Transformation = WithoutAdditionalTransformation, + val mayChangeSemantics: Boolean = false, val filter: (KtCallExpression) -> Boolean = { true } ) { private fun String.shortName() = takeLastWhile { it != '.' } diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt index 029c8da5a9ef..fc1c25fca759 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt @@ -1,3 +1,4 @@ +// FIX: Replace with 'copyOfRange' function (may change semantics) // WITH_STDLIB import java.util.Arrays diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt.after index 104810a249ad..9d4186b5f76f 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt.after +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/collections/copyOfRange.kt.after @@ -1,3 +1,4 @@ +// FIX: Replace with 'copyOfRange' function (may change semantics) // WITH_STDLIB import java.util.Arrays diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt index 440a089ce40c..f28fb3db0838 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt @@ -1,4 +1,4 @@ -// FIX: Replace with 'roundToInt' function +// FIX: Replace with 'roundToInt' function (may change semantics) // WITH_STDLIB fun test(x: Double) { Math.round(x) diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt.after index 32b931f1c781..48209d682eba 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt.after +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToInt.kt.after @@ -1,6 +1,6 @@ import kotlin.math.roundToInt -// FIX: Replace with 'roundToInt' function +// FIX: Replace with 'roundToInt' function (may change semantics) // WITH_STDLIB fun test(x: Double) { x.roundToInt() diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt index fb39dfd59445..54261bd28da8 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt @@ -1,4 +1,4 @@ -// FIX: Replace with 'roundToLong' function +// FIX: Replace with 'roundToLong' function (may change semantics) // WITH_STDLIB fun test(x: Double) { Math.round(x) diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt.after index df3c58d2da24..786f8094988b 100644 --- a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt.after +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceJavaStaticMethodWithKotlinAnalog/math/roundToLong.kt.after @@ -1,6 +1,6 @@ import kotlin.math.roundToLong -// FIX: Replace with 'roundToLong' function +// FIX: Replace with 'roundToLong' function (may change semantics) // WITH_STDLIB fun test(x: Double) { x.roundToLong()