diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 2bf5dfb1477..393e676d3f1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -204,6 +204,7 @@ class QuickFixRegistrar : QuickFixContributor { UNSAFE_CALL.registerFactory(AddExclExclCallFix) UNNECESSARY_NOT_NULL_ASSERTION.registerFactory(RemoveExclExclCallFix) UNSAFE_INFIX_CALL.registerFactory(ReplaceInfixCallFix) + UNSAFE_CALL.registerFactory(ReplaceInfixCallFix) // [] only AMBIGUOUS_ANONYMOUS_TYPE_INFERRED.registerActions(SpecifyTypeExplicitlyFix()) PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.registerActions(SpecifyTypeExplicitlyFix()) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixCallFix.kt index e5a4ed43816..375735c49f4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixCallFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixCallFix.kt @@ -23,9 +23,10 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -class ReplaceInfixCallFix(element: KtBinaryExpression) : KotlinQuickFixAction(element) { +class ReplaceInfixCallFix(element: KtExpression) : KotlinQuickFixAction(element) { override fun getText() = "Replace with safe (?.) call" @@ -33,16 +34,38 @@ class ReplaceInfixCallFix(element: KtBinaryExpression) : KotlinQuickFixAction { + val assignment = element.getAssignmentByLHS() + val right = assignment?.right + val arrayExpression = element.arrayExpression ?: return + if (assignment != null) { + if (right == null) return + val newExpression = psiFactory.createExpressionByPattern( + "$0?.set($1, $2)", arrayExpression, element.indexExpressions.joinToString(", ") { it.text }, right) + assignment.replace(newExpression) + } + else { + val newExpression = psiFactory.createExpressionByPattern( + "$0?.get($1)", arrayExpression, element.indexExpressions.joinToString(", ") { it.text }) + element.replace(newExpression) + } + } + is KtBinaryExpression -> { + if (element.operationToken == KtTokens.IDENTIFIER) { + val newExpression = psiFactory.createExpressionByPattern( + "$0?.$1($2)", element.left ?: return, element.operationReference, element.right ?: return) + element.replace(newExpression) + } + else { + val nameExpression = OperatorToFunctionIntention.convert(element).second + val callExpression = nameExpression.parent as KtCallExpression + val qualifiedExpression = callExpression.parent as KtDotQualifiedExpression + val safeExpression = psiFactory.createExpressionByPattern( + "$0?.$1", qualifiedExpression.receiverExpression, callExpression) + qualifiedExpression.replace(safeExpression) + } + } } } @@ -50,9 +73,14 @@ class ReplaceInfixCallFix(element: KtBinaryExpression) : KotlinQuickFixAction()!! - if (expression.left == null || expression.right == null) return null - return ReplaceInfixCallFix(expression) + val expression = diagnostic.psiElement + if (expression is KtArrayAccessExpression) { + if (expression.arrayExpression == null) return null + return ReplaceInfixCallFix(expression) + } + val binaryExpression = expression.parent as? KtBinaryExpression ?: return null + if (binaryExpression.left == null || binaryExpression.right == null) return null + return ReplaceInfixCallFix(binaryExpression) } } } diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt new file mode 100644 index 00000000000..a39b9af97b9 --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt @@ -0,0 +1,4 @@ +// "Replace with safe (?.) call" "true" + +operator fun Int.get(row: Int, column: Int) = this +fun foo(arg: Int?) = arg[42, 13] \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt.after b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt.after new file mode 100644 index 00000000000..cc367c7c68c --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt.after @@ -0,0 +1,4 @@ +// "Replace with safe (?.) call" "true" + +operator fun Int.get(row: Int, column: Int) = this +fun foo(arg: Int?) = arg?.get(42, 13) \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeSet.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeSet.kt new file mode 100644 index 00000000000..99f4c3ecdfe --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeSet.kt @@ -0,0 +1,6 @@ +// "Replace with safe (?.) call" "true" + +operator fun Int.set(row: Int, column: Int, value: Int) {} +fun foo(arg: Int?) { + arg[42, 13] = 0 +} \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeSet.kt.after b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeSet.kt.after new file mode 100644 index 00000000000..ce2ed0ba5e0 --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeSet.kt.after @@ -0,0 +1,6 @@ +// "Replace with safe (?.) call" "true" + +operator fun Int.set(row: Int, column: Int, value: Int) {} +fun foo(arg: Int?) { + arg?.set(42, 13, 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 38123dea418..de01fb093ec 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6072,6 +6072,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("unsafeGet.kt") + public void testUnsafeGet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt"); + doTest(fileName); + } + @TestMetadata("unsafeInfixCall.kt") public void testUnsafeInfixCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInfixCall.kt"); @@ -6083,6 +6089,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafePlus.kt"); doTest(fileName); } + + @TestMetadata("unsafeSet.kt") + public void testUnsafeSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeSet.kt"); + doTest(fileName); + } } }