Simplify boolean with constants intention supports now == (!=) true (false) cases #KT-13777 Fixed
This commit is contained in:
+28
-11
@@ -19,13 +19,15 @@ package org.jetbrains.kotlin.idea.intentions
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.psi.tree.IElementType
|
import com.intellij.psi.tree.IElementType
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.copied
|
import org.jetbrains.kotlin.idea.core.copied
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens.*
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
|
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||||
|
|
||||||
class SimplifyBooleanWithConstantsInspection : IntentionBasedInspection<KtBinaryExpression>(SimplifyBooleanWithConstantsIntention::class)
|
class SimplifyBooleanWithConstantsInspection : IntentionBasedInspection<KtBinaryExpression>(SimplifyBooleanWithConstantsIntention::class)
|
||||||
|
|
||||||
@@ -43,12 +45,13 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte
|
|||||||
|
|
||||||
is KtBinaryExpression -> {
|
is KtBinaryExpression -> {
|
||||||
val op = element.operationToken
|
val op = element.operationToken
|
||||||
if ((op == KtTokens.ANDAND || op == KtTokens.OROR) &&
|
if (op == ANDAND || op == OROR || op == EQEQ || op == EXCLEQ) {
|
||||||
(areThereExpressionsToBeSimplified(element.left) ||
|
if (areThereExpressionsToBeSimplified(element.left) && element.right.hasBooleanType()) return true
|
||||||
areThereExpressionsToBeSimplified(element.right))) return true
|
if (areThereExpressionsToBeSimplified(element.right) && element.left.hasBooleanType()) return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return element.canBeReducedToBooleanConstant(null)
|
return element.canBeReducedToBooleanConstant()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtBinaryExpression, editor: Editor?) {
|
override fun applyTo(element: KtBinaryExpression, editor: Editor?) {
|
||||||
@@ -88,7 +91,7 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte
|
|||||||
val left = expression.left
|
val left = expression.left
|
||||||
val right = expression.right
|
val right = expression.right
|
||||||
val op = expression.operationToken
|
val op = expression.operationToken
|
||||||
if (left != null && right != null && (op == KtTokens.ANDAND || op == KtTokens.OROR)) {
|
if (left != null && right != null && (op == ANDAND || op == OROR || op == EQEQ || op == EXCLEQ)) {
|
||||||
val simpleLeft = simplifyExpression(left)
|
val simpleLeft = simplifyExpression(left)
|
||||||
val simpleRight = simplifyExpression(right)
|
val simpleRight = simplifyExpression(right)
|
||||||
return when {
|
return when {
|
||||||
@@ -113,16 +116,30 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun toSimplifiedBooleanBinaryExpressionWithConstantOperand(constantOperand: Boolean, otherOperand: KtExpression, operation: IElementType): KtExpression {
|
private fun toSimplifiedBooleanBinaryExpressionWithConstantOperand(constantOperand: Boolean, otherOperand: KtExpression, operation: IElementType): KtExpression {
|
||||||
return when {
|
val factory = KtPsiFactory(otherOperand)
|
||||||
constantOperand && operation == KtTokens.OROR -> KtPsiFactory(otherOperand).createExpression("true")
|
when (operation) {
|
||||||
!constantOperand && operation == KtTokens.ANDAND -> KtPsiFactory(otherOperand).createExpression("false")
|
OROR -> {
|
||||||
else -> toSimplifiedExpression(otherOperand)
|
if (constantOperand) return factory.createExpression("true")
|
||||||
|
}
|
||||||
|
ANDAND -> {
|
||||||
|
if (!constantOperand) return factory.createExpression("false")
|
||||||
|
}
|
||||||
|
EQEQ, EXCLEQ -> toSimplifiedExpression(otherOperand).let {
|
||||||
|
return if (constantOperand == (operation == EQEQ)) it
|
||||||
|
else factory.createExpressionByPattern("!$0", it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return toSimplifiedExpression(otherOperand)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun simplifyExpression(expression: KtExpression) = expression.replaced(toSimplifiedExpression(expression))
|
private fun simplifyExpression(expression: KtExpression) = expression.replaced(toSimplifiedExpression(expression))
|
||||||
|
|
||||||
private fun KtExpression.canBeReducedToBooleanConstant(constant: Boolean?): Boolean {
|
private fun KtExpression?.hasBooleanType(): Boolean {
|
||||||
|
val type = this?.getType(this.analyze()) ?: return false
|
||||||
|
return KotlinBuiltIns.isBoolean(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.canBeReducedToBooleanConstant(constant: Boolean? = null): Boolean {
|
||||||
return CompileTimeConstantUtils.canBeReducedToBooleanConstant(this, this.analyze(), constant)
|
return CompileTimeConstantUtils.canBeReducedToBooleanConstant(this, this.analyze(), constant)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
fun foo(x: Boolean): Boolean = <caret>true == (x && false)
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
fun foo(x: Boolean): Boolean = false
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun foo(x: Boolean, y: Boolean): Boolean = <caret>(x || y) == false
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
fun foo(x: Boolean, y: Boolean): Boolean = !(x || y)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (false == <caret>arg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (!arg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (<caret>arg == true) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (arg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun foo(arg: Boolean): Boolean = <caret>arg == true || arg == false
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
fun foo(arg: Boolean): Boolean = arg || !arg
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (<caret>arg != false) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (arg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (true != <caret>arg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun use(arg: Boolean) {
|
||||||
|
if (!arg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
fun foo(arg: Boolean?): Boolean = <caret>arg == true
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
fun foo(arg: Boolean?): Boolean = <caret>arg == true || arg == false
|
||||||
+5
-1
@@ -1,3 +1,7 @@
|
|||||||
|
fun bar(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun foo(y: Boolean) {
|
fun foo(y: Boolean) {
|
||||||
<caret>true && () && y
|
<caret>true && (bar()) && y
|
||||||
}
|
}
|
||||||
+5
-1
@@ -1,3 +1,7 @@
|
|||||||
|
fun bar(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fun foo(y: Boolean) {
|
fun foo(y: Boolean) {
|
||||||
() && y
|
bar() && y
|
||||||
}
|
}
|
||||||
@@ -12729,6 +12729,36 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("equalsConjunction.kt")
|
||||||
|
public void testEqualsConjunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/equalsConjunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("equalsDisjunction.kt")
|
||||||
|
public void testEqualsDisjunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/equalsDisjunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("equalsFalse.kt")
|
||||||
|
public void testEqualsFalse() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/equalsFalse.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("equalsTrue.kt")
|
||||||
|
public void testEqualsTrue() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/equalsTrue.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("equalsTrueOrFalse.kt")
|
||||||
|
public void testEqualsTrueOrFalse() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/equalsTrueOrFalse.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("inapplicableNoConstants.kt")
|
@TestMetadata("inapplicableNoConstants.kt")
|
||||||
public void testInapplicableNoConstants() throws Exception {
|
public void testInapplicableNoConstants() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/inapplicableNoConstants.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/inapplicableNoConstants.kt");
|
||||||
@@ -12759,6 +12789,30 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notEqualsFalse.kt")
|
||||||
|
public void testNotEqualsFalse() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/notEqualsFalse.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notEqualsTrue.kt")
|
||||||
|
public void testNotEqualsTrue() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/notEqualsTrue.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableBoolean.kt")
|
||||||
|
public void testNullableBoolean() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/nullableBoolean.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableComplex.kt")
|
||||||
|
public void testNullableComplex() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/nullableComplex.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("reduceableBinary.kt")
|
@TestMetadata("reduceableBinary.kt")
|
||||||
public void testReduceableBinary() throws Exception {
|
public void testReduceableBinary() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/reduceableBinary.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/reduceableBinary.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user