"If null to elvis" now converts to elvis also !is check right after initializer #KT-14032 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-10-10 15:53:23 +03:00
committed by Mikhail Glukhikh
parent 121f0ec810
commit 0903402282
15 changed files with 55 additions and 28 deletions
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports an if expression checking variable being null or not of a type right after initializing it that can be converted into an elvis operator in the initializer
</body>
</html>
@@ -1,5 +0,0 @@
<html>
<body>
This inspection reports an if expression checking variable being null right after initializing it that can be converted into an elvis operator in the initializer
</body>
</html>
@@ -0,0 +1,5 @@
<html>
<body>
Converts an if expression checking variable being null or not of a type right after initializing it to an elvis operator in the initializer (if applicable)
</body>
</html>
@@ -1,5 +0,0 @@
<html>
<body>
Converts an if expression checking variable being null right after initializing it to an elvis operator in the initializer (if applicable)
</body>
</html>
+2 -2
View File
@@ -731,7 +731,7 @@
<usageToPsiElementProvider implementation="org.jetbrains.kotlin.idea.codeInsight.KotlinUsageToPsiElementProvider"/> <usageToPsiElementProvider implementation="org.jetbrains.kotlin.idea.codeInsight.KotlinUsageToPsiElementProvider"/>
<intentionAction> <intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention</className> <className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
<category>Kotlin</category> <category>Kotlin</category>
</intentionAction> </intentionAction>
@@ -1451,7 +1451,7 @@
language="kotlin" language="kotlin"
/> />
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.IfNullToElvisInspection" <localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisInspection"
displayName="If-Null return/break/... foldable to '?:'" displayName="If-Null return/break/... foldable to '?:'"
groupName="Kotlin" groupName="Kotlin"
enabledByDefault="true" enabledByDefault="true"
@@ -35,9 +35,9 @@ import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
class IfNullToElvisInspection : IntentionBasedInspection<KtIfExpression>(IfNullToElvisIntention::class) class FoldInitializerAndIfToElvisInspection : IntentionBasedInspection<KtIfExpression>(FoldInitializerAndIfToElvisIntention::class)
class IfNullToElvisIntention : SelfTargetingRangeIntention<KtIfExpression>(KtIfExpression::class.java, "Replace 'if' with elvis operator"){ class FoldInitializerAndIfToElvisIntention : SelfTargetingRangeIntention<KtIfExpression>(KtIfExpression::class.java, "Replace 'if' with elvis operator"){
override fun applicabilityRange(element: KtIfExpression): TextRange? { override fun applicabilityRange(element: KtIfExpression): TextRange? {
val data = calcData(element) ?: return null val data = calcData(element) ?: return null
@@ -49,7 +49,7 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention<KtIfExpression>(KtIfE
} }
override fun applyTo(element: KtIfExpression, editor: Editor?) { override fun applyTo(element: KtIfExpression, editor: Editor?) {
val (initializer, declaration, ifNullExpr) = calcData(element)!! val (initializer, declaration, ifNullExpr, typeReference) = calcData(element)!!
val factory = KtPsiFactory(element) val factory = KtPsiFactory(element)
val explicitTypeToSet = when { val explicitTypeToSet = when {
@@ -67,6 +67,9 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention<KtIfExpression>(KtIfE
val childRangeAfter = childRangeBefore.withoutLastStatement() val childRangeAfter = childRangeBefore.withoutLastStatement()
val elvis = factory.createExpressionByPattern("$0 ?: $1", initializer, ifNullExpr) as KtBinaryExpression val elvis = factory.createExpressionByPattern("$0 ?: $1", initializer, ifNullExpr) as KtBinaryExpression
if (typeReference != null) {
elvis.left!!.replace(factory.createExpressionByPattern("$0 as? $1", initializer, typeReference))
}
val newElvis = initializer.replaced(elvis) val newElvis = initializer.replaced(elvis)
element.delete() element.delete()
@@ -82,15 +85,25 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention<KtIfExpression>(KtIfE
private data class Data( private data class Data(
val initializer: KtExpression, val initializer: KtExpression,
val declaration: KtVariableDeclaration, val declaration: KtVariableDeclaration,
val ifNullExpression: KtExpression val ifNullExpression: KtExpression,
val typeChecked: KtTypeReference? = null
) )
private fun calcData(ifExpression: KtIfExpression): Data? { private fun calcData(ifExpression: KtIfExpression): Data? {
if (ifExpression.`else` != null) return null if (ifExpression.`else` != null) return null
val binaryExpression = ifExpression.condition as? KtBinaryExpression ?: return null val operationExpression = ifExpression.condition as? KtOperationExpression ?: return null
if (binaryExpression.operationToken != KtTokens.EQEQ) return null val value = when (operationExpression) {
val value = binaryExpression.expressionComparedToNull() as? KtNameReferenceExpression ?: return null is KtBinaryExpression -> {
if (operationExpression.operationToken != KtTokens.EQEQ) return null
operationExpression.expressionComparedToNull()
}
is KtIsExpression -> {
if (!operationExpression.isNegated) return null
operationExpression.leftHandSide
}
else -> return null
} as? KtNameReferenceExpression ?: return null
if (ifExpression.parent !is KtBlockExpression) return null if (ifExpression.parent !is KtBlockExpression) return null
val prevStatement = ifExpression.siblings(forward = false, withItself = false) val prevStatement = ifExpression.siblings(forward = false, withItself = false)
@@ -99,13 +112,14 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention<KtIfExpression>(KtIfE
if (prevStatement.nameAsName != value.getReferencedNameAsName()) return null if (prevStatement.nameAsName != value.getReferencedNameAsName()) return null
val initializer = prevStatement.initializer ?: return null val initializer = prevStatement.initializer ?: return null
val then = ifExpression.then ?: return null val then = ifExpression.then ?: return null
val typeReference = (operationExpression as? KtIsExpression)?.typeReference
if (then is KtBlockExpression) { if (then is KtBlockExpression) {
val statement = then.statements.singleOrNull() ?: return null val statement = then.statements.singleOrNull() ?: return null
return Data(initializer, prevStatement, statement) return Data(initializer, prevStatement, statement, typeReference)
} }
else { else {
return Data(initializer, prevStatement, then) return Data(initializer, prevStatement, then, typeReference)
} }
} }
@@ -61,7 +61,7 @@ object J2KPostProcessingRegistrar {
registerIntentionBasedProcessing(ConvertToExpressionBodyIntention(convertEmptyToUnit = false)) { it is KtPropertyAccessor } registerIntentionBasedProcessing(ConvertToExpressionBodyIntention(convertEmptyToUnit = false)) { it is KtPropertyAccessor }
registerIntentionBasedProcessing(IfThenToSafeAccessIntention()) registerIntentionBasedProcessing(IfThenToSafeAccessIntention())
registerIntentionBasedProcessing(IfThenToElvisIntention()) registerIntentionBasedProcessing(IfThenToElvisIntention())
registerIntentionBasedProcessing(IfNullToElvisIntention()) registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention())
registerIntentionBasedProcessing(SimplifyNegatedBinaryExpressionIntention()) registerIntentionBasedProcessing(SimplifyNegatedBinaryExpressionIntention())
registerIntentionBasedProcessing(ReplaceGetOrSetIntention(), additionalChecker = ReplaceGetOrSetInspection.additionalChecker) registerIntentionBasedProcessing(ReplaceGetOrSetIntention(), additionalChecker = ReplaceGetOrSetInspection.additionalChecker)
registerIntentionBasedProcessing(AddOperatorModifierIntention()) registerIntentionBasedProcessing(AddOperatorModifierIntention())
+1 -1
View File
@@ -1 +1 @@
org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention
+4
View File
@@ -0,0 +1,4 @@
fun foo(arg: Any?) {
val n = arg
if (<caret>n !is Int) return
}
+3
View File
@@ -0,0 +1,3 @@
fun foo(arg: Any?) {
val n = arg as? Int ?: return
}
@@ -1,4 +1,4 @@
// "Suppress 'IfNullToElvis' for fun foo" "true" // "Suppress 'FoldInitializerAndIfToElvis' for fun foo" "true"
fun foo(p: List<String?>, b: Boolean) { fun foo(p: List<String?>, b: Boolean) {
var v = p[0] var v = p[0]
@@ -6,4 +6,4 @@ fun foo(p: List<String?>, b: Boolean) {
if (b) v = null if (b) v = null
} }
// TOOL: org.jetbrains.kotlin.idea.intentions.IfNullToElvisInspection // TOOL: org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisInspection
@@ -1,10 +1,10 @@
// "Suppress 'IfNullToElvis' for fun foo" "true" // "Suppress 'FoldInitializerAndIfToElvis' for fun foo" "true"
@Suppress("IfNullToElvis") @Suppress("FoldInitializerAndIfToElvis")
fun foo(p: List<String?>, b: Boolean) { fun foo(p: List<String?>, b: Boolean) {
var v = p[0] var v = p[0]
<caret>if (v == null) return <caret>if (v == null) return
if (b) v = null if (b) v = null
} }
// TOOL: org.jetbrains.kotlin.idea.intentions.IfNullToElvisInspection // TOOL: org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisInspection
@@ -6463,6 +6463,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("NotIs.kt")
public void testNotIs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/ifNullToElvis/NotIs.kt");
doTest(fileName);
}
@TestMetadata("OtherVar1.kt") @TestMetadata("OtherVar1.kt")
public void testOtherVar1() throws Exception { public void testOtherVar1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/ifNullToElvis/OtherVar1.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/ifNullToElvis/OtherVar1.kt");