From 1f6f617546c824420d3bba4232e4fcfd6df3840f Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 29 Sep 2015 19:14:40 +0300 Subject: [PATCH] Create from Usage: Suggest synthetic function classes as extension receivers --- .../callableBuilder/CallableBuilder.kt | 7 ++++++- .../CreateCallableFromUsageFix.kt | 19 ++++++++++++++----- .../call/functionalTypeInReceiver.kt | 5 +++++ .../call/functionalTypeInReceiver.kt.after | 9 +++++++++ .../idea/quickfix/QuickFixTestGenerated.java | 6 ++++++ 5 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index bb220ac81d7..e7f6571b8ca 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -35,6 +35,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode import org.jetbrains.kotlin.descriptors.* @@ -435,7 +436,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } else null - val ownerTypeString = if (isExtension) "${receiverTypeCandidate!!.renderedType!!}." else "" + val ownerTypeString = if (isExtension) { + val renderedType = receiverTypeCandidate!!.renderedType!! + val isFunctionType = receiverTypeCandidate.theType.constructor.declarationDescriptor is FunctionClassDescriptor + if (isFunctionType) "($renderedType)." else "$renderedType." + } else "" val classKind = (callableInfo as? PrimaryConstructorInfo)?.classInfo?.kind diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt index 885ae239c9f..f8fcb38e2b1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt @@ -22,16 +22,16 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.idea.core.refactoring.chooseContainerElementIfNecessary import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.application.executeCommand -import org.jetbrains.kotlin.psi.JetClassBody -import org.jetbrains.kotlin.psi.JetClassOrObject -import org.jetbrains.kotlin.psi.JetElement -import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import java.util.HashSet @@ -61,9 +61,18 @@ public abstract class CreateCallableFromUsageFixBase( } } + private fun getDeclaration(descriptor: ClassifierDescriptor, project: Project): PsiElement? { + if (descriptor is FunctionClassDescriptor) { + val psiFactory = JetPsiFactory(project) + val syntheticClass = psiFactory.createClass(IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(descriptor)) + return psiFactory.createAnalyzableFile("${descriptor.name.asString()}.kt", "", element).add(syntheticClass) + } + return DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) + } + private fun getDeclarationIfApplicable(project: Project, candidate: TypeCandidate): PsiElement? { val descriptor = candidate.theType.constructor.declarationDescriptor ?: return null - val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null + val declaration = getDeclaration(descriptor, project) ?: return null if (declaration !is JetClassOrObject && declaration !is PsiClass) return null return if (isExtension || declaration.canRefactor()) declaration else null } diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt new file mode 100644 index 00000000000..976b9105311 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt @@ -0,0 +1,5 @@ +// "Create extension function 'bar'" "true" + +fun foo(block: (Int) -> String) { + block.bar() +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt.after new file mode 100644 index 00000000000..b693899465c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt.after @@ -0,0 +1,9 @@ +// "Create extension function 'bar'" "true" + +fun foo(block: (Int) -> String) { + block.bar() +} + +fun ((P1) -> R).bar() { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index ddd8065a779..cd041efe193 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -1790,6 +1790,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("functionalTypeInReceiver.kt") + public void testFunctionalTypeInReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/functionalTypeInReceiver.kt"); + doTest(fileName); + } + @TestMetadata("inLambda.kt") public void testInLambda() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/inLambda.kt");