diff --git a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/inspections/JavaCollectionsStaticMethodInspectionUtils.kt b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/inspections/JavaCollectionsStaticMethodInspectionUtils.kt index ee11bbbefd16..bdbb55bfd781 100644 --- a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/inspections/JavaCollectionsStaticMethodInspectionUtils.kt +++ b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/inspections/JavaCollectionsStaticMethodInspectionUtils.kt @@ -3,99 +3,84 @@ package org.jetbrains.kotlin.idea.codeinsights.impl.base.inspections import com.intellij.openapi.module.ModuleUtilCore import org.jetbrains.kotlin.analysis.api.KaSession -import org.jetbrains.kotlin.analysis.api.components.expandedSymbol import org.jetbrains.kotlin.analysis.api.components.expressionType +import org.jetbrains.kotlin.analysis.api.components.isSubtypeOf import org.jetbrains.kotlin.analysis.api.components.resolveToCall import org.jetbrains.kotlin.analysis.api.resolution.singleFunctionCallOrNull import org.jetbrains.kotlin.analysis.api.resolution.symbol import org.jetbrains.kotlin.analysis.api.types.KaType -import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings import org.jetbrains.kotlin.idea.codeinsight.utils.callExpression -import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.KtValueArgument -class JavaCollectionsStaticMethodInspectionUtils { +object JavaCollectionsStaticMethodInspectionUtils { - companion object Utils { - - context(_: KaSession) - fun getTargetMethod( - expression: KtDotQualifiedExpression, - isValidFirstArgument: (KaType?) -> Boolean - ): Pair? { - val callExpression = expression.callExpression ?: return null - val args = callExpression.valueArguments - val firstArg = args.firstOrNull() ?: return null - val firstArgType = firstArg.getArgumentExpression()?.expressionType - if (!isValidFirstArgument(firstArgType)) return null - - val call = callExpression.resolveToCall()?.singleFunctionCallOrNull() ?: return null - val callableId = call.partiallyAppliedSymbol.symbol.callableId ?: return null - val fqName = callableId.asSingleFqName().asString() - - if (!canReplaceWithStdLib(expression, fqName, args)) return null - - val methodName = fqName.split(".").last() - return methodName to firstArg + context(_: KaSession) + fun getMethodIfItsArgumentIsMutableList(expression: KtDotQualifiedExpression): Pair? = + getMethodIfCanReplaceItWithStdlib(expression) { methodArgument -> + isMutableListOrSubtype(methodArgument) } - private fun checkApiVersion(expression: KtDotQualifiedExpression): Boolean { - val module = ModuleUtilCore.findModuleForPsiElement(expression) ?: return true - return module.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_2 + context(_: KaSession) + fun getMethodIfItsArgumentIsImmutableList(expression: KtDotQualifiedExpression): Pair? = + getMethodIfCanReplaceItWithStdlib(expression) { methodArgument -> + isListOrSubtype(methodArgument) && !isMutableListOrSubtype( + methodArgument + ) } - private fun canReplaceWithStdLib(expression: KtDotQualifiedExpression, fqName: String, args: List): Boolean { - if (!fqName.startsWith("java.util.Collections.")) return false - val size = args.size - return when (fqName) { - "java.util.Collections.fill" -> checkApiVersion(expression) && size == 2 - "java.util.Collections.reverse" -> size == 1 - "java.util.Collections.shuffle" -> checkApiVersion(expression) && (size == 1 || size == 2) - "java.util.Collections.sort" -> { - size == 1 || (size == 2 && args.getOrNull(1)?.getArgumentExpression() is KtLambdaExpression) - } + context(_: KaSession) + private fun getMethodIfCanReplaceItWithStdlib( + expression: KtDotQualifiedExpression, + isValidFirstArgument: (KaType?) -> Boolean + ): Pair? { + val callExpression = expression.callExpression ?: return null + val args = callExpression.valueArguments + val firstArg = args.firstOrNull() ?: return null + val firstArgType = firstArg.getArgumentExpression()?.expressionType + if (!isValidFirstArgument(firstArgType)) return null - else -> false + val call = callExpression.resolveToCall()?.singleFunctionCallOrNull() ?: return null + val callableId = call.partiallyAppliedSymbol.symbol.callableId ?: return null + val fqName = callableId.asSingleFqName().asString() + + if (!canReplaceWithStdLib(expression, fqName, args)) return null + + val methodName = fqName.split(".").last() + return methodName to firstArg + } + + private fun checkApiVersion(expression: KtDotQualifiedExpression): Boolean { + val module = ModuleUtilCore.findModuleForPsiElement(expression) ?: return true + return module.languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_1_2 + } + + private fun canReplaceWithStdLib(expression: KtDotQualifiedExpression, fqName: String, args: List): Boolean { + if (!fqName.startsWith("java.util.Collections.")) return false + val size = args.size + return when (fqName) { + "java.util.Collections.fill" -> checkApiVersion(expression) && size == 2 + "java.util.Collections.reverse" -> size == 1 + "java.util.Collections.shuffle" -> checkApiVersion(expression) && (size == 1 || size == 2) + "java.util.Collections.sort" -> { + size == 1 || (size == 2 && args.getOrNull(1)?.getArgumentExpression() is KtLambdaExpression) } - } - context(_: KaSession) - fun isMutableListOrSubtype(type: KaType?): Boolean { - return isListOrSubtype(type, isMutable = true) - } - - context(_: KaSession) - fun isListOrSubtype(type: KaType?): Boolean { - return isListOrSubtype(type, isMutable = false) - } - - context(_: KaSession) - private fun isListOrSubtype(type: KaType?, isMutable: Boolean): Boolean { - if (type == null) return false - val fqName = getSingleFqName(type) ?: return false - - val qualifiedName = if (isMutable) { - if (fqName == StandardNames.FqNames.mutableList) return true - StandardNames.FqNames.mutableList - } else { - if (fqName == StandardNames.FqNames.list) return true - StandardNames.FqNames.list - } - val classSymbol = type.expandedSymbol ?: return false - return classSymbol.superTypes.reversed().any { superType -> - val fqNameOfSuperType = getSingleFqName(superType) - fqNameOfSuperType == qualifiedName - } - } - - context(_: KaSession) - private fun getSingleFqName(type: KaType): FqName? { - val classSymbol = type.expandedSymbol ?: return null - return classSymbol.classId?.asSingleFqName() + else -> false } } + + context(_: KaSession) + private fun isMutableListOrSubtype(type: KaType?): Boolean { + return type?.isSubtypeOf(StandardClassIds.MutableList) == true + } + + context(_: KaSession) + private fun isListOrSubtype(type: KaType?): Boolean { + return type?.isSubtypeOf(StandardClassIds.List) == true + } } \ No newline at end of file diff --git a/plugins/kotlin/code-insight/inspections-shared/src/org/jetbrains/kotlin/idea/codeInsight/inspections/shared/JavaCollectionsStaticMethodOnImmutableListInspection.kt b/plugins/kotlin/code-insight/inspections-shared/src/org/jetbrains/kotlin/idea/codeInsight/inspections/shared/JavaCollectionsStaticMethodOnImmutableListInspection.kt index dce168bcaf8c..2ef8c572c84c 100644 --- a/plugins/kotlin/code-insight/inspections-shared/src/org/jetbrains/kotlin/idea/codeInsight/inspections/shared/JavaCollectionsStaticMethodOnImmutableListInspection.kt +++ b/plugins/kotlin/code-insight/inspections-shared/src/org/jetbrains/kotlin/idea/codeInsight/inspections/shared/JavaCollectionsStaticMethodOnImmutableListInspection.kt @@ -9,7 +9,7 @@ import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinApplicableInspectionBase import org.jetbrains.kotlin.idea.codeinsight.api.applicators.ApplicabilityRange import org.jetbrains.kotlin.idea.codeinsight.utils.callExpression -import org.jetbrains.kotlin.idea.codeinsights.impl.base.inspections.JavaCollectionsStaticMethodInspectionUtils.Utils +import org.jetbrains.kotlin.idea.codeinsights.impl.base.inspections.JavaCollectionsStaticMethodInspectionUtils.getMethodIfItsArgumentIsImmutableList import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtValueArgument import org.jetbrains.kotlin.psi.KtVisitor @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.psi.dotQualifiedExpressionVisitor internal class JavaCollectionsStaticMethodOnImmutableListInspection : KotlinApplicableInspectionBase() { - internal class Context( + class Context( val methodName: String, val firstArg: KtValueArgument ) @@ -43,16 +43,10 @@ internal class JavaCollectionsStaticMethodOnImmutableListInspection : ) override fun KaSession.prepareContext(element: KtDotQualifiedExpression): Context? { - val (methodName, firstArg) = getTargetMethodOnImmutableList(element) ?: return null + val (methodName, firstArg) = getMethodIfItsArgumentIsImmutableList(element) ?: return null return Context(methodName, firstArg) } - context(_: KaSession) - private fun getTargetMethodOnImmutableList(expression: KtDotQualifiedExpression): Pair? = - Utils.getTargetMethod(expression) { type -> - Utils.isListOrSubtype(type) && !Utils.isMutableListOrSubtype(type) - } - override fun getApplicableRanges(element: KtDotQualifiedExpression): List = ApplicabilityRange.single(element) { it.callExpression?.calleeExpression } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodInspection.kt index 6a2c95d02846..3f1f093bcfc8 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodInspection.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinAp import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix import org.jetbrains.kotlin.idea.codeinsight.api.applicators.ApplicabilityRange import org.jetbrains.kotlin.idea.codeinsight.utils.callExpression -import org.jetbrains.kotlin.idea.codeinsights.impl.base.inspections.JavaCollectionsStaticMethodInspectionUtils.Utils +import org.jetbrains.kotlin.idea.codeinsights.impl.base.inspections.JavaCollectionsStaticMethodInspectionUtils.getMethodIfItsArgumentIsMutableList import org.jetbrains.kotlin.psi.* // In K2, it's ReplaceJavaStaticMethodWithKotlinAnalogInspection @@ -52,17 +52,13 @@ internal class JavaCollectionsStaticMethodInspection : } override fun KaSession.prepareContext(element: KtDotQualifiedExpression): Context? { - val (methodName, firstArg) = getTargetMethodOnMutableList(element) ?: return null + val (methodName, firstArg) = getMethodIfItsArgumentIsMutableList(element) ?: return null return Context(methodName, firstArg) } override fun getApplicableRanges(element: KtDotQualifiedExpression): List = ApplicabilityRange.self(element) - context(_: KaSession) - private fun getTargetMethodOnMutableList(expression: KtDotQualifiedExpression): Pair? = - Utils.getTargetMethod(expression) { type -> Utils.isMutableListOrSubtype(type) } - private class ReplaceWithStdLibFix(private val methodName: String, private val receiver: String) : KotlinModCommandQuickFix() {