diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/ConvertCallChainIntoSequenceInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/ConvertCallChainIntoSequenceInspection.kt index a9b3a289bb8..dec965d68a5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/ConvertCallChainIntoSequenceInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/ConvertCallChainIntoSequenceInspection.kt @@ -154,7 +154,7 @@ private fun KtQualifiedExpression.findCallChain(): CallChain? { val receiverType = (lastCall.getQualifiedExpressionForSelector())?.receiverExpression?.getResolvedCall(context)?.resultingDescriptor?.returnType ?: lastCall.implicitReceiver(context)?.type - if (receiverType?.isCollection() != true) return null + if (receiverType?.isMap(DefaultBuiltIns.Instance) == true) return null val firstCall = calls.first() val qualified = firstCall.getQualifiedExpressionForSelector() ?: firstCall.getQualifiedExpressionForReceiver() ?: return null @@ -193,14 +193,6 @@ private fun KtExpression.implicitReceiver(context: BindingContext): ImplicitRece return getResolvedCall(context)?.getImplicitReceiverValue() } -private fun KotlinType.isCollection(): Boolean { - val classDescriptor = constructor.declarationDescriptor as? ClassDescriptor ?: return false - val className = classDescriptor.name.asString() - val builtIns = DefaultBuiltIns.Instance - return className.endsWith("List") && classDescriptor.isSubclassOf(builtIns.list) - || className.endsWith("Set") && classDescriptor.isSubclassOf(builtIns.set) -} - private fun KtCallExpression.hasReturn(): Boolean = valueArguments.any { arg -> arg.anyDescendantOfType { it.labelQualifier == null } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt index 885617cbd53..58bf0ac744a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt @@ -16,14 +16,22 @@ package org.jetbrains.kotlin.idea.inspections.collections +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.getFunctionalClassKind +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf import org.jetbrains.kotlin.types.KotlinType fun KotlinType.isFunctionOfAnyKind() = constructor.declarationDescriptor?.getFunctionalClassKind() != null +fun KotlinType?.isMap(builtIns: KotlinBuiltIns): Boolean { + val classDescriptor = this?.constructor?.declarationDescriptor as? ClassDescriptor ?: return false + return classDescriptor.name.asString().endsWith("Map") && classDescriptor.isSubclassOf(builtIns.map) +} + fun ResolvedCall<*>.hasLastFunctionalParameterWithResult(context: BindingContext, predicate: (KotlinType) -> Boolean): Boolean { val lastParameter = resultingDescriptor.valueParameters.lastOrNull() ?: return false val lastArgument = valueArguments[lastParameter]?.arguments?.singleOrNull() ?: return false diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt index 0a22f59ca87..b08a59168f3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt @@ -82,9 +82,4 @@ class SimplifiableCallChainInspection : AbstractCallChainChecker() { Conversion("kotlin.collections.listOf", "kotlin.collections.filterNotNull", "listOfNotNull") ) } -} - -private fun KotlinType?.isMap(builtIns: KotlinBuiltIns): Boolean { - val classDescriptor = this?.constructor?.declarationDescriptor as? ClassDescriptor ?: return false - return classDescriptor.name.asString().endsWith("Map") && classDescriptor.isSubclassOf(builtIns.map) -} +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable.kt b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable.kt new file mode 100644 index 00000000000..0752ce12220 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(i: Iterable): List { + return i.filter { it > 1 }.map { it * 2 } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable.kt.after b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable.kt.after new file mode 100644 index 00000000000..9eff1a9a50f --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(i: Iterable): List { + return i.asSequence().filter { it > 1 }.map { it * 2 }.toList() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable2.kt b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable2.kt new file mode 100644 index 00000000000..162dac93497 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable2.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME + +class A(private val list: List) : Iterable { + override fun iterator(): Iterator = list.iterator() +} + +fun test(i: A): List { + return i.filter { it > 1 }.map { it * 2 } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable2.kt.after b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable2.kt.after new file mode 100644 index 00000000000..16e8b3c85da --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable2.kt.after @@ -0,0 +1,9 @@ +// WITH_RUNTIME + +class A(private val list: List) : Iterable { + override fun iterator(): Iterator = list.iterator() +} + +fun test(i: A): List { + return i.asSequence().filter { it > 1 }.map { it * 2 }.toList() +} \ 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 59ec8cb366e..0c90a715dee 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -574,6 +574,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/implicitReceiver.kt"); } + @TestMetadata("iterable.kt") + public void testIterable() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable.kt"); + } + + @TestMetadata("iterable2.kt") + public void testIterable2() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/iterable2.kt"); + } + @TestMetadata("mutableList.kt") public void testMutableList() throws Exception { runTest("idea/testData/inspectionsLocal/collections/convertCallChainIntoSequence/mutableList.kt");