KT-11425: convert a.compareTo(b) to binary comparison if possible
(cherry picked from commit 7219904)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
f309021c9f
commit
fc13b25ca4
+54
-13
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.intentions.conventionNameCalls
|
|||||||
import com.intellij.codeInsight.intention.HighPriorityAction
|
import com.intellij.codeInsight.intention.HighPriorityAction
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.tree.IElementType
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.idea.intentions.*
|
import org.jetbrains.kotlin.idea.intentions.*
|
||||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||||
@@ -36,6 +37,17 @@ class ReplaceCallWithBinaryOperatorIntention : SelfTargetingRangeIntention<KtDot
|
|||||||
KtDotQualifiedExpression::class.java,
|
KtDotQualifiedExpression::class.java,
|
||||||
"Replace call with binary operator"
|
"Replace call with binary operator"
|
||||||
), HighPriorityAction {
|
), HighPriorityAction {
|
||||||
|
|
||||||
|
private fun IElementType.inverted(): KtSingleValueToken? = when (this) {
|
||||||
|
KtTokens.LT -> KtTokens.GT
|
||||||
|
KtTokens.GT -> KtTokens.LT
|
||||||
|
|
||||||
|
KtTokens.GTEQ -> KtTokens.LTEQ
|
||||||
|
KtTokens.LTEQ -> KtTokens.GTEQ
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
override fun applicabilityRange(element: KtDotQualifiedExpression): TextRange? {
|
override fun applicabilityRange(element: KtDotQualifiedExpression): TextRange? {
|
||||||
val calleeExpression = element.callExpression?.calleeExpression as? KtSimpleNameExpression ?: return null
|
val calleeExpression = element.callExpression?.calleeExpression as? KtSimpleNameExpression ?: return null
|
||||||
val operation = operation(calleeExpression) ?: return null
|
val operation = operation(calleeExpression) ?: return null
|
||||||
@@ -58,15 +70,24 @@ class ReplaceCallWithBinaryOperatorIntention : SelfTargetingRangeIntention<KtDot
|
|||||||
val argument = callExpression.valueArguments.single().getArgumentExpression() ?: return
|
val argument = callExpression.valueArguments.single().getArgumentExpression() ?: return
|
||||||
val receiver = element.receiverExpression
|
val receiver = element.receiverExpression
|
||||||
|
|
||||||
if (operation == KtTokens.EXCLEQ) {
|
val factory = KtPsiFactory(element)
|
||||||
val prefixExpression = element.getWrappingPrefixExpressionIfAny() ?: return
|
when (operation) {
|
||||||
val newExpression = KtPsiFactory(element).createExpressionByPattern("$0 != $1", receiver, argument)
|
KtTokens.EXCLEQ -> {
|
||||||
prefixExpression.replace(newExpression)
|
val prefixExpression = element.getWrappingPrefixExpressionIfAny() ?: return
|
||||||
}
|
val newExpression = factory.createExpressionByPattern("$0 != $1", receiver, argument)
|
||||||
else {
|
prefixExpression.replace(newExpression)
|
||||||
val newExpression = KtPsiFactory(element).createExpressionByPattern("$0 ${operation.value} $1", receiver, argument)
|
}
|
||||||
element.replace(newExpression)
|
in OperatorConventions.COMPARISON_OPERATIONS -> {
|
||||||
|
val binaryParent = element.parent as? KtBinaryExpression ?: return
|
||||||
|
val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument)
|
||||||
|
binaryParent.replace(newExpression)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument)
|
||||||
|
element.replace(newExpression)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PsiElement.getWrappingPrefixExpressionIfAny() =
|
private fun PsiElement.getWrappingPrefixExpressionIfAny() =
|
||||||
@@ -74,11 +95,31 @@ class ReplaceCallWithBinaryOperatorIntention : SelfTargetingRangeIntention<KtDot
|
|||||||
|
|
||||||
private fun operation(calleeExpression: KtSimpleNameExpression): KtSingleValueToken? {
|
private fun operation(calleeExpression: KtSimpleNameExpression): KtSingleValueToken? {
|
||||||
val identifier = calleeExpression.getReferencedNameAsName()
|
val identifier = calleeExpression.getReferencedNameAsName()
|
||||||
if (identifier == OperatorNameConventions.EQUALS) {
|
return when (identifier) {
|
||||||
val prefixExpression = calleeExpression.parent?.parent?.getWrappingPrefixExpressionIfAny()
|
OperatorNameConventions.EQUALS -> {
|
||||||
return if (prefixExpression != null && prefixExpression.operationToken == KtTokens.EXCL) KtTokens.EXCLEQ
|
val prefixExpression = calleeExpression.parent?.parent?.getWrappingPrefixExpressionIfAny()
|
||||||
else KtTokens.EQEQ
|
if (prefixExpression != null && prefixExpression.operationToken == KtTokens.EXCL) KtTokens.EXCLEQ
|
||||||
|
else KtTokens.EQEQ
|
||||||
|
}
|
||||||
|
OperatorNameConventions.COMPARE_TO -> {
|
||||||
|
// callee -> call -> DotQualified -> Binary
|
||||||
|
val dotQualified = calleeExpression.parent?.parent
|
||||||
|
val binaryParent = dotQualified?.parent as? KtBinaryExpression ?: return null
|
||||||
|
val notZero = when {
|
||||||
|
binaryParent.right?.text == "0" -> binaryParent.left
|
||||||
|
binaryParent.left?.text == "0" -> binaryParent.right
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
if (notZero != dotQualified) return null
|
||||||
|
val token = binaryParent.operationToken as? KtSingleValueToken ?: return null
|
||||||
|
if (token in OperatorConventions.COMPARISON_OPERATIONS) {
|
||||||
|
if (notZero == binaryParent.left) token else token.inverted()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> OperatorConventions.BINARY_OPERATION_NAMES.inverse()[identifier]
|
||||||
}
|
}
|
||||||
return OperatorConventions.BINARY_OPERATION_NAMES.inverse()[identifier]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
val x = 2.compareTo<caret>(2) == 0
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// INTENTION_TEXT: Replace with '>' operator
|
||||||
|
|
||||||
|
val x = 3.compareTo<caret>(2) > 0
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// INTENTION_TEXT: Replace with '>' operator
|
||||||
|
|
||||||
|
val x = 3 > 2
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// INTENTION_TEXT: Replace with '<=' operator
|
||||||
|
|
||||||
|
val x = 0 >= 4.compareTo<caret>(5)
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// INTENTION_TEXT: Replace with '<=' operator
|
||||||
|
|
||||||
|
val x = 4 <= 5
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// ERROR: Infix call corresponds to a dot-qualified call 'nullable?.compareTo(1).compareTo(0)' which is not allowed on a nullable receiver 'nullable?.compareTo(1)'. Use '?.'-qualified call instead
|
||||||
|
|
||||||
|
val nullable: Int? = null
|
||||||
|
val x = nullable?.compareTo<caret>(1) >= 0
|
||||||
@@ -2744,6 +2744,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("equalsCompareTo.kt")
|
||||||
|
public void testEqualsCompareTo() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsCompareTo.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("extensionFunction.kt")
|
@TestMetadata("extensionFunction.kt")
|
||||||
public void testExtensionFunction() throws Exception {
|
public void testExtensionFunction() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/extensionFunction.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/extensionFunction.kt");
|
||||||
@@ -2756,6 +2762,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("greater.kt")
|
||||||
|
public void testGreater() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lessEquals.kt")
|
||||||
|
public void testLessEquals() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("minusSanityTest.kt")
|
@TestMetadata("minusSanityTest.kt")
|
||||||
public void testMinusSanityTest() throws Exception {
|
public void testMinusSanityTest() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/minusSanityTest.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/minusSanityTest.kt");
|
||||||
@@ -2816,6 +2834,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("safeCompareTo.kt")
|
||||||
|
public void testSafeCompareTo() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/safeCompareTo.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("super.kt")
|
@TestMetadata("super.kt")
|
||||||
public void testSuper() throws Exception {
|
public void testSuper() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user