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 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=Replace elvis expression with 'if' expression
elvis.to.if.then.family=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=Replace safe access expression with 'if' expression
safe.access.to.if.then.family=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 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 com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.JetNodeTypes import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.idea.caches.resolve.analyze 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.idea.util.isNothing
import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -84,14 +86,13 @@ public class IfNullToElvisIntention : JetSelfTargetingOffsetIndependentIntention
val binaryExpression = ifExpression.getCondition() as? JetBinaryExpression ?: return null val binaryExpression = ifExpression.getCondition() as? JetBinaryExpression ?: return null
if (binaryExpression.getOperationToken() != JetTokens.EQEQ) return null if (binaryExpression.getOperationToken() != JetTokens.EQEQ) return null
if (binaryExpression.getRight()?.getNode()?.getElementType() != JetNodeTypes.NULL) return null val value = binaryExpression.expressionComparedToNull() as? JetSimpleNameExpression ?: return null
val left = binaryExpression.getLeft() as? JetSimpleNameExpression ?: return null
if (ifExpression.getParent() !is JetBlockExpression) return null if (ifExpression.getParent() !is JetBlockExpression) return null
val prevStatement = ifExpression.siblings(forward = false, withItself = false) val prevStatement = ifExpression.siblings(forward = false, withItself = false)
.firstIsInstanceOrNull<JetExpression>() ?: return null .firstIsInstanceOrNull<JetExpression>() ?: return null
if (prevStatement !is JetVariableDeclaration) 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 initializer = prevStatement.getInitializer() ?: return null
val then = ifExpression.getThen() ?: return null val then = ifExpression.getThen() ?: return null
@@ -16,34 +16,37 @@
package org.jetbrains.kotlin.idea.intentions.branchedTransformations package org.jetbrains.kotlin.idea.intentions.branchedTransformations
import org.jetbrains.kotlin.lexer.JetTokens
import com.intellij.openapi.editor.Editor 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 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.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.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.psi.*
val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException" val NULL_PTR_EXCEPTION_FQ = "java.lang.NullPointerException"
val KOTLIN_NULL_PTR_EXCEPTION_FQ = "kotlin.KotlinNullPointerException" val KOTLIN_NULL_PTR_EXCEPTION_FQ = "kotlin.KotlinNullPointerException"
fun JetBinaryExpression.comparesNonNullToNull(): Boolean { fun JetBinaryExpression.expressionComparedToNull(): JetExpression? {
val operationToken = this.getOperationToken() val operationToken = this.getOperationToken()
val rhs = this.getRight() if (operationToken != JetTokens.EQEQ && operationToken != JetTokens.EXCLEQ) return null
val lhs = this.getLeft()
if (rhs == null || lhs == null) return false
val rightIsNull = rhs.isNullExpression() val right = this.getRight() ?: return null
val leftIsNull = lhs.isNullExpression() val left = this.getLeft() ?: return null
return leftIsNull != rightIsNull && (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EXCLEQ)
val rightIsNull = right.isNullExpression()
val leftIsNull = left.isNullExpression()
if (leftIsNull == rightIsNull) return null
return if (leftIsNull) right else left
} }
fun JetExpression.unwrapBlock(): JetExpression { fun JetExpression.unwrapBlock(): JetExpression {
@@ -57,18 +60,9 @@ fun JetExpression.unwrapBlock(): JetExpression {
fun JetExpression.isStatement(): Boolean = isUsedAsStatement(this.analyze()) fun JetExpression.isStatement(): Boolean = isUsedAsStatement(this.analyze())
fun JetBinaryExpression.getNonNullExpression(): JetExpression? = when { fun JetExpression?.isNullExpression(): Boolean = this?.unwrapBlock()?.getNode()?.getElementType() == JetNodeTypes.NULL
this.getLeft()?.isNullExpression() == false ->
this.getLeft()
this.getRight()?.isNullExpression() == false ->
this.getRight()
else ->
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 { fun JetThrowExpression.throwsNullPointerExceptionWithNoArguments(): Boolean {
val thrownExpression = this.getThrownExpression() val thrownExpression = this.getThrownExpression()
@@ -76,8 +70,7 @@ fun JetThrowExpression.throwsNullPointerExceptionWithNoArguments(): Boolean {
val context = this.analyze() val context = this.analyze()
val descriptor = context.get(BindingContext.REFERENCE_TARGET, thrownExpression.getCalleeExpression() as JetSimpleNameExpression) val descriptor = context.get(BindingContext.REFERENCE_TARGET, thrownExpression.getCalleeExpression() as JetSimpleNameExpression)
val declDescriptor = descriptor?.getContainingDeclaration() val declDescriptor = descriptor?.getContainingDeclaration() ?: return false
if (declDescriptor == null) return false
val exceptionName = DescriptorUtils.getFqName(declDescriptor).asString() val exceptionName = DescriptorUtils.getFqName(declDescriptor).asString()
return (exceptionName == NULL_PTR_EXCEPTION_FQ || exceptionName == KOTLIN_NULL_PTR_EXCEPTION_FQ) && thrownExpression.getValueArguments().isEmpty() 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()) { public class IfThenToDoubleBangIntention : JetSelfTargetingOffsetIndependentIntention<JetIfExpression>("if.then.to.double.bang", javaClass()) {
override fun isApplicableTo(element: JetIfExpression): Boolean { override fun isApplicableTo(element: JetIfExpression): Boolean {
val condition = element.getCondition() val condition = element.getCondition() as? JetBinaryExpression ?: return false
val thenClause = element.getThen() val thenClause = element.getThen()
val elseClause = element.getElse() val elseClause = element.getElse()
if (condition !is JetBinaryExpression || !condition.comparesNonNullToNull()) return false val expression = condition.expressionComparedToNull() ?: return false
val expression = condition.getNonNullExpression()
if (expression == null) return false
val token = condition.getOperationToken() val token = condition.getOperationToken()
if (token != JetTokens.EQEQ && token != JetTokens.EXCLEQ) return false
val throwExpression = val throwExpression =
when (token) { when (token) {
@@ -80,7 +75,7 @@ public class IfThenToDoubleBangIntention : JetSelfTargetingOffsetIndependentInte
override fun applyTo(element: JetIfExpression, editor: Editor) { override fun applyTo(element: JetIfExpression, editor: Editor) {
val condition = element.getCondition() as JetBinaryExpression 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 resultingExprString = expression.getText() + "!!"
val result = element.replace(resultingExprString) as JetPostfixExpression val result = element.replace(resultingExprString) as JetPostfixExpression
@@ -17,21 +17,22 @@
package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.* import org.jetbrains.kotlin.idea.intentions.branchedTransformations.*
import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.* 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 { override fun isApplicableTo(element: JetIfExpression): Boolean {
val condition = element.getCondition() val condition = element.getCondition() as? JetBinaryExpression ?: return false
val thenClause = element.getThen() val thenClause = element.getThen() ?: return false
val elseClause = element.getElse() val elseClause = element.getElse() ?: return false
if (thenClause == null || elseClause == null || condition !is JetBinaryExpression || !condition.comparesNonNullToNull()) return false
val expression = condition.getNonNullExpression() val expression = condition.expressionComparedToNull() ?: return false
if (expression == null || !expression.isStableVariable()) return false if (!expression.isStableVariable()) return false
return when (condition.getOperationToken()) { return when (condition.getOperationToken()) {
JetTokens.EQEQ -> JetTokens.EQEQ ->
@@ -49,7 +50,7 @@ public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention
private fun JetExpression.isNotNullExpression(): Boolean { private fun JetExpression.isNotNullExpression(): Boolean {
val innerExpression = this.unwrapBlock() 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) { override fun applyTo(element: JetIfExpression, editor: Editor) {
@@ -60,8 +61,8 @@ public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention
public fun applyTo(element: JetIfExpression): JetBinaryExpression { public fun applyTo(element: JetIfExpression): JetBinaryExpression {
val condition = element.getCondition() as JetBinaryExpression val condition = element.getCondition() as JetBinaryExpression
val thenClause = checkNotNull(element.getThen(), "The then clause cannot be null") val thenClause = element.getThen()!!
val elseClause = checkNotNull(element.getElse(), "The else clause cannot be null") val elseClause = element.getElse()!!
val thenExpression = thenClause.unwrapBlock() val thenExpression = thenClause.unwrapBlock()
val elseExpression = elseClause.unwrapBlock() val elseExpression = elseClause.unwrapBlock()
@@ -69,16 +70,10 @@ public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention
when(condition.getOperationToken()) { when(condition.getOperationToken()) {
JetTokens.EQEQ -> Pair(elseExpression, thenExpression) JetTokens.EQEQ -> Pair(elseExpression, thenExpression)
JetTokens.EXCLEQ -> Pair(thenExpression, elseExpression) 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 newExpr = element.replaced(JetPsiFactory(element).createExpressionByPattern("$0 ?: $1", left, right))
val resultingExpression = JetPsiUtil.deparenthesize(element.replace(resultingExprString) as? JetExpression) return JetPsiUtil.deparenthesize(newExpr) as JetBinaryExpression
assert(resultingExpression is JetBinaryExpression) {
"Unexpected expression type: ${resultingExpression?.javaClass}, expected JetBinaryExpression, element = '${element.getText()}'"
}
return resultingExpression as JetBinaryExpression
} }
} }
@@ -25,13 +25,12 @@ import org.jetbrains.kotlin.psi.*
public class IfThenToSafeAccessIntention : JetSelfTargetingOffsetIndependentIntention<JetIfExpression>("if.then.to.safe.access", javaClass()) { public class IfThenToSafeAccessIntention : JetSelfTargetingOffsetIndependentIntention<JetIfExpression>("if.then.to.safe.access", javaClass()) {
override fun isApplicableTo(element: JetIfExpression): Boolean { override fun isApplicableTo(element: JetIfExpression): Boolean {
val condition = element.getCondition() val condition = element.getCondition() as? JetBinaryExpression ?: return false
val thenClause = element.getThen() val thenClause = element.getThen()
val elseClause = element.getElse() val elseClause = element.getElse()
if (condition !is JetBinaryExpression || !condition.comparesNonNullToNull()) return false
val receiverExpression = condition.getNonNullExpression() val receiverExpression = condition.expressionComparedToNull() ?: return false
if (receiverExpression == null || !receiverExpression.isStableVariable()) return false if (!receiverExpression.isStableVariable()) return false
return when (condition.getOperationToken()) { return when (condition.getOperationToken()) {
JetTokens.EQEQ -> JetTokens.EQEQ ->
@@ -54,7 +53,7 @@ public class IfThenToSafeAccessIntention : JetSelfTargetingOffsetIndependentInte
public fun applyTo(element: JetIfExpression): JetSafeQualifiedExpression { public fun applyTo(element: JetIfExpression): JetSafeQualifiedExpression {
val condition = element.getCondition() as JetBinaryExpression val condition = element.getCondition() as JetBinaryExpression
val receiverExpression = checkNotNull(condition.getNonNullExpression(), "The receiver expression cannot be null") val receiverExpression = condition.expressionComparedToNull()!!
val selectorExpression = val selectorExpression =
when(condition.getOperationToken()) { when(condition.getOperationToken()) {