From 0903402282ba0dcbb337a24a97d6e8ef6b4b2d88 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 10 Oct 2016 15:53:23 +0300 Subject: [PATCH] "If null to elvis" now converts to elvis also !is check right after initializer #KT-14032 Fixed --- .../FoldInitializerAndIfToElvis.html | 5 +++ .../inspectionDescriptions/IfNullToElvis.html | 5 --- .../after.kt.template | 0 .../before.kt.template | 0 .../description.html | 5 +++ .../IfNullToElvisIntention/description.html | 5 --- idea/src/META-INF/plugin.xml | 4 +-- ...> FoldInitializerAndIfToElvisIntention.kt} | 32 +++++++++++++------ .../kotlin/idea/j2k/J2kPostProcessings.kt | 2 +- .../intentions/ifNullToElvis/.intention | 2 +- .../intentions/ifNullToElvis/NotIs.kt | 4 +++ .../intentions/ifNullToElvis/NotIs.kt.after | 3 ++ .../suppress/inspections/ifNullToElvis.kt | 4 +-- .../inspections/ifNullToElvis.kt.after | 6 ++-- .../intentions/IntentionTestGenerated.java | 6 ++++ 15 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 idea/resources/inspectionDescriptions/FoldInitializerAndIfToElvis.html delete mode 100644 idea/resources/inspectionDescriptions/IfNullToElvis.html rename idea/resources/intentionDescriptions/{IfNullToElvisIntention => FoldInitializerAndIfToElvisIntention}/after.kt.template (100%) rename idea/resources/intentionDescriptions/{IfNullToElvisIntention => FoldInitializerAndIfToElvisIntention}/before.kt.template (100%) create mode 100644 idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/description.html delete mode 100644 idea/resources/intentionDescriptions/IfNullToElvisIntention/description.html rename idea/src/org/jetbrains/kotlin/idea/intentions/{IfNullToElvisIntention.kt => FoldInitializerAndIfToElvisIntention.kt} (75%) create mode 100644 idea/testData/intentions/ifNullToElvis/NotIs.kt create mode 100644 idea/testData/intentions/ifNullToElvis/NotIs.kt.after diff --git a/idea/resources/inspectionDescriptions/FoldInitializerAndIfToElvis.html b/idea/resources/inspectionDescriptions/FoldInitializerAndIfToElvis.html new file mode 100644 index 00000000000..5035e8fba27 --- /dev/null +++ b/idea/resources/inspectionDescriptions/FoldInitializerAndIfToElvis.html @@ -0,0 +1,5 @@ + + +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 + + diff --git a/idea/resources/inspectionDescriptions/IfNullToElvis.html b/idea/resources/inspectionDescriptions/IfNullToElvis.html deleted file mode 100644 index 630afdfe18e..00000000000 --- a/idea/resources/inspectionDescriptions/IfNullToElvis.html +++ /dev/null @@ -1,5 +0,0 @@ - - -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 - - diff --git a/idea/resources/intentionDescriptions/IfNullToElvisIntention/after.kt.template b/idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/after.kt.template similarity index 100% rename from idea/resources/intentionDescriptions/IfNullToElvisIntention/after.kt.template rename to idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/after.kt.template diff --git a/idea/resources/intentionDescriptions/IfNullToElvisIntention/before.kt.template b/idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/before.kt.template similarity index 100% rename from idea/resources/intentionDescriptions/IfNullToElvisIntention/before.kt.template rename to idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/before.kt.template diff --git a/idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/description.html b/idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/description.html new file mode 100644 index 00000000000..5937182819d --- /dev/null +++ b/idea/resources/intentionDescriptions/FoldInitializerAndIfToElvisIntention/description.html @@ -0,0 +1,5 @@ + + + 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) + + diff --git a/idea/resources/intentionDescriptions/IfNullToElvisIntention/description.html b/idea/resources/intentionDescriptions/IfNullToElvisIntention/description.html deleted file mode 100644 index d4cd9342ff0..00000000000 --- a/idea/resources/intentionDescriptions/IfNullToElvisIntention/description.html +++ /dev/null @@ -1,5 +0,0 @@ - - - Converts an if expression checking variable being null right after initializing it to an elvis operator in the initializer (if applicable) - - diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index fd99c671ab8..f1b77fa7ce9 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -731,7 +731,7 @@ - org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention + org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention Kotlin @@ -1451,7 +1451,7 @@ language="kotlin" /> - (IfNullToElvisIntention::class) +class FoldInitializerAndIfToElvisInspection : IntentionBasedInspection(FoldInitializerAndIfToElvisIntention::class) -class IfNullToElvisIntention : SelfTargetingRangeIntention(KtIfExpression::class.java, "Replace 'if' with elvis operator"){ +class FoldInitializerAndIfToElvisIntention : SelfTargetingRangeIntention(KtIfExpression::class.java, "Replace 'if' with elvis operator"){ override fun applicabilityRange(element: KtIfExpression): TextRange? { val data = calcData(element) ?: return null @@ -49,7 +49,7 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention(KtIfE } 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 explicitTypeToSet = when { @@ -67,6 +67,9 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention(KtIfE val childRangeAfter = childRangeBefore.withoutLastStatement() 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) element.delete() @@ -82,15 +85,25 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention(KtIfE private data class Data( val initializer: KtExpression, val declaration: KtVariableDeclaration, - val ifNullExpression: KtExpression + val ifNullExpression: KtExpression, + val typeChecked: KtTypeReference? = null ) private fun calcData(ifExpression: KtIfExpression): Data? { if (ifExpression.`else` != null) return null - val binaryExpression = ifExpression.condition as? KtBinaryExpression ?: return null - if (binaryExpression.operationToken != KtTokens.EQEQ) return null - val value = binaryExpression.expressionComparedToNull() as? KtNameReferenceExpression ?: return null + val operationExpression = ifExpression.condition as? KtOperationExpression ?: return null + val value = when (operationExpression) { + 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 val prevStatement = ifExpression.siblings(forward = false, withItself = false) @@ -99,13 +112,14 @@ class IfNullToElvisIntention : SelfTargetingRangeIntention(KtIfE if (prevStatement.nameAsName != value.getReferencedNameAsName()) return null val initializer = prevStatement.initializer ?: return null val then = ifExpression.then ?: return null + val typeReference = (operationExpression as? KtIsExpression)?.typeReference if (then is KtBlockExpression) { val statement = then.statements.singleOrNull() ?: return null - return Data(initializer, prevStatement, statement) + return Data(initializer, prevStatement, statement, typeReference) } else { - return Data(initializer, prevStatement, then) + return Data(initializer, prevStatement, then, typeReference) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt index b55560a5663..3338fa266ba 100644 --- a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt @@ -61,7 +61,7 @@ object J2KPostProcessingRegistrar { registerIntentionBasedProcessing(ConvertToExpressionBodyIntention(convertEmptyToUnit = false)) { it is KtPropertyAccessor } registerIntentionBasedProcessing(IfThenToSafeAccessIntention()) registerIntentionBasedProcessing(IfThenToElvisIntention()) - registerIntentionBasedProcessing(IfNullToElvisIntention()) + registerIntentionBasedProcessing(FoldInitializerAndIfToElvisIntention()) registerIntentionBasedProcessing(SimplifyNegatedBinaryExpressionIntention()) registerIntentionBasedProcessing(ReplaceGetOrSetIntention(), additionalChecker = ReplaceGetOrSetInspection.additionalChecker) registerIntentionBasedProcessing(AddOperatorModifierIntention()) diff --git a/idea/testData/intentions/ifNullToElvis/.intention b/idea/testData/intentions/ifNullToElvis/.intention index f7463a9e35f..d15cccdf88e 100644 --- a/idea/testData/intentions/ifNullToElvis/.intention +++ b/idea/testData/intentions/ifNullToElvis/.intention @@ -1 +1 @@ -org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention +org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention diff --git a/idea/testData/intentions/ifNullToElvis/NotIs.kt b/idea/testData/intentions/ifNullToElvis/NotIs.kt new file mode 100644 index 00000000000..f47f4882e18 --- /dev/null +++ b/idea/testData/intentions/ifNullToElvis/NotIs.kt @@ -0,0 +1,4 @@ +fun foo(arg: Any?) { + val n = arg + if (n !is Int) return +} \ No newline at end of file diff --git a/idea/testData/intentions/ifNullToElvis/NotIs.kt.after b/idea/testData/intentions/ifNullToElvis/NotIs.kt.after new file mode 100644 index 00000000000..4c479960b6c --- /dev/null +++ b/idea/testData/intentions/ifNullToElvis/NotIs.kt.after @@ -0,0 +1,3 @@ +fun foo(arg: Any?) { + val n = arg as? Int ?: return +} \ No newline at end of file diff --git a/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt b/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt index 695bf9e0800..5ced09b4b38 100644 --- a/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt +++ b/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt @@ -1,4 +1,4 @@ -// "Suppress 'IfNullToElvis' for fun foo" "true" +// "Suppress 'FoldInitializerAndIfToElvis' for fun foo" "true" fun foo(p: List, b: Boolean) { var v = p[0] @@ -6,4 +6,4 @@ fun foo(p: List, b: Boolean) { if (b) v = null } -// TOOL: org.jetbrains.kotlin.idea.intentions.IfNullToElvisInspection \ No newline at end of file +// TOOL: org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisInspection \ No newline at end of file diff --git a/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt.after b/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt.after index c3589d6d13c..8f623d98b2b 100644 --- a/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt.after +++ b/idea/testData/quickfix/suppress/inspections/ifNullToElvis.kt.after @@ -1,10 +1,10 @@ -// "Suppress 'IfNullToElvis' for fun foo" "true" +// "Suppress 'FoldInitializerAndIfToElvis' for fun foo" "true" -@Suppress("IfNullToElvis") +@Suppress("FoldInitializerAndIfToElvis") fun foo(p: List, b: Boolean) { var v = p[0] if (v == null) return if (b) v = null } -// TOOL: org.jetbrains.kotlin.idea.intentions.IfNullToElvisInspection \ No newline at end of file +// TOOL: org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisInspection \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 166083bad22..0b0775d7eec 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -6463,6 +6463,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { 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") public void testOtherVar1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/ifNullToElvis/OtherVar1.kt");