diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt index 7bee3712eb7..19f6f741e3a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/JavaCollectionsStaticMethodCallInspection.kt @@ -12,7 +12,6 @@ import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.project.Project import com.intellij.psi.PsiElementVisitor -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.imports.importableFqName @@ -20,10 +19,12 @@ import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getType -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf class JavaCollectionsStaticMethodInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { @@ -32,11 +33,9 @@ class JavaCollectionsStaticMethodInspection : AbstractKotlinInspection() { val args = callExpression.valueArguments val firstArg = args.firstOrNull() ?: return val context = expression.analyze(BodyResolveMode.PARTIAL) - if (KotlinBuiltIns.FQ_NAMES.mutableList != - firstArg.getArgumentExpression()?.getType(context)?.constructor?.declarationDescriptor?.fqNameSafe) return + if (!firstArg.isMutableList(context)) return - val resolvedCall = expression.getResolvedCall(context) ?: return - val descriptor = resolvedCall.resultingDescriptor as? JavaMethodDescriptor ?: return + val descriptor = expression.getResolvedCall(context)?.resultingDescriptor as? JavaMethodDescriptor ?: return val fqName = descriptor.importableFqName?.asString() ?: return if (!canReplaceWithStdLib(expression, fqName, args)) return @@ -69,6 +68,16 @@ class JavaCollectionsStaticMethodInspection : AbstractKotlinInspection() { } +private fun KtValueArgument.isMutableList(context: BindingContext): Boolean { + val type = getArgumentExpression()?.getType(context) ?: return false + val constructor = type.constructor + val mutableListType = type.builtIns.mutableList.defaultType + if (constructor.declarationDescriptor?.defaultType?.isSubtypeOf(mutableListType) == true) return true + return constructor.supertypes.reversed().any { + it.constructor.declarationDescriptor?.defaultType?.isSubtypeOf(mutableListType) == true + } +} + private class ReplaceWithStdLibFix(private val methodName: String, private val receiver: String) : LocalQuickFix { override fun getName() = "Replace with $receiver.$methodName" diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortArrayList.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortArrayList.kt new file mode 100644 index 00000000000..de55d278f88 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortArrayList.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test () { + val list: ArrayList = ArrayList() + Collections.sort(list) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortArrayList.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortArrayList.kt.after new file mode 100644 index 00000000000..f880418b2c2 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortArrayList.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test () { + val list: ArrayList = ArrayList() + list.sort() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortLinkedList.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortLinkedList.kt new file mode 100644 index 00000000000..11a1d2e8132 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortLinkedList.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test () { + val list: LinkedList = LinkedList() + Collections.sort(list) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortLinkedList.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortLinkedList.kt.after new file mode 100644 index 00000000000..3a53ba3ac52 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortLinkedList.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test () { + val list: LinkedList = LinkedList() + list.sort() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortVector.kt b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortVector.kt new file mode 100644 index 00000000000..df5027fa115 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortVector.kt @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test () { + val list: Vector = Vector() + Collections.sort(list) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortVector.kt.after b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortVector.kt.after new file mode 100644 index 00000000000..55afd51e766 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortVector.kt.after @@ -0,0 +1,7 @@ +// RUNTIME_WITH_FULL_JDK +import java.util.* + +fun test () { + val list: Vector = Vector() + list.sort() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index e7b662c035a..ca5a8266652 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -1545,12 +1545,30 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { doTest(fileName); } + @TestMetadata("sortArrayList.kt") + public void testSortArrayList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortArrayList.kt"); + doTest(fileName); + } + @TestMetadata("sortImmutableList.kt") public void testSortImmutableList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortImmutableList.kt"); doTest(fileName); } + @TestMetadata("sortLinkedList.kt") + public void testSortLinkedList() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortLinkedList.kt"); + doTest(fileName); + } + + @TestMetadata("sortVector.kt") + public void testSortVector() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortVector.kt"); + doTest(fileName); + } + @TestMetadata("sortWith.kt") public void testSortWith() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod/sortWith.kt");