diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ConvertExtensionToFunctionTypeFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ConvertExtensionToFunctionTypeFix.kt new file mode 100644 index 00000000000..1ccd7c46a2e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ConvertExtensionToFunctionTypeFix.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2016 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 + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType +import org.jetbrains.kotlin.builtins.isExtensionFunctionType +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.KotlinType + +class ConvertExtensionToFunctionTypeFix(element: KtTypeReference, type: KotlinType) : KotlinQuickFixAction(element) { + + private val targetTypeStringShort = type.renderType(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES) + private val targetTypeStringLong = type.renderType(IdeDescriptorRenderers.SOURCE_CODE) + + override fun getText() = "Convert supertype to '$targetTypeStringShort'" + override fun getFamilyName() = "Convert extension function type to regular function type" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val replaced = element.replaced(KtPsiFactory(project).createType(targetTypeStringLong)) + ShortenReferences.DEFAULT.process(replaced) + } + + private fun KotlinType.renderType(renderer: DescriptorRenderer) = buildString { + append('(') + arguments.dropLast(1).map { renderer.renderType(it.type) }.joinTo(this@buildString, ", ") + append(") -> ") + append(renderer.renderType(getReturnTypeFromFunctionType(this@renderType))) + } + + companion object Factory : KotlinIntentionActionsFactory() { + override fun doCreateActions(diagnostic: Diagnostic): List { + val casted = Errors.SUPERTYPE_IS_EXTENSION_FUNCTION_TYPE.cast(diagnostic) + val element = casted.psiElement + + val type = element.analyze(BodyResolveMode.PARTIAL).get(BindingContext.TYPE, element) ?: return emptyList() + if (!type.isExtensionFunctionType) return emptyList() + + return listOf(ConvertExtensionToFunctionTypeFix(element, type)) + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index da30b14e408..d4013e4f74e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -380,5 +380,7 @@ class QuickFixRegistrar : QuickFixContributor { DATA_CLASS_NOT_PROPERTY_PARAMETER.registerFactory(AddValVarToConstructorParameterAction.QuickFixFactory) NON_LOCAL_RETURN_NOT_ALLOWED.registerFactory(AddCrossInlineFix) + + SUPERTYPE_IS_EXTENSION_FUNCTION_TYPE.registerFactory(ConvertExtensionToFunctionTypeFix) } } diff --git a/idea/testData/quickfix/superTypeIsExtensionType/typeWith1Argument.kt b/idea/testData/quickfix/superTypeIsExtensionType/typeWith1Argument.kt new file mode 100644 index 00000000000..1ca0d04b48a --- /dev/null +++ b/idea/testData/quickfix/superTypeIsExtensionType/typeWith1Argument.kt @@ -0,0 +1,6 @@ +// "Convert supertype to '(String, String) -> Unit'" "true" + +class Foo : String.(String) -> Unit { + override fun invoke(p1: String, p2: String) { + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/superTypeIsExtensionType/typeWith1Argument.kt.after b/idea/testData/quickfix/superTypeIsExtensionType/typeWith1Argument.kt.after new file mode 100644 index 00000000000..e57f2096693 --- /dev/null +++ b/idea/testData/quickfix/superTypeIsExtensionType/typeWith1Argument.kt.after @@ -0,0 +1,6 @@ +// "Convert supertype to '(String, String) -> Unit'" "true" + +class Foo : (String, String) -> Unit { + override fun invoke(p1: String, p2: String) { + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/superTypeIsExtensionType/typeWithNoArgument.kt b/idea/testData/quickfix/superTypeIsExtensionType/typeWithNoArgument.kt new file mode 100644 index 00000000000..abf6713a8b9 --- /dev/null +++ b/idea/testData/quickfix/superTypeIsExtensionType/typeWithNoArgument.kt @@ -0,0 +1,6 @@ +// "Convert supertype to '(String) -> Unit'" "true" + +class Foo : String.() -> Unit { + override fun invoke(p1: String) { + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/superTypeIsExtensionType/typeWithNoArgument.kt.after b/idea/testData/quickfix/superTypeIsExtensionType/typeWithNoArgument.kt.after new file mode 100644 index 00000000000..0f2b8e4f9d6 --- /dev/null +++ b/idea/testData/quickfix/superTypeIsExtensionType/typeWithNoArgument.kt.after @@ -0,0 +1,6 @@ +// "Convert supertype to '(String) -> Unit'" "true" + +class Foo : (String) -> Unit { + override fun invoke(p1: String) { + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/superTypeIsExtensionType/typeWithTypeArgument.kt b/idea/testData/quickfix/superTypeIsExtensionType/typeWithTypeArgument.kt new file mode 100644 index 00000000000..4e6123e7fcc --- /dev/null +++ b/idea/testData/quickfix/superTypeIsExtensionType/typeWithTypeArgument.kt @@ -0,0 +1,6 @@ +// "Convert supertype to '(String, T) -> Unit'" "true" + +class Foo : String.(T) -> Unit { + override fun invoke(p1: String, p2: T) { + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/superTypeIsExtensionType/typeWithTypeArgument.kt.after b/idea/testData/quickfix/superTypeIsExtensionType/typeWithTypeArgument.kt.after new file mode 100644 index 00000000000..d3f97928804 --- /dev/null +++ b/idea/testData/quickfix/superTypeIsExtensionType/typeWithTypeArgument.kt.after @@ -0,0 +1,6 @@ +// "Convert supertype to '(String, T) -> Unit'" "true" + +class Foo : (String, T) -> Unit { + override fun invoke(p1: String, p2: T) { + } +} \ 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 58e539a5f81..b0e152610e6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6518,6 +6518,33 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/superTypeIsExtensionType") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuperTypeIsExtensionType extends AbstractQuickFixTest { + public void testAllFilesPresentInSuperTypeIsExtensionType() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/superTypeIsExtensionType"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("typeWith1Argument.kt") + public void testTypeWith1Argument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/superTypeIsExtensionType/typeWith1Argument.kt"); + doTest(fileName); + } + + @TestMetadata("typeWithNoArgument.kt") + public void testTypeWithNoArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/superTypeIsExtensionType/typeWithNoArgument.kt"); + doTest(fileName); + } + + @TestMetadata("typeWithTypeArgument.kt") + public void testTypeWithTypeArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/superTypeIsExtensionType/typeWithTypeArgument.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/supercalls") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)