Call chain into sequence: fix false negative on Iterable

#KT-26650 Fixed
This commit is contained in:
Toshiaki Kameyama
2018-09-10 12:35:05 +03:00
committed by Mikhail Glukhikh
parent 2e5ee242c7
commit 9a725b99b2
8 changed files with 48 additions and 15 deletions
@@ -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<KtReturnExpression> { it.labelQualifier == null }
}
@@ -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
@@ -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)
}
}
@@ -0,0 +1,5 @@
// WITH_RUNTIME
fun test(i: Iterable<Int>): List<Int> {
return i.<caret>filter { it > 1 }.map { it * 2 }
}
@@ -0,0 +1,5 @@
// WITH_RUNTIME
fun test(i: Iterable<Int>): List<Int> {
return i.asSequence().filter { it > 1 }.map { it * 2 }.toList()
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
class A<T>(private val list: List<T>) : Iterable<T> {
override fun iterator(): Iterator<T> = list.iterator()
}
fun test(i: A<Int>): List<Int> {
return i.<caret>filter { it > 1 }.map { it * 2 }
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
class A<T>(private val list: List<T>) : Iterable<T> {
override fun iterator(): Iterator<T> = list.iterator()
}
fun test(i: A<Int>): List<Int> {
return i.asSequence().filter { it > 1 }.map { it * 2 }.toList()
}
@@ -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");