Code improvements in IfThenToElvisIntention

This commit is contained in:
Valentin Kipyatkov
2015-05-05 22:32:41 +03:00
parent 345f9217ae
commit f2cced4384
6 changed files with 48 additions and 67 deletions
@@ -246,8 +246,6 @@ if.then.to.double.bang.replace.exception=Replace 'if' expression with '!!' expre
if.then.to.double.bang.family=Replace 'if' Expression with '!!' Expression
elvis.to.if.then=Replace elvis expression with 'if' expression
elvis.to.if.then.family=Replace Elvis Expression with 'if' Expression
if.then.to.elvis=Replace 'if' expression with elvis expression
if.then.to.elvis.family=Replace 'if' Expression with Elvis Expression
safe.access.to.if.then=Replace safe access expression with 'if' expression
safe.access.to.if.then.family=Replace Safe Access Expression with 'if' Expression
if.then.to.safe.access=Replace 'if' expression with safe access expression
@@ -22,6 +22,8 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.expressionComparedToNull
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.idea.util.isNothing
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
@@ -84,14 +86,13 @@ public class IfNullToElvisIntention : JetSelfTargetingOffsetIndependentIntention
val binaryExpression = ifExpression.getCondition() as? JetBinaryExpression ?: return null
if (binaryExpression.getOperationToken() != JetTokens.EQEQ) return null
if (binaryExpression.getRight()?.getNode()?.getElementType() != JetNodeTypes.NULL) return null
val left = binaryExpression.getLeft() as? JetSimpleNameExpression ?: return null
val value = binaryExpression.expressionComparedToNull() as? JetSimpleNameExpression ?: return null
if (ifExpression.getParent() !is JetBlockExpression) return null
val prevStatement = ifExpression.siblings(forward = false, withItself = false)
.firstIsInstanceOrNull<JetExpression>() ?: return null
if (prevStatement !is JetVariableDeclaration) return null
if (prevStatement.getNameAsName() != left.getReferencedNameAsName()) return null
if (prevStatement.getNameAsName() != value.getReferencedNameAsName()) return null
val initializer = prevStatement.getInitializer() ?: return null
val then = ifExpression.getThen() ?: return null
@@ -16,34 +16,37 @@
package org.jetbrains.kotlin.idea.intentions.branchedTransformations
import org.jetbrains.kotlin.lexer.JetTokens
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
import org.jetbrains.kotlin.resolve.BindingContext
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException"
val KOTLIN_NULL_PTR_EXCEPTION_FQ = "kotlin.KotlinNullPointerException"
fun JetBinaryExpression.comparesNonNullToNull(): Boolean {
fun JetBinaryExpression.expressionComparedToNull(): JetExpression? {
val operationToken = this.getOperationToken()
val rhs = this.getRight()
val lhs = this.getLeft()
if (rhs == null || lhs == null) return false
if (operationToken != JetTokens.EQEQ && operationToken != JetTokens.EXCLEQ) return null
val rightIsNull = rhs.isNullExpression()
val leftIsNull = lhs.isNullExpression()
return leftIsNull != rightIsNull && (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EXCLEQ)
val right = this.getRight() ?: return null
val left = this.getLeft() ?: return null
val rightIsNull = right.isNullExpression()
val leftIsNull = left.isNullExpression()
if (leftIsNull == rightIsNull) return null
return if (leftIsNull) right else left
}
fun JetExpression.unwrapBlock(): JetExpression {
@@ -57,18 +60,9 @@ fun JetExpression.unwrapBlock(): JetExpression {
fun JetExpression.isStatement(): Boolean = isUsedAsStatement(this.analyze())
fun JetBinaryExpression.getNonNullExpression(): JetExpression? = when {
this.getLeft()?.isNullExpression() == false ->
this.getLeft()
this.getRight()?.isNullExpression() == false ->
this.getRight()
else ->
null
}
fun JetExpression?.isNullExpression(): Boolean = this?.unwrapBlock()?.getNode()?.getElementType() == JetNodeTypes.NULL
fun JetExpression.isNullExpression(): Boolean = this.unwrapBlock().getText() == "null"
fun JetExpression.isNullExpressionOrEmptyBlock(): Boolean = this.isNullExpression() || this is JetBlockExpression && this.getStatements().isEmpty()
fun JetExpression?.isNullExpressionOrEmptyBlock(): Boolean = this.isNullExpression() || this is JetBlockExpression && this.getStatements().isEmpty()
fun JetThrowExpression.throwsNullPointerExceptionWithNoArguments(): Boolean {
val thrownExpression = this.getThrownExpression()
@@ -76,8 +70,7 @@ fun JetThrowExpression.throwsNullPointerExceptionWithNoArguments(): Boolean {
val context = this.analyze()
val descriptor = context.get(BindingContext.REFERENCE_TARGET, thrownExpression.getCalleeExpression() as JetSimpleNameExpression)
val declDescriptor = descriptor?.getContainingDeclaration()
if (declDescriptor == null) return false
val declDescriptor = descriptor?.getContainingDeclaration() ?: return false
val exceptionName = DescriptorUtils.getFqName(declDescriptor).asString()
return (exceptionName == NULL_PTR_EXCEPTION_FQ || exceptionName == KOTLIN_NULL_PTR_EXCEPTION_FQ) && thrownExpression.getValueArguments().isEmpty()
@@ -29,18 +29,13 @@ import org.jetbrains.kotlin.psi.JetThrowExpression
public class IfThenToDoubleBangIntention : JetSelfTargetingOffsetIndependentIntention<JetIfExpression>("if.then.to.double.bang", javaClass()) {
override fun isApplicableTo(element: JetIfExpression): Boolean {
val condition = element.getCondition()
val condition = element.getCondition() as? JetBinaryExpression ?: return false
val thenClause = element.getThen()
val elseClause = element.getElse()
if (condition !is JetBinaryExpression || !condition.comparesNonNullToNull()) return false
val expression = condition.getNonNullExpression()
if (expression == null) return false
val expression = condition.expressionComparedToNull() ?: return false
val token = condition.getOperationToken()
if (token != JetTokens.EQEQ && token != JetTokens.EXCLEQ) return false
val throwExpression =
when (token) {
@@ -80,7 +75,7 @@ public class IfThenToDoubleBangIntention : JetSelfTargetingOffsetIndependentInte
override fun applyTo(element: JetIfExpression, editor: Editor) {
val condition = element.getCondition() as JetBinaryExpression
val expression = checkNotNull(condition.getNonNullExpression(), "condition must contain non null expression")
val expression = condition.expressionComparedToNull()!!
val resultingExprString = expression.getText() + "!!"
val result = element.replace(resultingExprString) as JetPostfixExpression
@@ -17,21 +17,22 @@
package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.replaced
public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention<JetIfExpression>("if.then.to.elvis", javaClass()) {
public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention<JetIfExpression>(javaClass(), "Replace 'if' expression with elvis expression") {
override fun isApplicableTo(element: JetIfExpression): Boolean {
val condition = element.getCondition()
val thenClause = element.getThen()
val elseClause = element.getElse()
if (thenClause == null || elseClause == null || condition !is JetBinaryExpression || !condition.comparesNonNullToNull()) return false
val condition = element.getCondition() as? JetBinaryExpression ?: return false
val thenClause = element.getThen() ?: return false
val elseClause = element.getElse() ?: return false
val expression = condition.getNonNullExpression()
if (expression == null || !expression.isStableVariable()) return false
val expression = condition.expressionComparedToNull() ?: return false
if (!expression.isStableVariable()) return false
return when (condition.getOperationToken()) {
JetTokens.EQEQ ->
@@ -49,7 +50,7 @@ public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention
private fun JetExpression.isNotNullExpression(): Boolean {
val innerExpression = this.unwrapBlock()
return innerExpression !is JetBlockExpression && innerExpression.getText() != "null"
return innerExpression !is JetBlockExpression && innerExpression.getNode().getElementType() != JetNodeTypes.NULL
}
override fun applyTo(element: JetIfExpression, editor: Editor) {
@@ -60,8 +61,8 @@ public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention
public fun applyTo(element: JetIfExpression): JetBinaryExpression {
val condition = element.getCondition() as JetBinaryExpression
val thenClause = checkNotNull(element.getThen(), "The then clause cannot be null")
val elseClause = checkNotNull(element.getElse(), "The else clause cannot be null")
val thenClause = element.getThen()!!
val elseClause = element.getElse()!!
val thenExpression = thenClause.unwrapBlock()
val elseExpression = elseClause.unwrapBlock()
@@ -69,16 +70,10 @@ public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention
when(condition.getOperationToken()) {
JetTokens.EQEQ -> Pair(elseExpression, thenExpression)
JetTokens.EXCLEQ -> Pair(thenExpression, elseExpression)
else -> throw IllegalStateException("Operation token must be either null or not null")
else -> throw IllegalArgumentException()
}
val resultingExprString = "${left.getText()} ?: ${right.getText()}"
val resultingExpression = JetPsiUtil.deparenthesize(element.replace(resultingExprString) as? JetExpression)
assert(resultingExpression is JetBinaryExpression) {
"Unexpected expression type: ${resultingExpression?.javaClass}, expected JetBinaryExpression, element = '${element.getText()}'"
}
return resultingExpression as JetBinaryExpression
val newExpr = element.replaced(JetPsiFactory(element).createExpressionByPattern("$0 ?: $1", left, right))
return JetPsiUtil.deparenthesize(newExpr) as JetBinaryExpression
}
}
@@ -25,13 +25,12 @@ import org.jetbrains.kotlin.psi.*
public class IfThenToSafeAccessIntention : JetSelfTargetingOffsetIndependentIntention<JetIfExpression>("if.then.to.safe.access", javaClass()) {
override fun isApplicableTo(element: JetIfExpression): Boolean {
val condition = element.getCondition()
val condition = element.getCondition() as? JetBinaryExpression ?: return false
val thenClause = element.getThen()
val elseClause = element.getElse()
if (condition !is JetBinaryExpression || !condition.comparesNonNullToNull()) return false
val receiverExpression = condition.getNonNullExpression()
if (receiverExpression == null || !receiverExpression.isStableVariable()) return false
val receiverExpression = condition.expressionComparedToNull() ?: return false
if (!receiverExpression.isStableVariable()) return false
return when (condition.getOperationToken()) {
JetTokens.EQEQ ->
@@ -54,7 +53,7 @@ public class IfThenToSafeAccessIntention : JetSelfTargetingOffsetIndependentInte
public fun applyTo(element: JetIfExpression): JetSafeQualifiedExpression {
val condition = element.getCondition() as JetBinaryExpression
val receiverExpression = checkNotNull(condition.getNonNullExpression(), "The receiver expression cannot be null")
val receiverExpression = condition.expressionComparedToNull()!!
val selectorExpression =
when(condition.getOperationToken()) {