From d5e71ebef12eba694e6f45e4fdc8428514bf76ae Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Mon, 11 Nov 2019 14:13:38 +0900 Subject: [PATCH] Invert if condition intention: 'isEmpty' <-> 'isNotEmpty' #KT-34593 Fixed --- ...eNegatedIsEmptyWithIsNotEmptyInspection.kt | 37 ++++++++++++------- .../intentions/InvertIfConditionIntention.kt | 4 +- .../intentions/invertIfCondition/isEmpty.kt | 10 +++++ .../invertIfCondition/isEmpty.kt.after | 10 +++++ .../invertIfCondition/isNotEmpty.kt | 10 +++++ .../invertIfCondition/isNotEmpty.kt.after | 10 +++++ .../invertIfCondition/negatedIsEmpty.kt | 10 +++++ .../invertIfCondition/negatedIsEmpty.kt.after | 10 +++++ .../invertIfCondition/negatedIsNotEmpty.kt | 10 +++++ .../negatedIsNotEmpty.kt.after | 10 +++++ .../intentions/IntentionTestGenerated.java | 20 ++++++++++ 11 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 idea/testData/intentions/invertIfCondition/isEmpty.kt create mode 100644 idea/testData/intentions/invertIfCondition/isEmpty.kt.after create mode 100644 idea/testData/intentions/invertIfCondition/isNotEmpty.kt create mode 100644 idea/testData/intentions/invertIfCondition/isNotEmpty.kt.after create mode 100644 idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt create mode 100644 idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt.after create mode 100644 idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt create mode 100644 idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceNegatedIsEmptyWithIsNotEmptyInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceNegatedIsEmptyWithIsNotEmptyInspection.kt index 1e23974314f..dba55ac1dba 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceNegatedIsEmptyWithIsNotEmptyInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceNegatedIsEmptyWithIsNotEmptyInspection.kt @@ -24,20 +24,10 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType class ReplaceNegatedIsEmptyWithIsNotEmptyInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { return qualifiedExpressionVisitor(fun(expression) { - val callExpression = expression.callExpression ?: return - val calleeExpression = callExpression.calleeExpression ?: return - val calleeText = calleeExpression.text - val isEmptyCall = calleeText == "isEmpty" - val isNotEmptyCall = calleeText == "isNotEmpty" - if (!isEmptyCall && !isNotEmptyCall) return - - val prefixExpression = expression.getWrappingPrefixExpressionIfAny() ?: return - if (prefixExpression.operationToken != KtTokens.EXCL) return - - if (isEmptyCall && isEmptyFunctions.none { callExpression.isCalling(FqName(it)) } - || isNotEmptyCall && isNotEmptyFunctions.none { callExpression.isCalling(FqName(it)) }) return - - val (from, to) = if (isEmptyCall) "isEmpty" to "isNotEmpty" else "isNotEmpty" to "isEmpty" + if (expression.getWrappingPrefixExpressionIfAny()?.operationToken != KtTokens.EXCL) return + val calleeExpression = expression.callExpression?.calleeExpression ?: return + val from = calleeExpression.text + val to = expression.invertSelectorFunction()?.callExpression?.calleeExpression?.text ?: return holder.registerProblem( calleeExpression, "Replace negated '$from' with '$to'", @@ -46,6 +36,25 @@ class ReplaceNegatedIsEmptyWithIsNotEmptyInspection : AbstractKotlinInspection() ) }) } + + companion object { + fun KtQualifiedExpression.invertSelectorFunction(): KtQualifiedExpression? { + val callExpression = this.callExpression ?: return null + val calleeExpression = callExpression.calleeExpression ?: return null + val calleeText = calleeExpression.text + val isEmptyCall = calleeText == "isEmpty" + val isNotEmptyCall = calleeText == "isNotEmpty" + if (!isEmptyCall && !isNotEmptyCall) return null + if (isEmptyCall && isEmptyFunctions.none { callExpression.isCalling(FqName(it)) } + || isNotEmptyCall && isNotEmptyFunctions.none { callExpression.isCalling(FqName(it)) }) return null + val to = if (isEmptyCall) "isNotEmpty" else "isEmpty" + return KtPsiFactory(this).createExpressionByPattern( + "$0.$to()", + this.receiverExpression, + reformat = false + ) as? KtQualifiedExpression + } + } } class ReplaceNegatedIsEmptyWithIsNotEmptyQuickFix(private val from: String, private val to: String) : LocalQuickFix { diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/InvertIfConditionIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/InvertIfConditionIntention.kt index c612bfe5e27..5bcaf56c123 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/InvertIfConditionIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/InvertIfConditionIntention.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.core.moveCaret import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.core.unblockDocument +import org.jetbrains.kotlin.idea.inspections.ReplaceNegatedIsEmptyWithIsNotEmptyInspection.Companion.invertSelectorFunction import org.jetbrains.kotlin.idea.refactoring.getLineNumber import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches @@ -39,7 +40,8 @@ class InvertIfConditionIntention : SelfTargetingIntention(KtIfEx element.nextEolCommentOnSameLine()?.delete() } - val newCondition = element.condition!!.negate() + val condition = element.condition!! + val newCondition = (condition as? KtQualifiedExpression)?.invertSelectorFunction() ?: condition.negate() val newIf = handleSpecialCases(element, newCondition) ?: handleStandardCase(element, newCondition) diff --git a/idea/testData/intentions/invertIfCondition/isEmpty.kt b/idea/testData/intentions/invertIfCondition/isEmpty.kt new file mode 100644 index 00000000000..e2bbfef1010 --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/isEmpty.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (s.isEmpty()) { + foo(1) + } else { + foo(2) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/invertIfCondition/isEmpty.kt.after b/idea/testData/intentions/invertIfCondition/isEmpty.kt.after new file mode 100644 index 00000000000..393cba8a8cc --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/isEmpty.kt.after @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (s.isNotEmpty()) { + foo(2) + } else { + foo(1) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/invertIfCondition/isNotEmpty.kt b/idea/testData/intentions/invertIfCondition/isNotEmpty.kt new file mode 100644 index 00000000000..b4c7dbd799a --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/isNotEmpty.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (s.isNotEmpty()) { + foo(1) + } else { + foo(2) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/invertIfCondition/isNotEmpty.kt.after b/idea/testData/intentions/invertIfCondition/isNotEmpty.kt.after new file mode 100644 index 00000000000..5899880f353 --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/isNotEmpty.kt.after @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (s.isEmpty()) { + foo(2) + } else { + foo(1) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt b/idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt new file mode 100644 index 00000000000..c4c30908a99 --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (!s.isEmpty()) { + foo(1) + } else { + foo(2) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt.after b/idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt.after new file mode 100644 index 00000000000..5899880f353 --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt.after @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (s.isEmpty()) { + foo(2) + } else { + foo(1) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt b/idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt new file mode 100644 index 00000000000..ee4b4f6cdd7 --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (!s.isNotEmpty()) { + foo(1) + } else { + foo(2) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt.after b/idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt.after new file mode 100644 index 00000000000..393cba8a8cc --- /dev/null +++ b/idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt.after @@ -0,0 +1,10 @@ +// WITH_RUNTIME +fun foo(i: Int) {} + +fun test(s: String) { + if (s.isNotEmpty()) { + foo(2) + } else { + foo(1) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index db245be4303..c6adee11e8a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -9494,6 +9494,16 @@ public class IntentionTestGenerated extends AbstractIntentionTest { runTest("idea/testData/intentions/invertIfCondition/is.kt"); } + @TestMetadata("isEmpty.kt") + public void testIsEmpty() throws Exception { + runTest("idea/testData/intentions/invertIfCondition/isEmpty.kt"); + } + + @TestMetadata("isNotEmpty.kt") + public void testIsNotEmpty() throws Exception { + runTest("idea/testData/intentions/invertIfCondition/isNotEmpty.kt"); + } + @TestMetadata("lambdaNonLocalAndLocalReturn.kt") public void testLambdaNonLocalAndLocalReturn() throws Exception { runTest("idea/testData/intentions/invertIfCondition/lambdaNonLocalAndLocalReturn.kt"); @@ -9559,6 +9569,16 @@ public class IntentionTestGenerated extends AbstractIntentionTest { runTest("idea/testData/intentions/invertIfCondition/negatedExpression.kt"); } + @TestMetadata("negatedIsEmpty.kt") + public void testNegatedIsEmpty() throws Exception { + runTest("idea/testData/intentions/invertIfCondition/negatedIsEmpty.kt"); + } + + @TestMetadata("negatedIsNotEmpty.kt") + public void testNegatedIsNotEmpty() throws Exception { + runTest("idea/testData/intentions/invertIfCondition/negatedIsNotEmpty.kt"); + } + @TestMetadata("nestedIfWithReturn.kt") public void testNestedIfWithReturn() throws Exception { runTest("idea/testData/intentions/invertIfCondition/nestedIfWithReturn.kt");