diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt index 678ada32980..cfb2795c6ce 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt @@ -7,7 +7,9 @@ package org.jetbrains.kotlin.idea.inspections.collections import com.intellij.codeInspection.ProblemHighlightType import com.intellij.openapi.util.TextRange +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.quickfix.ReplaceSelectorOfQualifiedExpressionFix import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtQualifiedExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -15,6 +17,8 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset 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.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.isFlexible import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf @@ -68,6 +72,11 @@ class UselessCallOnCollectionInspection : AbstractUselessCallInspection() { ) holder.registerProblem(descriptor) } else { + val fix = if (resolvedCall.resultingDescriptor.returnType.isList() && !receiverType.isList()) { + ReplaceSelectorOfQualifiedExpressionFix("toList()") + } else { + RemoveUselessCallFix() + } val descriptor = holder.manager.createProblemDescriptor( expression, TextRange( @@ -77,9 +86,11 @@ class UselessCallOnCollectionInspection : AbstractUselessCallInspection() { KotlinBundle.message("useless.call.on.collection.type"), ProblemHighlightType.LIKE_UNUSED_SYMBOL, isOnTheFly, - RemoveUselessCallFix() + fix ) holder.registerProblem(descriptor) } } + + private fun KotlinType?.isList() = this?.constructor?.declarationDescriptor?.fqNameSafe == KotlinBuiltIns.FQ_NAMES.list } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceSelectorOfQualifiedExpressionFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceSelectorOfQualifiedExpressionFix.kt new file mode 100644 index 00000000000..59ab8bc780a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceSelectorOfQualifiedExpressionFix.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtQualifiedExpression +import org.jetbrains.kotlin.psi.createExpressionByPattern + +class ReplaceSelectorOfQualifiedExpressionFix(private val newSelector: String) : LocalQuickFix { + override fun getName() = KotlinBundle.message("replace.with.0", newSelector) + + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + (descriptor.psiElement as? KtQualifiedExpression)?.let { + it.replace(KtPsiFactory(it).createExpressionByPattern("$0.$newSelector", it.receiverExpression)) + } + } +} diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceOnArray.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceOnArray.kt new file mode 100644 index 00000000000..db4f5cfa53e --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceOnArray.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x: List = arrayOf("1").filterIsInstance() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceOnArray.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceOnArray.kt.after new file mode 100644 index 00000000000..1165abe1a8a --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceOnArray.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x: List = arrayOf("1").toList() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnArray.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnArray.kt new file mode 100644 index 00000000000..f2d66c8b8e3 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnArray.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x: List = arrayOf("1").filterNotNull() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnArray.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnArray.kt.after new file mode 100644 index 00000000000..1165abe1a8a --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnArray.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x: List = arrayOf("1").toList() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnIterable.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnIterable.kt new file mode 100644 index 00000000000..057a40160b2 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnIterable.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(iterable: Iterable): List { + return iterable.filterNotNull() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnIterable.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnIterable.kt.after new file mode 100644 index 00000000000..f8e0dd3c755 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnIterable.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(iterable: Iterable): List { + return iterable.toList() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnSet.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnSet.kt new file mode 100644 index 00000000000..1452e3c7690 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnSet.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(): List { + return setOf(1).filterNotNull() +} diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnSet.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnSet.kt.after new file mode 100644 index 00000000000..8072b65aacf --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnSet.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(): List { + return setOf(1).toList() +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index bd627cb7f46..36ae57319dd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -1935,6 +1935,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceFake.kt"); } + @TestMetadata("FilterIsExactInstanceOnArray.kt") + public void testFilterIsExactInstanceOnArray() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceOnArray.kt"); + } + @TestMetadata("FilterIsForFlexible.kt") public void testFilterIsForFlexible() throws Exception { runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsForFlexible.kt"); @@ -1960,6 +1965,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/filterNotNullFake.kt"); } + @TestMetadata("FilterNotNullOnArray.kt") + public void testFilterNotNullOnArray() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnArray.kt"); + } + + @TestMetadata("FilterNotNullOnIterable.kt") + public void testFilterNotNullOnIterable() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnIterable.kt"); + } + + @TestMetadata("FilterNotNullOnSet.kt") + public void testFilterNotNullOnSet() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNullOnSet.kt"); + } + @TestMetadata("MapIndexedNotNullTo.kt") public void testMapIndexedNotNullTo() throws Exception { runTest("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt");