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 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 {
|
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 thenClause = element.then ?: return false
|
||||||
val elseClause = element.`else` ?: 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
|
if (!expression.isStableVariable()) return false
|
||||||
|
|
||||||
return when (condition.operationToken) {
|
return when (condition) {
|
||||||
KtTokens.EQEQ ->
|
is KtBinaryExpression -> when (condition.operationToken) {
|
||||||
thenClause.isNotNullExpression() && elseClause.evaluatesTo(expression) &&
|
KtTokens.EQEQ -> expression.clausesReplaceableByElvis(thenClause, elseClause)
|
||||||
!(thenClause is KtThrowExpression && thenClause.throwsNullPointerExceptionWithNoArguments())
|
KtTokens.EXCLEQ -> expression.clausesReplaceableByElvis(elseClause, thenClause)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
KtTokens.EXCLEQ ->
|
is KtIsExpression -> when (condition.isNegated) {
|
||||||
elseClause.isNotNullExpression() && thenClause.evaluatesTo(expression) &&
|
true -> expression.clausesReplaceableByElvis(thenClause, elseClause)
|
||||||
!(elseClause is KtThrowExpression && elseClause.throwsNullPointerExceptionWithNoArguments())
|
false -> expression.clausesReplaceableByElvis(elseClause, thenClause)
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,22 +69,32 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpre
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtIfExpression, editor: Editor?) {
|
override fun applyTo(element: KtIfExpression, editor: Editor?) {
|
||||||
val condition = element.condition as KtBinaryExpression
|
val condition = element.condition as KtOperationExpression
|
||||||
|
|
||||||
val thenClause = element.then!!
|
val thenClause = element.then!!
|
||||||
val elseClause = element.`else`!!
|
val elseClause = element.`else`!!
|
||||||
val thenExpression = thenClause.unwrapBlockOrParenthesis()
|
val thenExpression = thenClause.unwrapBlockOrParenthesis()
|
||||||
val elseExpression = elseClause.unwrapBlockOrParenthesis()
|
val elseExpression = elseClause.unwrapBlockOrParenthesis()
|
||||||
|
|
||||||
val (left, right) =
|
val (left, right) = when (condition) {
|
||||||
when(condition.operationToken) {
|
is KtBinaryExpression -> when (condition.operationToken) {
|
||||||
KtTokens.EQEQ -> Pair(elseExpression, thenExpression)
|
KtTokens.EQEQ -> Pair(elseExpression, thenExpression)
|
||||||
KtTokens.EXCLEQ -> Pair(thenExpression, elseExpression)
|
KtTokens.EXCLEQ -> Pair(thenExpression, elseExpression)
|
||||||
else -> throw IllegalArgumentException()
|
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 factory = KtPsiFactory(element)
|
||||||
val elvis = KtPsiUtil.deparenthesize(newExpr) as KtBinaryExpression
|
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) {
|
if (editor != null) {
|
||||||
elvis.inlineLeftSideIfApplicableWithPrompt(editor)
|
elvis.inlineLeftSideIfApplicableWithPrompt(editor)
|
||||||
|
|||||||
@@ -103,4 +103,20 @@
|
|||||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?:'</problem_class>
|
||||||
<description>Replace 'if' expression with elvis expression</description>
|
<description>Replace 'if' expression with elvis expression</description>
|
||||||
</problem>
|
</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>
|
</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);
|
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")
|
@TestMetadata("lhsEqualsNull.kt")
|
||||||
public void testLhsEqualsNull() throws Exception {
|
public void testLhsEqualsNull() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/lhsEqualsNull.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/lhsEqualsNull.kt");
|
||||||
@@ -1356,6 +1362,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("nullBranchAlsoNull.kt")
|
||||||
public void testNullBranchAlsoNull() throws Exception {
|
public void testNullBranchAlsoNull() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/nullBranchAlsoNull.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/nullBranchAlsoNull.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user