diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt index 0006abbcae9..d8d23257153 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt @@ -96,7 +96,8 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe Errors.INFIX_MODIFIER_REQUIRED, Errors.CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS, Errors.DEPRECATED_TYPE_PARAMETER_SYNTAX, - Errors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS + Errors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS, + Errors.INVOKE_ON_EXTENSION_FUNCTION_WITH_EXPLICIT_DISPATCH_RECEIVER ) private fun Diagnostic.isObsoleteLabel(): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 7330b57f93f..937ca1ac21a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClas import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateLocalVariableActionFactory import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByNamedArgumentActionFactory import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByRefActionFactory +import org.jetbrains.kotlin.idea.quickfix.migration.InvokeOnExtensionFunctionWithExplicitReceiverFix import org.jetbrains.kotlin.idea.quickfix.migration.MigrateTypeParameterListFix import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageInWholeProjectFix @@ -343,5 +344,7 @@ public class QuickFixRegistrar : QuickFixContributor { DELEGATE_RESOLVED_TO_DEPRECATED_CONVENTION.registerFactory(DeprecatedFunctionConventionFix) DEPRECATED_UNARY_PLUS_MINUS.registerFactory(DeprecatedFunctionConventionFix) + + INVOKE_ON_EXTENSION_FUNCTION_WITH_EXPLICIT_DISPATCH_RECEIVER.registerFactory(InvokeOnExtensionFunctionWithExplicitReceiverFix) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/migration/InvokeOnExtensionFunctionWithExplicitReceiverFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/migration/InvokeOnExtensionFunctionWithExplicitReceiverFix.kt new file mode 100644 index 00000000000..dabe9666831 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/migration/InvokeOnExtensionFunctionWithExplicitReceiverFix.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix.migration + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.quickfix.CleanupFix +import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction +import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector + +class InvokeOnExtensionFunctionWithExplicitReceiverFix(qualifiedExpression: KtDotQualifiedExpression) : KotlinQuickFixAction(qualifiedExpression), CleanupFix { + override fun getFamilyName() = "Fix extension function value call" + override fun getText() = familyName + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val callExpression = element.selectorExpression as KtCallExpression + val pattern = if (element is KtSafeQualifiedExpression)"($0?.$1)" else "($0.$1)" + val newCallee = KtPsiFactory(file).createExpressionByPattern(pattern, element.receiverExpression, callExpression.calleeExpression!!) + val newCallExpression = element.replaced(callExpression) + newCallExpression.calleeExpression!!.replace(newCallee) + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val callee = Errors.INVOKE_ON_EXTENSION_FUNCTION_WITH_EXPLICIT_DISPATCH_RECEIVER.cast(diagnostic).a as? KtNameReferenceExpression ?: return null + val callExpression = callee.parent as? KtCallExpression ?: return null + val qualifiedExpression = callExpression.getQualifiedExpressionForSelector() as? KtDotQualifiedExpression ?: return null + return InvokeOnExtensionFunctionWithExplicitReceiverFix(qualifiedExpression) + } + } +} \ No newline at end of file diff --git a/idea/testData/inspections/cleanup/cleanup.kt b/idea/testData/inspections/cleanup/cleanup.kt index f154bd68e33..55917724884 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt +++ b/idea/testData/inspections/cleanup/cleanup.kt @@ -70,4 +70,16 @@ fun typed() { fun withTypeParameters() where T : Comparable { } -C() willBeInfix 1 +val x = C() willBeInfix 1 + +class AAA { + val foo: BBB.() -> Unit get() = null!! +} + +class BBB + +fun test(a: AAA, b: BBB) { + with(b) { + a.foo() + } +} diff --git a/idea/testData/inspections/cleanup/cleanup.kt.after b/idea/testData/inspections/cleanup/cleanup.kt.after index 70c9623aa18..9d5fbb09a4c 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt.after +++ b/idea/testData/inspections/cleanup/cleanup.kt.after @@ -61,7 +61,7 @@ class C { fun bar() = C::foo - fun willBeInfix(i: Int) {} + infix fun willBeInfix(i: Int) {} } fun typed() { @@ -70,4 +70,16 @@ fun typed() { fun withTypeParameters() where T : Cloneable, T : Comparable { } -C() willBeInfix 1 +val x = C() willBeInfix 1 + +class AAA { + val foo: BBB.() -> Unit get() = null!! +} + +class BBB + +fun test(a: AAA, b: BBB) { + with(b) { + (a.foo)() + } +} diff --git a/idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix/simple.kt b/idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix/simple.kt new file mode 100644 index 00000000000..ef7faf9a683 --- /dev/null +++ b/idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix/simple.kt @@ -0,0 +1,15 @@ +// "Fix extension function value call" "true" + +class A { + val foo: B.() -> Unit get() = null!! +} + +class B + +fun test(a: A, b: B) { + with(b) { + a.foo() + } +} + +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() \ No newline at end of file diff --git a/idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix/simple.kt.after b/idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix/simple.kt.after new file mode 100644 index 00000000000..704a2406717 --- /dev/null +++ b/idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix/simple.kt.after @@ -0,0 +1,15 @@ +// "Fix extension function value call" "true" + +class A { + val foo: B.() -> Unit get() = null!! +} + +class B + +fun test(a: A, b: B) { + with(b) { + (a.foo)() + } +} + +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() \ 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 169eb255fac..22763a223ea 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -4361,6 +4361,21 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class InvokeOnExtensionFunctionWithExplicitReceiverFix extends AbstractQuickFixTest { + public void testAllFilesPresentInInvokeOnExtensionFunctionWithExplicitReceiverFix() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/migration/invokeOnExtensionFunctionWithExplicitReceiverFix/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration/missingConstructorKeyword") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)