"Simplify boolean expression": handle Boolean? comparison correctly

So #KT-23377 Fixed
This commit is contained in:
Toshiaki Kameyama
2018-03-24 10:50:27 +03:00
committed by Mikhail Glukhikh
parent a495c2e5ea
commit f91819ace7
4 changed files with 28 additions and 7 deletions
@@ -35,14 +35,15 @@ import org.jetbrains.kotlin.types.isFlexible
class SimplifyBooleanWithConstantsInspection : IntentionBasedInspection<KtBinaryExpression>(SimplifyBooleanWithConstantsIntention::class) class SimplifyBooleanWithConstantsInspection : IntentionBasedInspection<KtBinaryExpression>(SimplifyBooleanWithConstantsIntention::class)
class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(KtBinaryExpression::class.java, "Simplify boolean expression") { class SimplifyBooleanWithConstantsIntention :
SelfTargetingOffsetIndependentIntention<KtBinaryExpression>(KtBinaryExpression::class.java, "Simplify boolean expression") {
override fun isApplicableTo(element: KtBinaryExpression): Boolean { override fun isApplicableTo(element: KtBinaryExpression): Boolean {
val topBinary = PsiTreeUtil.getTopmostParentOfType(element, KtBinaryExpression::class.java) ?: element val topBinary = PsiTreeUtil.getTopmostParentOfType(element, KtBinaryExpression::class.java) ?: element
return areThereExpressionsToBeSimplified(topBinary) return areThereExpressionsToBeSimplified(topBinary)
} }
private fun areThereExpressionsToBeSimplified(element: KtExpression?) : Boolean { private fun areThereExpressionsToBeSimplified(element: KtExpression?): Boolean {
if (element == null) return false if (element == null) return false
when (element) { when (element) {
is KtParenthesizedExpression -> return areThereExpressionsToBeSimplified(element.expression) is KtParenthesizedExpression -> return areThereExpressionsToBeSimplified(element.expression)
@@ -70,14 +71,14 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte
val fqName = callExpression.getCallableDescriptor()?.fqNameOrNull() val fqName = callExpression.getCallableDescriptor()?.fqNameOrNull()
val valueArguments = callExpression.valueArguments val valueArguments = callExpression.valueArguments
val isRedundant = fqName?.asString() == "kotlin.assert" && val isRedundant = fqName?.asString() == "kotlin.assert" &&
valueArguments.singleOrNull()?.getArgumentExpression().isTrueConstant() valueArguments.singleOrNull()?.getArgumentExpression().isTrueConstant()
if (isRedundant) callExpression.delete() if (isRedundant) callExpression.delete()
} }
private fun toSimplifiedExpression(expression: KtExpression): KtExpression { private fun toSimplifiedExpression(expression: KtExpression): KtExpression {
val psiFactory = KtPsiFactory(expression) val psiFactory = KtPsiFactory(expression)
when { when {
expression.canBeReducedToTrue() -> { expression.canBeReducedToTrue() -> {
return psiFactory.createExpression("true") return psiFactory.createExpression("true")
} }
@@ -93,8 +94,7 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte
return if (simplified is KtBinaryExpression) { return if (simplified is KtBinaryExpression) {
// wrap in new parentheses to keep the user's original format // wrap in new parentheses to keep the user's original format
psiFactory.createExpressionByPattern("($0)", simplified) psiFactory.createExpressionByPattern("($0)", simplified)
} } else {
else {
// if we now have a simpleName, constant, or parenthesized we don't need parentheses // if we now have a simpleName, constant, or parenthesized we don't need parentheses
simplified simplified
} }
@@ -102,6 +102,7 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte
} }
expression is KtBinaryExpression -> { expression is KtBinaryExpression -> {
if (!areThereExpressionsToBeSimplified(expression)) return expression.copied()
val left = expression.left val left = expression.left
val right = expression.right val right = expression.right
val op = expression.operationToken val op = expression.operationToken
@@ -129,7 +130,11 @@ class SimplifyBooleanWithConstantsIntention : SelfTargetingOffsetIndependentInte
return expression.copied() return expression.copied()
} }
private fun toSimplifiedBooleanBinaryExpressionWithConstantOperand(constantOperand: Boolean, otherOperand: KtExpression, operation: IElementType): KtExpression { private fun toSimplifiedBooleanBinaryExpressionWithConstantOperand(
constantOperand: Boolean,
otherOperand: KtExpression,
operation: IElementType
): KtExpression {
val factory = KtPsiFactory(otherOperand) val factory = KtPsiFactory(otherOperand)
when (operation) { when (operation) {
OROR -> { OROR -> {
@@ -0,0 +1,5 @@
data class Test(val notnull: Boolean, val nullable: Boolean?)
fun test(a: Test, b: Test?) {
<caret>a.notnull == true || a.nullable == true || b?.notnull == true || b?.nullable == true
}
@@ -0,0 +1,5 @@
data class Test(val notnull: Boolean, val nullable: Boolean?)
fun test(a: Test, b: Test?) {
a.notnull || a.nullable == true || b?.notnull == true || b?.nullable == true
}
@@ -15399,6 +15399,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("nullableComplex2.kt")
public void testNullableComplex2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/simplifyBooleanWithConstants/nullableComplex2.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");