KT-14032 related: if expression to elvis now handles also is and !is checks

This commit is contained in:
Mikhail Glukhikh
2016-10-07 21:38:33 +03:00
committed by Mikhail Glukhikh
parent 8188bb1e54
commit 121f0ec810
7 changed files with 92 additions and 22 deletions
@@ -27,26 +27,38 @@ import org.jetbrains.kotlin.psi.*
class IfThenToElvisInspection : IntentionBasedInspection<KtIfExpression>(IfThenToElvisIntention::class)
class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpression>(KtIfExpression::class.java, "Replace 'if' expression with elvis expression") {
class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpression>(
KtIfExpression::class.java,
"Replace 'if' expression with elvis expression"
) {
private fun KtExpression.clausesReplaceableByElvis(firstClause: KtExpression, secondClause: KtExpression) =
firstClause.isNotNullExpression() && secondClause.evaluatesTo(this) &&
!(firstClause is KtThrowExpression && firstClause.throwsNullPointerExceptionWithNoArguments())
override fun isApplicableTo(element: KtIfExpression): Boolean {
val condition = element.condition as? KtBinaryExpression ?: return false
val condition = element.condition as? KtOperationExpression ?: return false
val thenClause = element.then ?: return false
val elseClause = element.`else` ?: return false
val expression = condition.expressionComparedToNull() ?: return false
val expression = when (condition) {
is KtBinaryExpression -> condition.expressionComparedToNull() ?: return false
is KtIsExpression -> condition.leftHandSide
else -> return false
}
if (!expression.isStableVariable()) return false
return when (condition.operationToken) {
KtTokens.EQEQ ->
thenClause.isNotNullExpression() && elseClause.evaluatesTo(expression) &&
!(thenClause is KtThrowExpression && thenClause.throwsNullPointerExceptionWithNoArguments())
KtTokens.EXCLEQ ->
elseClause.isNotNullExpression() && thenClause.evaluatesTo(expression) &&
!(elseClause is KtThrowExpression && elseClause.throwsNullPointerExceptionWithNoArguments())
return when (condition) {
is KtBinaryExpression -> when (condition.operationToken) {
KtTokens.EQEQ -> expression.clausesReplaceableByElvis(thenClause, elseClause)
KtTokens.EXCLEQ -> expression.clausesReplaceableByElvis(elseClause, thenClause)
else -> false
}
is KtIsExpression -> when (condition.isNegated) {
true -> expression.clausesReplaceableByElvis(thenClause, elseClause)
false -> expression.clausesReplaceableByElvis(elseClause, thenClause)
}
else -> false
}
}
@@ -57,22 +69,32 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpre
}
override fun applyTo(element: KtIfExpression, editor: Editor?) {
val condition = element.condition as KtBinaryExpression
val condition = element.condition as KtOperationExpression
val thenClause = element.then!!
val elseClause = element.`else`!!
val thenExpression = thenClause.unwrapBlockOrParenthesis()
val elseExpression = elseClause.unwrapBlockOrParenthesis()
val (left, right) =
when(condition.operationToken) {
KtTokens.EQEQ -> Pair(elseExpression, thenExpression)
KtTokens.EXCLEQ -> Pair(thenExpression, elseExpression)
else -> throw IllegalArgumentException()
}
val (left, right) = when (condition) {
is KtBinaryExpression -> when (condition.operationToken) {
KtTokens.EQEQ -> Pair(elseExpression, thenExpression)
KtTokens.EXCLEQ -> Pair(thenExpression, elseExpression)
else -> throw IllegalArgumentException()
}
is KtIsExpression -> when (condition.isNegated) {
true -> Pair(elseExpression, thenExpression)
false -> Pair(thenExpression, elseExpression)
}
else -> throw IllegalArgumentException()
}
val newExpr = element.replaced(KtPsiFactory(element).createExpressionByPattern("$0 ?: $1", left, right))
val elvis = KtPsiUtil.deparenthesize(newExpr) as KtBinaryExpression
val factory = KtPsiFactory(element)
val newExpr = factory.createExpressionByPattern("$0 ?: $1", left, right) as KtBinaryExpression
if (condition is KtIsExpression) {
newExpr.left!!.replace(factory.createExpressionByPattern("$0 as? $1", left, condition.typeReference!!))
}
val elvis = KtPsiUtil.deparenthesize(element.replaced(newExpr)) as KtBinaryExpression
if (editor != null) {
elvis.inlineLeftSideIfApplicableWithPrompt(editor)
@@ -103,4 +103,20 @@
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
<description>Replace 'if' expression with elvis expression</description>
</problem>
<problem>
<file>notIsCheck.kt</file>
<line>4</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/notIsCheck.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
<description>Replace 'if' expression with elvis expression</description>
</problem>
<problem>
<file>isCheck.kt</file>
<line>4</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/isCheck.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
<description>Replace 'if' expression with elvis expression</description>
</problem>
</problems>
@@ -0,0 +1,5 @@
class My(val x: Int)
fun foo(arg: Any?): My {
return if (<caret>arg is My) arg else My(42)
}
@@ -0,0 +1,5 @@
class My(val x: Int)
fun foo(arg: Any?): My {
return arg as? My ?: My(42)
}
@@ -0,0 +1,5 @@
class My(val x: Int)
fun foo(arg: Any?): My {
return if (<caret>arg !is My) My(42) else arg
}
@@ -0,0 +1,5 @@
class My(val x: Int)
fun foo(arg: Any?): My {
return arg as? My ?: My(42)
}
@@ -1296,6 +1296,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("isCheck.kt")
public void testIsCheck() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/isCheck.kt");
doTest(fileName);
}
@TestMetadata("lhsEqualsNull.kt")
public void testLhsEqualsNull() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/lhsEqualsNull.kt");
@@ -1356,6 +1362,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("notIsCheck.kt")
public void testNotIsCheck() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/notIsCheck.kt");
doTest(fileName);
}
@TestMetadata("nullBranchAlsoNull.kt")
public void testNullBranchAlsoNull() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/nullBranchAlsoNull.kt");