Invert if condition intention: 'isEmpty' <-> 'isNotEmpty'

#KT-34593 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-11-11 14:13:38 +09:00
committed by Vladimir Dolzhenko
parent 79c15e49b6
commit d5e71ebef1
11 changed files with 126 additions and 15 deletions
@@ -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 {
@@ -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<KtIfExpression>(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)
+10
View File
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (s.isEmpty()) {
foo(1)
} else {
foo(2)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (s.isNotEmpty()) {
foo(2)
} else {
foo(1)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (s.isNotEmpty()) {
foo(1)
} else {
foo(2)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (s.isEmpty()) {
foo(2)
} else {
foo(1)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (!s.isEmpty()) {
foo(1)
} else {
foo(2)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (s.isEmpty()) {
foo(2)
} else {
foo(1)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (!s.isNotEmpty()) {
foo(1)
} else {
foo(2)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(i: Int) {}
fun test(s: String) {
<caret>if (s.isNotEmpty()) {
foo(2)
} else {
foo(1)
}
}
@@ -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");