From a8d08815a61ccf5d4a356c75c38a36331a37881a Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Tue, 16 Jul 2019 16:05:29 +0900 Subject: [PATCH] Simplify call: Fix false positive in "filter {}" detection (KT-32468) "Simplify filter {}" conversion changes semantics when the casted type is not a subtype if an initial collection element type. This commit limits a replacement suggestion to subtype cases. --- .../collections/SimplifiableCallInspection.kt | 18 ++++++++++++++++++ .../filterIsInstanceDisjointHierarchy.kt | 7 +++++++ .../filterIsInstanceSupertype.kt | 7 +++++++ .../LocalInspectionTestGenerated.java | 10 ++++++++++ 4 files changed, 42 insertions(+) create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceDisjointHierarchy.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSupertype.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallInspection.kt index 7432bfb6739..89d355ce615 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallInspection.kt @@ -11,13 +11,18 @@ import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.project.Project import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.FqName 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.model.ResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf class SimplifiableCallInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = @@ -103,6 +108,19 @@ class SimplifiableCallInspection : AbstractKotlinInspection() { if (statement.isNegated) return null if (!statement.leftHandSide.isNameReferenceTo(lambdaParameterName)) return null val rightTypeReference = statement.typeReference ?: return null + + val bindingContext = callExpression.analyze(BodyResolveMode.PARTIAL) + val rightType = bindingContext[BindingContext.TYPE, rightTypeReference] + val resolvedCall = callExpression.getResolvedCall(bindingContext) + + if (resolvedCall != null && rightType != null) { + val resultingElementType = resolvedCall.resultingDescriptor.returnType + ?.arguments?.singleOrNull()?.takeIf { !it.isStarProjection }?.type + if (resultingElementType != null && !rightType.isSubtypeOf(resultingElementType)) { + return null + } + } + return "filterIsInstance<${rightTypeReference.text}>()" } } diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceDisjointHierarchy.kt b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceDisjointHierarchy.kt new file mode 100644 index 00000000000..6f6a40c7db1 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceDisjointHierarchy.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// PROBLEM: none + +interface Foo +interface Bar + +fun instanceOfMarkerInterface(x: List): List = x.filter { it is Bar } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSupertype.kt b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSupertype.kt new file mode 100644 index 00000000000..b4b4c7be7db --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSupertype.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// PROBLEM: none + +interface Foo +interface Bar : Foo + +fun instanceOfMarkerInterface(x: List): List = x.filter { it is Foo } \ 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 07f6828831b..f6f5c45adcc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -1477,6 +1477,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/explicitLambdaParameter.kt"); } + @TestMetadata("filterIsInstanceDisjointHierarchy.kt") + public void testFilterIsInstanceDisjointHierarchy() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceDisjointHierarchy.kt"); + } + @TestMetadata("filterIsInstanceReceiver.kt") public void testFilterIsInstanceReceiver() throws Exception { runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceReceiver.kt"); @@ -1487,6 +1492,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt"); } + @TestMetadata("filterIsInstanceSupertype.kt") + public void testFilterIsInstanceSupertype() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSupertype.kt"); + } + @TestMetadata("filterIsNotInstance.kt") public void testFilterIsNotInstance() throws Exception { runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsNotInstance.kt");