From 5df5a001a1f3f0def40e082288d43383e6a0ae72 Mon Sep 17 00:00:00 2001 From: Dmitry Neverov Date: Tue, 13 Jun 2017 22:48:14 +0300 Subject: [PATCH] Support implicit receiver in if-then intentions #KT-16069 Fixed --- .../intentions/ReplaceSubstringIntention.kt | 4 ++-- .../branchedTransformations/IfThenUtils.kt | 14 +++++++++++--- .../intentions/DoubleBangToIfThenIntention.kt | 4 ++-- .../intentions/ElvisToIfThenIntention.kt | 4 ++-- .../intentions/IfThenToDoubleBangIntention.kt | 2 +- .../intentions/IfThenToElvisIntention.kt | 6 ++++-- .../intentions/IfThenToSafeAccessIntention.kt | 5 +++-- .../intentions/MergeWhenIntention.kt | 4 ++-- .../intentions/SafeAccessToIfThenIntention.kt | 4 ++-- .../idea/quickfix/SurroundWithNullCheckFix.kt | 12 +----------- .../branched/ifThenToDoubleBang/this.kt | 2 ++ .../branched/ifThenToDoubleBang/this.kt.after | 2 ++ .../branched/ifThenToElvis/implicitReceiver.kt | 2 ++ .../ifThenToElvis/implicitReceiver.kt.after | 2 ++ .../ifThenToElvis/inspectionData/expected.xml | 8 ++++++++ .../ifThenToSafeAccess/implicitReceiver.kt | 2 ++ .../implicitReceiver.kt.after | 2 ++ .../inspectionData/expected.xml | 8 ++++++++ .../intentions/IntentionTestGenerated.java | 18 ++++++++++++++++++ 19 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 idea/testData/intentions/branched/ifThenToDoubleBang/this.kt create mode 100644 idea/testData/intentions/branched/ifThenToDoubleBang/this.kt.after create mode 100644 idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt create mode 100644 idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt.after create mode 100644 idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt create mode 100644 idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceSubstringIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceSubstringIntention.kt index 3c39edc0de3..0a5bd88a5d8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceSubstringIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceSubstringIntention.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.openapi.util.TextRange import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.intentions.branchedTransformations.evaluatesTo -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableVariable +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator @@ -30,7 +30,7 @@ abstract class ReplaceSubstringIntention(text: String) : SelfTargetingRangeInten protected abstract fun applicabilityRangeInner(element: KtDotQualifiedExpression): TextRange? override fun applicabilityRange(element: KtDotQualifiedExpression): TextRange? { - if (element.receiverExpression.isStableVariable() && element.isMethodCall("kotlin.text.substring")) { + if (element.receiverExpression.isStable() && element.isMethodCall("kotlin.text.substring")) { return applicabilityRangeInner(element) } 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 f1b673aecec..b3b23758aa5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/IfThenUtils.kt @@ -34,7 +34,9 @@ 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.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getType +import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.TypeUtils @@ -140,7 +142,8 @@ fun KtPostfixExpression.inlineBaseExpressionIfApplicableWithPrompt(editor: Edito (this.baseExpression as? KtNameReferenceExpression)?.inlineIfDeclaredLocallyAndOnlyUsedOnceWithPrompt(editor) } -fun KtExpression.isStableVariable(context: BindingContext = this.analyze()): Boolean { +fun KtExpression.isStable(context: BindingContext = this.analyze()): Boolean { + if (this is KtConstantExpression || this is KtThisExpression) return true val descriptor = BindingContextUtils.extractVariableDescriptorFromReference(context, this) return descriptor is VariableDescriptor && DataFlowValueFactory.isStableValue(descriptor, DescriptorUtils.getContainingModule(descriptor)) @@ -168,11 +171,17 @@ data class IfThenToSelectData( (baseClause as KtDotQualifiedExpression).replaceFirstReceiver( factory, newReceiver!!, safeAccess = true) } - else { + else if (hasImplicitReceiver()) { + factory.createExpressionByPattern("this?.$0", baseClause).insertSafeCalls(factory) + } else { baseClause.insertSafeCalls(factory) } } } + + internal fun hasImplicitReceiver(): Boolean { + return baseClause.getResolvedCall(context)?.getImplicitReceiverValue() != null + } } internal fun KtIfExpression.buildSelectTransformationData(): IfThenToSelectData? { @@ -199,7 +208,6 @@ internal fun KtIfExpression.buildSelectTransformationData(): IfThenToSelectData? when (condition.isNegated) { true -> elseClause to thenClause false -> thenClause to elseClause - else -> return null } } else -> return null diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt index c5074f21378..ed5748d66c0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/DoubleBangToIfThenIntention.kt @@ -31,7 +31,7 @@ import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNullExpression import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableVariable +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtPostfixExpression import org.jetbrains.kotlin.psi.KtPsiFactory @@ -56,7 +56,7 @@ class DoubleBangToIfThenIntention : SelfTargetingRangeIntention(K val left = KtPsiUtil.safeDeparenthesize(element.left!!) val right = KtPsiUtil.safeDeparenthesize(element.right!!) - val leftIsStable = left.isStableVariable() + val leftIsStable = left.isStable() val ifStatement = element.convertToIfNotNullExpression(left, left, right) 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 991906f93cc..bc07690f60a 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 @@ -38,7 +38,7 @@ class IfThenToDoubleBangIntention : SelfTargetingRangeIntention( val matchesAsStatement = element.isUsedAsStatement(context) && (baseClause?.isNullExpressionOrEmptyBlock() ?: true) if (!matchesAsStatement && - !(baseClause?.evaluatesTo(receiverExpression) ?: false && receiverExpression.isStableVariable())) return null + !(baseClause?.evaluatesTo(receiverExpression) ?: false && receiverExpression.isStable())) return null var text = "Replace 'if' expression with '!!' expression" if (!throwExpression.throwsNullPointerExceptionWithNoArguments()) { 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 5fa78985942..408aa1ee258 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 @@ -49,13 +49,15 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention false negatedClause == null && baseClause.isUsedAsExpression(context) -> false negatedClause != null && !negatedClause.isNullExpression() -> false - else -> baseClause.evaluatesTo(receiverExpression) || baseClause.hasFirstReceiverOf(receiverExpression) + else -> baseClause.evaluatesTo(receiverExpression) || baseClause.hasFirstReceiverOf(receiverExpression) || + receiverExpression is KtThisExpression && hasImplicitReceiver() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt index dd59947a5a9..8e40a51a4f4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt @@ -23,7 +23,7 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.idea.core.appendElement import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableVariable +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange import org.jetbrains.kotlin.psi.* @@ -36,7 +36,7 @@ class MergeWhenIntention : SelfTargetingRangeIntention(KtWhenE val subject1 = element.subjectExpression val subject2 = next.subjectExpression if (!subject1.matches(subject2)) return null - if (subject1 != null && !subject1.isStableVariable()) return null + if (subject1 != null && !subject1.isStable()) return null val entries1 = element.entries val entries2 = next.entries diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt index dbcb9067a3b..f855fff30f2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/SafeAccessToIfThenIntention.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.convertToIfNotNullExpression import org.jetbrains.kotlin.idea.intentions.branchedTransformations.introduceValueForCondition -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStableVariable +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isStable import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement @@ -37,7 +37,7 @@ class SafeAccessToIfThenIntention : SelfTargetingRangeIntentionnull) throw NullPointerException() else this \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifThenToDoubleBang/this.kt.after b/idea/testData/intentions/branched/ifThenToDoubleBang/this.kt.after new file mode 100644 index 00000000000..cd88e33339e --- /dev/null +++ b/idea/testData/intentions/branched/ifThenToDoubleBang/this.kt.after @@ -0,0 +1,2 @@ +// WITH_RUNTIME +fun String?.foo() = this!! \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt b/idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt new file mode 100644 index 00000000000..187b60f183d --- /dev/null +++ b/idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt @@ -0,0 +1,2 @@ +// WITH_RUNTIME +fun String?.foo() = if (this == null) true else isEmpty() \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt.after b/idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt.after new file mode 100644 index 00000000000..b87e929e9ed --- /dev/null +++ b/idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt.after @@ -0,0 +1,2 @@ +// WITH_RUNTIME +fun String?.foo() = this?.isEmpty() ?: true \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifThenToElvis/inspectionData/expected.xml b/idea/testData/intentions/branched/ifThenToElvis/inspectionData/expected.xml index b57d4d3b862..be8ffd9f47f 100644 --- a/idea/testData/intentions/branched/ifThenToElvis/inspectionData/expected.xml +++ b/idea/testData/intentions/branched/ifThenToElvis/inspectionData/expected.xml @@ -95,4 +95,12 @@ If-Then foldable to '?:' Replace 'if' expression with elvis expression + + implicitReceiver.kt + 2 + light_idea_test_case + + If-Then foldable to '?:' + Replace 'if' expression with elvis expression + diff --git a/idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt b/idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt new file mode 100644 index 00000000000..21be42334ab --- /dev/null +++ b/idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt @@ -0,0 +1,2 @@ +// WITH_RUNTIME +fun String?.foo() = if (this == null) null else isEmpty() diff --git a/idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt.after b/idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt.after new file mode 100644 index 00000000000..3fd38588bbe --- /dev/null +++ b/idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt.after @@ -0,0 +1,2 @@ +// WITH_RUNTIME +fun String?.foo() = this?.isEmpty() \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifThenToSafeAccess/inspectionData/expected.xml b/idea/testData/intentions/branched/ifThenToSafeAccess/inspectionData/expected.xml index 97907791cc2..44a2be33357 100644 --- a/idea/testData/intentions/branched/ifThenToSafeAccess/inspectionData/expected.xml +++ b/idea/testData/intentions/branched/ifThenToSafeAccess/inspectionData/expected.xml @@ -143,4 +143,12 @@ If-Then foldable to '?.' Remove redundant 'if' expression + + implicitReceiver.kt + 2 + light_idea_test_case + + If-Then foldable to '?.' + Remove redundant 'if' expression + diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 2f951911a8e..5f58f5d279b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -1381,6 +1381,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("this.kt") + public void testThis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToDoubleBang/this.kt"); + doTest(fileName); + } + @TestMetadata("throwByFqName.kt") public void testThrowByFqName() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToDoubleBang/throwByFqName.kt"); @@ -1510,6 +1516,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("implicitReceiver.kt") + public void testImplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/implicitReceiver.kt"); + doTest(fileName); + } + @TestMetadata("isCheck.kt") public void testIsCheck() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/isCheck.kt"); @@ -1765,6 +1777,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("implicitReceiver.kt") + public void testImplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToSafeAccess/implicitReceiver.kt"); + doTest(fileName); + } + @TestMetadata("isCheckSimple.kt") public void testIsCheckSimple() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToSafeAccess/isCheckSimple.kt");