diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt index 74334b956ad..3ddb8afbd7d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFix.kt @@ -19,12 +19,18 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project +import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze 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 +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.types.expressions.OperatorConventions +import org.jetbrains.kotlin.types.typeUtil.isBoolean class ReplaceInfixOrOperatorCallFix(element: KtExpression) : KotlinQuickFixAction(element) { @@ -87,7 +93,11 @@ class ReplaceInfixOrOperatorCallFix(element: KtExpression) : KotlinQuickFixActio return when (parent) { is KtBinaryExpression -> { if (parent.left == null || parent.right == null) null - else ReplaceInfixOrOperatorCallFix(parent) + else { + if (parent.operationToken in OperatorConventions.COMPARISON_OPERATIONS && + parent.analyze()[BindingContext.EXPECTED_EXPRESSION_TYPE, parent]?.isBoolean() ?: false) null + else ReplaceInfixOrOperatorCallFix(parent) + } } is KtCallExpression -> { if (parent.calleeExpression == null) null diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt index b20070b06bd..978dd64e9c2 100644 --- a/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt @@ -2,6 +2,6 @@ // ERROR: Infix call corresponds to a dot-qualified call 'value?.compareTo(1).compareTo(0)' which is not allowed on a nullable receiver 'value?.compareTo(1)'. Use '?.'-qualified call instead var foo: Int? = null set(value) { - if(value < 1) { + if((value < 1) ?: false) { } } \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt.after b/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt.after index 595dcfc3393..7217ba54619 100644 --- a/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt.after +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/operator.kt.after @@ -2,6 +2,6 @@ // ERROR: Infix call corresponds to a dot-qualified call 'value?.compareTo(1).compareTo(0)' which is not allowed on a nullable receiver 'value?.compareTo(1)'. Use '?.'-qualified call instead var foo: Int? = null set(value) { - if(value?.compareTo(1) < 0) { + if((value?.compareTo(1) < 0) ?: false) { } } \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInCondition.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInCondition.kt new file mode 100644 index 00000000000..2714895ba42 --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInCondition.kt @@ -0,0 +1,11 @@ +// "Replace with safe (?.) call" "false" +// ACTION: Add non-null asserted (!!) call +// ACTION: Flip '<' +// ACTION: Replace overloaded operator with function call +// ERROR: Infix call corresponds to a dot-qualified call 'w?.x.compareTo(42)' which is not allowed on a nullable receiver 'w?.x'. Use '?.'-qualified call instead + +class Wrapper(val x: Int) + +fun test(w: Wrapper?) { + if (w?.x < 42) {} +} \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInLogic.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInLogic.kt new file mode 100644 index 00000000000..1d9be5fa60a --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInLogic.kt @@ -0,0 +1,12 @@ +// "Replace with safe (?.) call" "false" +// ACTION: Add non-null asserted (!!) call +// ACTION: Flip '>' +// ACTION: Replace overloaded operator with function call +// ACTION: Simplify boolean expression +// ERROR: Infix call corresponds to a dot-qualified call 'w?.x.compareTo(42)' which is not allowed on a nullable receiver 'w?.x'. Use '?.'-qualified call instead + +class Wrapper(val x: Int) + +fun test(w: Wrapper?) { + val t = 1 < 2 && w?.x > 42 +} \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhen.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhen.kt new file mode 100644 index 00000000000..09c13cca28f --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhen.kt @@ -0,0 +1,13 @@ +// "Replace with safe (?.) call" "false" +// ACTION: Add non-null asserted (!!) call +// ACTION: Flip '<=' +// ACTION: Replace overloaded operator with function call +// ERROR: Infix call corresponds to a dot-qualified call 'w?.x.compareTo(42)' which is not allowed on a nullable receiver 'w?.x'. Use '?.'-qualified call instead + +class Wrapper(val x: Int) + +fun test(w: Wrapper?) { + when { + w?.x <= 42 -> {} + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhile.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhile.kt new file mode 100644 index 00000000000..7177842cc77 --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhile.kt @@ -0,0 +1,11 @@ +// "Replace with safe (?.) call" "false" +// ACTION: Add non-null asserted (!!) call +// ACTION: Flip '>=' +// ACTION: Replace overloaded operator with function call +// ERROR: Infix call corresponds to a dot-qualified call 'w?.x.compareTo(42)' which is not allowed on a nullable receiver 'w?.x'. Use '?.'-qualified call instead + +class Wrapper(val x: Int) + +fun test(w: Wrapper?) { + while (w?.x >= 42) {} +} \ 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 8efe786c017..2a79ac14784 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6153,6 +6153,30 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("unsafeComparisonInCondition.kt") + public void testUnsafeComparisonInCondition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInCondition.kt"); + doTest(fileName); + } + + @TestMetadata("unsafeComparisonInLogic.kt") + public void testUnsafeComparisonInLogic() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInLogic.kt"); + doTest(fileName); + } + + @TestMetadata("unsafeComparisonInWhen.kt") + public void testUnsafeComparisonInWhen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhen.kt"); + doTest(fileName); + } + + @TestMetadata("unsafeComparisonInWhile.kt") + public void testUnsafeComparisonInWhile() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeComparisonInWhile.kt"); + doTest(fileName); + } + @TestMetadata("unsafeGet.kt") public void testUnsafeGet() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeGet.kt");