KT-14032 related: if expression to elvis now handles also is and !is checks
This commit is contained in:
committed by
Mikhail Glukhikh
parent
8188bb1e54
commit
121f0ec810
+44
-22
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user