diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/MainKtQuickFixRegistrar.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/MainKtQuickFixRegistrar.kt index 4f473447136..849634ed7aa 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/MainKtQuickFixRegistrar.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/MainKtQuickFixRegistrar.kt @@ -111,6 +111,7 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() { registerApplicator(ReplaceCallFixFactories.unsafeCallFactory) registerApplicator(ReplaceCallFixFactories.unsafeInfixCallFactory) registerApplicator(ReplaceCallFixFactories.unsafeOperatorCallFactory) + registerApplicator(ReplaceCallFixFactories.unsafeImplicitInvokeCallFactory) registerApplicator(AddExclExclCallFixFactories.unsafeCallFactory) registerApplicator(AddExclExclCallFixFactories.unsafeInfixCallFactory) registerApplicator(AddExclExclCallFixFactories.unsafeOperatorCallFactory) diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt index 3608f1f2742..9fdff3e9163 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/quickfix/fixes/ReplaceCallFixFactories.kt @@ -66,6 +66,26 @@ object ReplaceCallFixFactories { listOf(ReplaceInfixOrOperatorCallFix(target, shouldHaveNotNullType(target), diagnostic.operator)) } + val unsafeImplicitInvokeCallFactory = + diagnosticFixFactory { diagnostic -> + val target = diagnostic.psi as? KtNameReferenceExpression ?: return@diagnosticFixFactory emptyList() + + val callExpression = target.parent as? KtCallExpression ?: return@diagnosticFixFactory emptyList() + val qualifiedExpression = callExpression.parent as? KtQualifiedExpression + if (qualifiedExpression == null) { + // TODO: This matches FE 1.0 behavior (see ReplaceInfixOrOperatorCallFixFactory.kt) but we should be able to do the fix + // when the call is a qualified expression. We just need to make sure to pass any extension receiver as an argument, e.g.: + // + // fun test(exec: (String.() -> Unit)?) = "".exec() // Can be fixed to exec?.invoke("") + // + // This should be differentiated from this case without an extension receiver: + // + // class A(val exec: (() -> Unit)?) + // fun test(a: A) = a.exec() // Can be fixed to a.exec?.invoke() + listOf(ReplaceInfixOrOperatorCallFix(callExpression, shouldHaveNotNullType(callExpression))) + } else emptyList() + } + private fun KtAnalysisSession.shouldHaveNotNullType(expression: KtExpression): Boolean { // This function is used to determine if we may need to add an elvis operator after the safe call. For example, to replace // `s.length` in `val x: Int = s.length` with a safe call, it should be replaced with `s.length ?: `. diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.fir.kt.after b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.fir.kt.after new file mode 100644 index 00000000000..262f6dfe3db --- /dev/null +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.fir.kt.after @@ -0,0 +1,3 @@ +// "Replace with safe (?.) call" "true" + +fun foo(exec: (() -> Unit)?) = exec?.invoke() ?: \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt index bf5448f09c2..1639ac13d73 100644 --- a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt @@ -1,5 +1,3 @@ // "Replace with safe (?.) call" "true" -fun foo(exec: (() -> Unit)?) = exec() - -/* IGNORE_FIR */ +fun foo(exec: (() -> Unit)?) = exec() \ No newline at end of file diff --git a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt.after b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt.after index d46ef7dd4ad..8242b09bf2f 100644 --- a/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt.after +++ b/idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvoke.kt.after @@ -1,5 +1,3 @@ // "Replace with safe (?.) call" "true" -fun foo(exec: (() -> Unit)?) = exec?.invoke() - -/* IGNORE_FIR */ +fun foo(exec: (() -> Unit)?) = exec?.invoke() \ No newline at end of file diff --git a/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt b/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt index 6e2973812a0..8f9864a079a 100644 --- a/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt +++ b/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt @@ -5,6 +5,4 @@ fun bar() { val fff: (() -> Int)? = { 1 } var i: Int = 1 i = fff() -} - -/* IGNORE_FIR */ +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt.after b/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt.after index 0a5f42658b3..233525ac994 100644 --- a/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt.after +++ b/idea/testData/quickfix/replaceInfixOrOperatorCall/assignmentCallExpression.kt.after @@ -5,6 +5,4 @@ fun bar() { val fff: (() -> Int)? = { 1 } var i: Int = 1 i = fff?.invoke() ?: -} - -/* IGNORE_FIR */ +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt b/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt index fc03770bbb4..3b062c34e48 100644 --- a/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt +++ b/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt @@ -6,6 +6,4 @@ fun foo() {} fun bar() { val fff: (() -> Unit)? = ::foo fff() -} - -/* IGNORE_FIR */ +} \ No newline at end of file diff --git a/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt.after b/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt.after index 288f140be4a..918037f70b1 100644 --- a/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt.after +++ b/idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt.after @@ -6,6 +6,4 @@ fun foo() {} fun bar() { val fff: (() -> Unit)? = ::foo fff?.invoke() -} - -/* IGNORE_FIR */ +} \ No newline at end of file