diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index 3e4785b105c..8400195c5ae 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/IfNullToElvisIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/IfNullToElvisIntention.kt index afea5de6149..efb3774be09 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/IfNullToElvisIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/IfNullToElvisIntention.kt @@ -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() ?: 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 diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt index 772454d359a..1b9dc5a7642 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt @@ -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() diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt index 364130e85ab..ad001c164eb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToDoubleBangIntention.kt @@ -29,18 +29,13 @@ import org.jetbrains.kotlin.psi.JetThrowExpression public class IfThenToDoubleBangIntention : JetSelfTargetingOffsetIndependentIntention("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 diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToElvisIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToElvisIntention.kt index 8be3a09a0de..7e0db126b1d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToElvisIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToElvisIntention.kt @@ -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("if.then.to.elvis", javaClass()) { +public class IfThenToElvisIntention : JetSelfTargetingOffsetIndependentIntention(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 } } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToSafeAccessIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToSafeAccessIntention.kt index 59d7580896f..e90a81f4624 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToSafeAccessIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/IfThenToSafeAccessIntention.kt @@ -25,13 +25,12 @@ import org.jetbrains.kotlin.psi.* public class IfThenToSafeAccessIntention : JetSelfTargetingOffsetIndependentIntention("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()) {