From e934eba6e48b3533d2dc6b5c4d53940af3cc39c8 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 6 May 2019 13:26:19 +0300 Subject: [PATCH] Simplifiable call inspection: add filter -> filterIsInstance replacement #KT-30501 Fixed --- .../collections/SimplifiableCallInspection.kt | 44 +++++++++++-------- .../filterIsInstanceSimple.kt | 4 ++ .../filterIsInstanceSimple.kt.after | 4 ++ .../simplifiableCall/filterIsNotInstance.kt | 5 +++ .../LocalInspectionTestGenerated.java | 10 +++++ 5 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsNotInstance.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 c094e9501f8..4bc6eacca4b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallInspection.kt @@ -26,13 +26,13 @@ class SimplifiableCallInspection : AbstractKotlinInspection() { val calleeExpression = callExpression.calleeExpression ?: return val (conversion, resolvedCall) = callExpression.findConversionAndResolvedCall() ?: return if (!conversion.callChecker(resolvedCall)) return - val conversionSuffix = conversion.analyzer(callExpression) ?: return + val replacement = conversion.analyzer(callExpression) ?: return holder.registerProblem( calleeExpression, - "${conversion.fqName.shortName()} call could be simplified to ${conversion.replacement}$conversionSuffix", + "${conversion.fqName.shortName()} call could be simplified to $replacement", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - SimplifyCallFix(conversion, conversionSuffix) + SimplifyCallFix(conversion, replacement) ) }) @@ -50,7 +50,6 @@ class SimplifiableCallInspection : AbstractKotlinInspection() { private data class Conversion( val callFqName: String, - val replacement: String, val analyzer: (KtCallExpression) -> String?, val callChecker: (ResolvedCall<*>) -> Boolean = { true } ) { @@ -79,25 +78,34 @@ class SimplifiableCallInspection : AbstractKotlinInspection() { this is KtConstantExpression && this.node.elementType == KtNodeTypes.NULL private val conversions = listOf( - Conversion("kotlin.collections.flatMap", "flatten", fun(callExpression: KtCallExpression): String? { + Conversion("kotlin.collections.flatMap", fun(callExpression: KtCallExpression): String? { val lambdaExpression = callExpression.singleLambdaExpression() ?: return null val reference = lambdaExpression.singleStatement() ?: return null val lambdaParameterName = lambdaExpression.singleLambdaParameterName() ?: return null if (!reference.isNameReferenceTo(lambdaParameterName)) return null - return "()" + return "flatten()" }), - Conversion("kotlin.collections.filter", "filterNotNull", analyzer = fun(callExpression: KtCallExpression): String? { + Conversion("kotlin.collections.filter", analyzer = fun(callExpression: KtCallExpression): String? { val lambdaExpression = callExpression.singleLambdaExpression() ?: return null - val statement = lambdaExpression.singleStatement() as? KtBinaryExpression ?: return null val lambdaParameterName = lambdaExpression.singleLambdaParameterName() ?: return null - if (statement.operationToken != KtTokens.EXCLEQ && statement.operationToken != KtTokens.EXCLEQEQEQ) return null - val left = statement.left ?: return null - val right = statement.right ?: return null - if (left.isNameReferenceTo(lambdaParameterName) && right.isNull()) { - return "()" - } else if (right.isNameReferenceTo(lambdaParameterName) && left.isNull()) { - return "()" + when (val statement = lambdaExpression.singleStatement() ?: return null) { + is KtBinaryExpression -> { + if (statement.operationToken != KtTokens.EXCLEQ && statement.operationToken != KtTokens.EXCLEQEQEQ) return null + val left = statement.left ?: return null + val right = statement.right ?: return null + if (left.isNameReferenceTo(lambdaParameterName) && right.isNull()) { + return "filterNotNull()" + } else if (right.isNameReferenceTo(lambdaParameterName) && left.isNull()) { + return "filterNotNull()" + } + } + is KtIsExpression -> { + if (statement.isNegated) return null + if (!statement.leftHandSide.isNameReferenceTo(lambdaParameterName)) return null + val rightTypeReference = statement.typeReference ?: return null + return "filterIsInstance<${rightTypeReference.text}>()" + } } return null }, callChecker = fun(resolvedCall: ResolvedCall<*>): Boolean { @@ -107,14 +115,14 @@ class SimplifiableCallInspection : AbstractKotlinInspection() { ) } - private class SimplifyCallFix(val conversion: Conversion, val conversionSuffix: String) : LocalQuickFix { - override fun getName() = "Convert '${conversion.fqName.shortName()}' call to '${conversion.replacement}$conversionSuffix'" + private class SimplifyCallFix(val conversion: Conversion, val replacement: String) : LocalQuickFix { + override fun getName() = "Convert '${conversion.fqName.shortName()}' call to '$replacement'" override fun getFamilyName() = name override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val callExpression = descriptor.psiElement.parent as? KtCallExpression ?: return - callExpression.replace(KtPsiFactory(callExpression).createExpression("${conversion.replacement}$conversionSuffix")) + callExpression.replace(KtPsiFactory(callExpression).createExpression(replacement)) } } } diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt new file mode 100644 index 00000000000..6864b8331a3 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test(list: List) { + list.filter { it is String } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt.after new file mode 100644 index 00000000000..9693c06067a --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test(list: List) { + list.filterIsInstance() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsNotInstance.kt b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsNotInstance.kt new file mode 100644 index 00000000000..40455e83dd7 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsNotInstance.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME +// PROBLEM: none +fun test(list: List) { + list.filter { it !is String } +} \ 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 3857d4cfe8f..7f30afbe67d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -1108,6 +1108,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/explicitLambdaParameter.kt"); } + @TestMetadata("filterIsInstanceSimple.kt") + public void testFilterIsInstanceSimple() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsInstanceSimple.kt"); + } + + @TestMetadata("filterIsNotInstance.kt") + public void testFilterIsNotInstance() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterIsNotInstance.kt"); + } + @TestMetadata("filterNotNullBrackets.kt") public void testFilterNotNullBrackets() throws Exception { runTest("idea/testData/inspectionsLocal/collections/simplifiableCall/filterNotNullBrackets.kt");