diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFix.kt index af7e2f3473c..9234c2a56d6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFix.kt @@ -46,7 +46,7 @@ abstract class ReplaceCallFix( override fun invoke(project: Project, editor: Editor?, file: KtFile) { val element = element ?: return - val elvis = elvisOrEmpty(notNullNeeded) + val elvis = element.elvisOrEmpty(notNullNeeded) val betweenReceiverAndOperation = element.elementsBetweenReceiverAndOperation().joinToString(separator = "") { it.text } val newExpression = KtPsiFactory(element).createExpressionByPattern( "$0$betweenReceiverAndOperation$operation$1$elvis", @@ -54,7 +54,7 @@ abstract class ReplaceCallFix( element.selectorExpression!! ) val replacement = element.replace(newExpression) - if (notNullNeeded) { + if (elvis.isNotEmpty()) { replacement.moveCaretToEnd(editor, project) } } @@ -78,10 +78,10 @@ class ReplaceImplicitReceiverCallFix( override fun invoke(project: Project, editor: Editor?, file: KtFile) { val element = element ?: return - val elvis = elvisOrEmpty(notNullNeeded) + val elvis = element.elvisOrEmpty(notNullNeeded) val newExpression = KtPsiFactory(element).createExpressionByPattern("this?.$0$elvis", element) val replacement = element.replace(newExpression) - if (notNullNeeded) { + if (elvis.isNotEmpty()) { replacement.moveCaretToEnd(editor, project) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFixUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFixUtils.kt index 302a1e54d9d..1ebc8dc91e0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFixUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceCallFixUtils.kt @@ -21,14 +21,20 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getType -fun elvisOrEmpty(notNullNeeded: Boolean): String = if (notNullNeeded) "?:" else "" +fun KtExpression.elvisOrEmpty(notNullNeeded: Boolean): String { + if (!notNullNeeded) return "" + val binaryExpression = getStrictParentOfType() + return if (binaryExpression?.left == this && binaryExpression.operationToken == KtTokens.ELVIS) "" else "?:" +} fun KtExpression.shouldHaveNotNullType(): Boolean { val parent = parent diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt index f60abec2479..e6673265ea8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt @@ -43,7 +43,7 @@ class ReplaceInfixOrOperatorCallFix( override fun invoke(project: Project, editor: Editor?, file: KtFile) { val element = element ?: return val psiFactory = KtPsiFactory(file) - val elvis = elvisOrEmpty(notNullNeeded) + val elvis = element.elvisOrEmpty(notNullNeeded) var replacement: PsiElement? = null when (element) { is KtArrayAccessExpression -> { @@ -83,7 +83,7 @@ class ReplaceInfixOrOperatorCallFix( } } } - if (notNullNeeded) { + if (elvis.isNotEmpty()) { replacement?.moveCaretToEnd(editor, project) } } diff --git a/idea/testData/quickfix/replaceInfixOrOperatorCall/hasElvis.kt b/idea/testData/quickfix/replaceInfixOrOperatorCall/hasElvis.kt new file mode 100644 index 00000000000..dbf659385c4 --- /dev/null +++ b/idea/testData/quickfix/replaceInfixOrOperatorCall/hasElvis.kt @@ -0,0 +1,7 @@ +// "Replace with safe (?.) call" "true" +// WITH_RUNTIME + +fun foo(list: List?) { + var s = "" + s = list[0] ?: "" +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceInfixOrOperatorCall/hasElvis.kt.after b/idea/testData/quickfix/replaceInfixOrOperatorCall/hasElvis.kt.after new file mode 100644 index 00000000000..a22be396f5d --- /dev/null +++ b/idea/testData/quickfix/replaceInfixOrOperatorCall/hasElvis.kt.after @@ -0,0 +1,7 @@ +// "Replace with safe (?.) call" "true" +// WITH_RUNTIME + +fun foo(list: List?) { + var s = "" + s = list?.get(0) ?: "" +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceWithSafeCall/hasElvis.kt b/idea/testData/quickfix/replaceWithSafeCall/hasElvis.kt new file mode 100644 index 00000000000..4954207bfc5 --- /dev/null +++ b/idea/testData/quickfix/replaceWithSafeCall/hasElvis.kt @@ -0,0 +1,10 @@ +// "Replace with safe (?.) call" "true" +// WITH_RUNTIME + +fun main() { + var a = foo().length ?: 0 +} + +fun foo(): String? { + return "" +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceWithSafeCall/hasElvis.kt.after b/idea/testData/quickfix/replaceWithSafeCall/hasElvis.kt.after new file mode 100644 index 00000000000..7584c83c274 --- /dev/null +++ b/idea/testData/quickfix/replaceWithSafeCall/hasElvis.kt.after @@ -0,0 +1,10 @@ +// "Replace with safe (?.) call" "true" +// WITH_RUNTIME + +fun main() { + var a = foo()?.length ?: 0 +} + +fun foo(): String? { + return "" +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceWithSafeCall/hasElvis2.kt b/idea/testData/quickfix/replaceWithSafeCall/hasElvis2.kt new file mode 100644 index 00000000000..8057a617bc2 --- /dev/null +++ b/idea/testData/quickfix/replaceWithSafeCall/hasElvis2.kt @@ -0,0 +1,9 @@ +// "Replace with safe (this?.) call" "true" +// WITH_RUNTIME +var i = 0 + +fun foo(a: String?) { + a.run { + i = length ?: 0 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceWithSafeCall/hasElvis2.kt.after b/idea/testData/quickfix/replaceWithSafeCall/hasElvis2.kt.after new file mode 100644 index 00000000000..3a4fd8ed84a --- /dev/null +++ b/idea/testData/quickfix/replaceWithSafeCall/hasElvis2.kt.after @@ -0,0 +1,9 @@ +// "Replace with safe (this?.) call" "true" +// WITH_RUNTIME +var i = 0 + +fun foo(a: String?) { + a.run { + i = this?.length ?: 0 + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index cb3d005851e..aaf37d6962e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -10901,6 +10901,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt"); } + @TestMetadata("hasElvis.kt") + public void testHasElvis() throws Exception { + runTest("idea/testData/quickfix/replaceInfixOrOperatorCall/hasElvis.kt"); + } + @TestMetadata("list.kt") public void testList() throws Exception { runTest("idea/testData/quickfix/replaceInfixOrOperatorCall/list.kt"); @@ -11098,6 +11103,16 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/replaceWithSafeCall/functionCall.kt"); } + @TestMetadata("hasElvis.kt") + public void testHasElvis() throws Exception { + runTest("idea/testData/quickfix/replaceWithSafeCall/hasElvis.kt"); + } + + @TestMetadata("hasElvis2.kt") + public void testHasElvis2() throws Exception { + runTest("idea/testData/quickfix/replaceWithSafeCall/hasElvis2.kt"); + } + @TestMetadata("invokeOperator.kt") public void testInvokeOperator() throws Exception { runTest("idea/testData/quickfix/replaceWithSafeCall/invokeOperator.kt");