From 6043c6d0a782d6f0242bcda9351a35173e7160d0 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 24 Sep 2014 18:16:29 +0400 Subject: [PATCH] Create Function From Usage: Quick-fix for functions invoked via call expressions --- .../plugin/quickfix/QuickFixRegistrar.java | 5 + .../CreateFunctionFromCallActionFactory.kt | 52 +++++++ .../CreateFunctionFromUsageFix.kt | 80 +++------- .../createFunction/FunctionInfo.kt | 6 +- .../createFunction/functionBuilder.kt | 115 +++++++++------ .../autoImports/packageClass.before.Main.kt | 1 + .../call/afterCallWithLambdaArg.kt | 12 ++ .../call/afterCallWithLambdaArgOnly.kt | 12 ++ .../createFromUsage/call/afterFunExtraArgs.kt | 12 ++ .../call/afterFunMissingArgs.kt | 12 ++ .../call/afterFunOnClassObject.kt | 14 ++ .../call/afterFunOnLibObject.kt | 8 + .../createFromUsage/call/afterFunOnLibType.kt | 10 ++ .../call/afterFunOnUserObject.kt | 11 ++ .../call/afterFunOnUserType.kt | 11 ++ .../call/afterFunOnUserTypeWithTypeParams.kt | 11 ++ ...fterFunWithExplicitParamNamesOnUserType.kt | 11 ++ .../call/afterInconsistentTypes.kt | 12 ++ .../call/afterLocalFunNoReceiver.kt | 10 ++ .../call/afterMemberFunNoReceiver.kt | 12 ++ .../call/afterObjectMemberFunNoReceiver.kt | 12 ++ .../createFromUsage/call/afterThisInClass.kt | 10 ++ .../call/afterThisInExtension.kt | 11 ++ .../call/afterThisInNestedClass1.kt | 12 ++ .../call/afterThisInNestedClass2.kt | 12 ++ .../call/afterTopLevelFunNoReceiver.kt | 8 + .../createFromUsage/call/afterUnknownType.kt | 12 ++ .../call/beforeCallWithLambdaArg.kt | 9 ++ .../call/beforeCallWithLambdaArgOnly.kt | 9 ++ .../call/beforeFunExtraArgs.kt | 9 ++ .../call/beforeFunMissingArgs.kt | 9 ++ .../call/beforeFunOnClassObject.kt | 11 ++ .../call/beforeFunOnLibObject.kt | 5 + .../call/beforeFunOnLibType.kt | 7 + .../call/beforeFunOnUserObject.kt | 7 + .../call/beforeFunOnUserType.kt | 7 + .../call/beforeFunOnUserTypeWithTypeParams.kt | 7 + ...foreFunWithExplicitParamNamesOnUserType.kt | 7 + .../call/beforeInconsistentTypes.kt | 8 + .../call/beforeLocalFunNoReceiver.kt | 7 + .../call/beforeMemberFunNoReceiver.kt | 9 ++ .../call/beforeObjectMemberFunNoReceiver.kt | 9 ++ .../createFromUsage/call/beforeThisInClass.kt | 7 + .../call/beforeThisInExtension.kt | 7 + .../call/beforeThisInNestedClass1.kt | 9 ++ .../call/beforeThisInNestedClass2.kt | 9 ++ .../call/beforeTopLevelFunNoReceiver.kt | 5 + .../createFromUsage/call/beforeUnknownType.kt | 8 + .../quickfix/QuickFixTestGenerated.java | 138 +++++++++++++++++- 49 files changed, 694 insertions(+), 103 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromCallActionFactory.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArg.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArgOnly.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunExtraArgs.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunMissingArgs.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunOnClassObject.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunOnLibObject.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunOnLibType.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunOnUserObject.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunOnUserType.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunOnUserTypeWithTypeParams.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterFunWithExplicitParamNamesOnUserType.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterInconsistentTypes.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterLocalFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterMemberFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterObjectMemberFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterThisInClass.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterThisInExtension.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass1.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass2.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterTopLevelFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/afterUnknownType.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArg.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArgOnly.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunExtraArgs.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunMissingArgs.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunOnClassObject.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunOnLibObject.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunOnLibType.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunOnUserObject.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunOnUserType.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunOnUserTypeWithTypeParams.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeFunWithExplicitParamNamesOnUserType.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeInconsistentTypes.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeLocalFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeMemberFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeObjectMemberFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeThisInClass.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeThisInExtension.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass1.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass2.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeTopLevelFunNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/call/beforeUnknownType.kt diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixRegistrar.java index 23dcaeb929d..8a6dc68ada9 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixRegistrar.java @@ -232,6 +232,11 @@ public class QuickFixRegistrar { QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateBinaryOperationActionFactory.INSTANCE$); QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateBinaryOperationActionFactory.INSTANCE$); + QuickFixes.factories.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, CreateFunctionFromCallActionFactory.INSTANCE$); + QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateFunctionFromCallActionFactory.INSTANCE$); + QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateFunctionFromCallActionFactory.INSTANCE$); + QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateFunctionFromCallActionFactory.INSTANCE$); + QuickFixes.factories.put(FUNCTION_EXPECTED, CreateInvokeFunctionActionFactory.INSTANCE$); QuickFixes.factories.put(TYPE_MISMATCH, new QuickFixFactoryForTypeMismatchError()); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromCallActionFactory.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromCallActionFactory.kt new file mode 100644 index 00000000000..b4e3fa07bf5 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromCallActionFactory.kt @@ -0,0 +1,52 @@ +package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction + +import org.jetbrains.jet.plugin.quickfix.JetSingleIntentionActionFactory +import org.jetbrains.jet.lang.diagnostics.Diagnostic +import com.intellij.codeInsight.intention.IntentionAction +import org.jetbrains.jet.lang.psi.JetCallExpression +import org.jetbrains.jet.lang.types.Variance +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.jet.plugin.quickfix.QuickFixUtil +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression +import org.jetbrains.jet.lexer.JetTokens +import org.jetbrains.jet.lang.psi.JetQualifiedExpression +import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache +import org.jetbrains.jet.lang.resolve.calls.callUtil.getCall +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.jet.lang.resolve.scopes.receivers.Qualifier + +object CreateFunctionFromCallActionFactory : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val callExpr = QuickFixUtil.getParentElementOfType(diagnostic, javaClass()) ?: return null + + val calleeExpr = callExpr.getCalleeExpression() as? JetSimpleNameExpression ?: return null + if (calleeExpr.getReferencedNameElementType() != JetTokens.IDENTIFIER) return null + + val callParent = callExpr.getParent() + val fullCallExpr = + if (callParent is JetQualifiedExpression && callParent.getSelectorExpression() == callExpr) callParent else callExpr + + val context = AnalyzerFacadeWithCache.getContextForElement(callExpr) + val receiver = callExpr.getCall(context)?.getExplicitReceiver() ?: return null + + val receiverType = when (receiver) { + ReceiverValue.NO_RECEIVER -> TypeInfo.Empty + is Qualifier -> when { + receiver.classifier != null -> TypeInfo(receiver.expression, Variance.IN_VARIANCE) + else -> return null + } + else -> TypeInfo(receiver.getType(), Variance.IN_VARIANCE) + } + + val anyType = KotlinBuiltIns.getInstance().getNullableAnyType() + val parameters = callExpr.getValueArguments().map { + ParameterInfo( + it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE), + it.getArgumentName()?.getReferenceExpression()?.getReferencedName() + ) + } + + val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE) + return CreateFunctionFromUsageFix(callExpr, FunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, parameters)) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt index 1a54bf1af89..c9eb394f448 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionFromUsageFix.kt @@ -2,50 +2,19 @@ package org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction import com.intellij.psi.PsiElement import org.jetbrains.jet.plugin.quickfix.createFromUsage.CreateFromUsageFixBase -import com.intellij.ide.util.PsiElementListCellRenderer -import org.jetbrains.jet.lang.psi.JetClass -import org.jetbrains.jet.plugin.presentation.JetClassPresenter -import javax.swing.JList -import java.awt.Component import org.jetbrains.jet.lang.psi.JetFile import com.intellij.openapi.editor.Editor import org.jetbrains.jet.plugin.JetBundle import com.intellij.openapi.project.Project -import com.intellij.openapi.application.ApplicationManager -import com.intellij.ui.components.JBList -import javax.swing.ListSelectionModel -import com.intellij.openapi.ui.popup.PopupChooserBuilder import com.intellij.openapi.command.CommandProcessor import org.jetbrains.jet.lang.resolve.DescriptorUtils import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil +import org.jetbrains.jet.lang.psi.JetClassOrObject +import org.jetbrains.jet.plugin.refactoring.chooseContainerElementIfNecessary +import org.jetbrains.jet.plugin.refactoring.getExtractionContainers +import org.jetbrains.jet.lang.psi.JetClassBody public class CreateFunctionFromUsageFix(element: PsiElement, val functionInfo: FunctionInfo) : CreateFromUsageFixBase(element) { - /** - * Represents an element in the class selection list. - */ - private class ClassCandidate(val typeCandidate: TypeCandidate, file: JetFile) { - val jetClass: JetClass = DescriptorToDeclarationUtil.getDeclaration( - file, DescriptorUtils.getClassDescriptorForType(typeCandidate.theType) - ) as JetClass - } - - private class ClassCandidateListCellRenderer : PsiElementListCellRenderer() { - private val presenter = JetClassPresenter() - - override fun getElementText(element: JetClass): String? = - presenter.getPresentation(element)?.getPresentableText() - - protected override fun getContainerText(element: JetClass?, name: String?): String? = - element?.let { presenter.getPresentation(it) }?.getLocationString() - - override fun getIconFlags(): Int = 0 - - public override fun getListCellRendererComponent( - list: JList, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean - ): Component? = - super.getListCellRendererComponent(list, (value as ClassCandidate).jetClass, index, isSelected, cellHasFocus) - } - override fun getText(): String { return JetBundle.message("create.function.from.usage", functionInfo.name) } @@ -53,32 +22,29 @@ public class CreateFunctionFromUsageFix(element: PsiElement, val functionInfo: F override fun invoke(project: Project, editor: Editor?, file: JetFile?) { val functionBuilder = FunctionBuilderConfiguration(functionInfo, file!!, editor!!).createBuilder() - val ownerTypeCandidates = functionBuilder.computeTypeCandidates(functionInfo.receiverTypeInfo) - assert(!ownerTypeCandidates.empty) + fun runBuilder(placement: FunctionPlacement) { + functionBuilder.placement = placement + CommandProcessor.getInstance().executeCommand(project, { functionBuilder.build() }, getText(), null) + } - if (ownerTypeCandidates.size == 1 || ApplicationManager.getApplication()!!.isUnitTestMode()) { - functionBuilder.receiverTypeCandidate = ownerTypeCandidates.first!! - functionBuilder.build() + val popupTitle = JetBundle.message("choose.target.class.or.trait.title") + val receiverTypeCandidates = functionBuilder.computeTypeCandidates(functionInfo.receiverTypeInfo) + if (receiverTypeCandidates.isNotEmpty()) { + val toPsi: (TypeCandidate) -> JetClassOrObject = { + val descriptor = DescriptorUtils.getClassDescriptorForType(it.theType) + DescriptorToDeclarationUtil.getDeclaration(file, descriptor) as JetClassOrObject + } + chooseContainerElementIfNecessary(receiverTypeCandidates, editor, popupTitle, false, toPsi) { + runBuilder(FunctionPlacement.WithReceiver(it)) + } } else { - // class selection - val list = JBList(ownerTypeCandidates.map { ClassCandidate(it, file) }) - val renderer = ClassCandidateListCellRenderer() - list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) - list.setCellRenderer(renderer) - val builder = PopupChooserBuilder(list) - renderer.installSpeedSearch(builder) + assert(functionInfo.receiverTypeInfo is TypeInfo.Empty, "No receiver type candidates: ${element.getText()} in ${file.getText()}") - builder.setTitle(JetBundle.message("choose.target.class.or.trait.title")) - .setItemChoosenCallback { - val selectedCandidate = list.getSelectedValue() as ClassCandidate? - if (selectedCandidate != null) { - functionBuilder.receiverTypeCandidate = selectedCandidate.typeCandidate - CommandProcessor.getInstance().executeCommand(project, { functionBuilder.build() }, getText(), null) - } - } - .createPopup() - .showInBestPositionFor(editor) + chooseContainerElementIfNecessary(element.getExtractionContainers(), editor, popupTitle, true, { it }) { + val container = if (it is JetClassBody) it.getParent() as JetClassOrObject else it + runBuilder(FunctionPlacement.NoReceiver(container)) + } } } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/FunctionInfo.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/FunctionInfo.kt index 940ed44d1cc..147396f1a5a 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/FunctionInfo.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/FunctionInfo.kt @@ -15,6 +15,10 @@ import org.jetbrains.jet.plugin.util.supertypes * Represents a concrete type or a set of types yet to be inferred from an expression. */ abstract class TypeInfo(val variance: Variance) { + object Empty: TypeInfo(Variance.INVARIANT) { + override fun getPossibleTypes(builder: FunctionBuilder): List = Collections.emptyList() + } + class ByExpression(val expression: JetExpression, variance: Variance): TypeInfo(variance) { override val possibleNamesFromExpression: Array by Delegates.lazy { JetNameSuggester.suggestNamesForExpression(expression, EmptyValidator) @@ -31,7 +35,7 @@ abstract class TypeInfo(val variance: Variance) { class ByReceiverType(variance: Variance): TypeInfo(variance) { override fun getPossibleTypes(builder: FunctionBuilder): List = - builder.receiverTypeCandidate.theType.getPossibleSupertypes(variance) + (builder.placement as FunctionPlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance) } open val possibleNamesFromExpression: Array get() = ArrayUtil.EMPTY_STRING_ARRAY diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/functionBuilder.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/functionBuilder.kt index 208bb17a38b..2ad48e2c8d9 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/functionBuilder.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/functionBuilder.kt @@ -52,6 +52,7 @@ import org.jetbrains.jet.plugin.refactoring.EmptyValidator import org.jetbrains.jet.plugin.refactoring.CollectingValidator import org.jetbrains.jet.plugin.util.isUnit import org.jetbrains.jet.plugin.refactoring.runWriteAction +import com.intellij.util.ArrayUtil private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList" private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt" @@ -90,14 +91,17 @@ class FunctionBuilderConfiguration( val currentEditor: Editor ) +trait FunctionPlacement { + class WithReceiver(val receiverTypeCandidate: TypeCandidate): FunctionPlacement + class NoReceiver(val containingElement: JetElement): FunctionPlacement +} + class FunctionBuilder(val config: FunctionBuilderConfiguration) { private var finished: Boolean = false val currentFileContext: BindingContext val currentFileModule: ModuleDescriptor - public var receiverTypeCandidate: TypeCandidate by Delegates.notNull() - private val typeCandidates = HashMap>(); { @@ -106,6 +110,8 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { currentFileModule = exhaust.getModuleDescriptor() } + public var placement: FunctionPlacement by Delegates.notNull() + fun computeTypeCandidates(typeInfo: TypeInfo): List = typeCandidates.getOrPut(typeInfo) { typeInfo.getPossibleTypes(this).map { TypeCandidate(it) } } @@ -136,44 +142,48 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { } } - private inner class Context { + private inner class Context() { val isUnit: Boolean val isExtension: Boolean val containingFile: JetFile val containingFileEditor: Editor - val receiverClass: JetClass? - val receiverClassDescriptor: ClassDescriptor + val containingElement: JetElement + val receiverClassDescriptor: ClassDescriptor? val typeParameterNameMap: Map + val receiverTypeCandidate: TypeCandidate? { // gather relevant information - receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(receiverTypeCandidate.theType) - val receiverType = receiverClassDescriptor.getDefaultType() - val classDeclaration = DescriptorToSourceUtils.classDescriptorToDeclaration(receiverClassDescriptor) - if (classDeclaration is JetClass) { - receiverClass = classDeclaration - isExtension = !classDeclaration.isWritable() + val placement = placement + when { + placement is FunctionPlacement.NoReceiver -> { + receiverClassDescriptor = null + isExtension = false + containingElement = placement.containingElement + } + placement is FunctionPlacement.WithReceiver -> { + receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(placement.receiverTypeCandidate.theType) + val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.classDescriptorToDeclaration(it) } + isExtension = !(classDeclaration is JetClassOrObject && classDeclaration.isWritable()) + containingElement = if (isExtension) config.currentFile else classDeclaration as JetElement + } + else -> throw IllegalArgumentException("Unexpected function kind: $placement") } - else { - receiverClass = null - isExtension = true + val receiverType = receiverClassDescriptor?.getDefaultType() - } - - if (isExtension) { - containingFile = config.currentFile - containingFileEditor = config.currentEditor - } - else { - containingFile = receiverClass!!.getContainingJetFile() - NavigationUtil.activateFileWithPsiElement(containingFile) + containingFile = containingElement.getContainingJetFile() + if (containingFile != config.currentFile) { + NavigationUtil.activateFileWithPsiElement(containingElement) containingFileEditor = FileEditorManager.getInstance(config.currentFile.getProject())!!.getSelectedTextEditor()!! } + else { + containingFileEditor = config.currentEditor + } isUnit = config.functionInfo.returnTypeInfo.let { it is TypeInfo.ByType && it.theType.isUnit() } - val scope = if (isExtension) { + val scope = if (isExtension || receiverClassDescriptor == null) { currentFileModule.getPackage(config.currentFile.getPackageFqName())!!.getMemberScope() } else { @@ -181,8 +191,9 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { } // figure out type substitutions for type parameters - val classTypeParameters = receiverType.getArguments() - val ownerTypeArguments = receiverTypeCandidate.theType.getArguments() + val classTypeParameters = receiverType?.getArguments() ?: Collections.emptyList() + val ownerTypeArguments = (placement as? FunctionPlacement.WithReceiver)?.receiverTypeCandidate?.theType?.getArguments() + ?: Collections.emptyList() assert(ownerTypeArguments.size == classTypeParameters.size) val substitutions = ownerTypeArguments.zip(classTypeParameters).map { JetTypeSubstitution(it.first.getType(), it.second.getType()) @@ -193,7 +204,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { } // now that we have done substitutions, we can throw it away - receiverTypeCandidate = TypeCandidate(receiverType, scope) + receiverTypeCandidate = receiverType?.let { TypeCandidate(it, scope) } // figure out type parameter renames to avoid conflicts typeParameterNameMap = getTypeParameterRenames(scope) @@ -201,7 +212,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { if (!isUnit) { renderTypeCandidates(config.functionInfo.returnTypeInfo, typeParameterNameMap) } - receiverTypeCandidate.render(typeParameterNameMap) + receiverTypeCandidate?.render(typeParameterNameMap) } private fun renderTypeCandidates( @@ -218,25 +229,39 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { val psiFactory = JetPsiFactory(currentFile) if (isExtension) { // create as extension function - val ownerTypeString = receiverTypeCandidate.renderedType!! + val ownerTypeString = receiverTypeCandidate!!.renderedType!! val func = psiFactory.createFunction( "fun $ownerTypeString.${functionInfo.name}($parametersString)$returnTypeString { }" ) return currentFile.add(func) as JetNamedFunction } else { - receiverClass!! - // create as regular function + val func = psiFactory.createFunction("fun ${functionInfo.name}($parametersString)$returnTypeString { }") - var classBody = receiverClass.getBody() - if (classBody == null) { - classBody = receiverClass.add(psiFactory.createEmptyClassBody()) as JetClassBody - receiverClass.addBefore(psiFactory.createWhiteSpace(), classBody) + when (containingElement) { + is JetFile -> { + return currentFile.add(func) as JetNamedFunction + } + + is JetClassOrObject -> { + var classBody = containingElement.getBody() + if (classBody == null) { + classBody = containingElement.add(psiFactory.createEmptyClassBody()) as JetClassBody + containingElement.addBefore(psiFactory.createWhiteSpace(), classBody) + } + val rBrace = classBody!!.getRBrace() + + //TODO: Assert rbrace not null? It can be if the class isn't closed. + return classBody!!.addBefore(func, rBrace) as JetNamedFunction + } + + is JetBlockExpression -> { + return containingElement.addAfter(func, containingElement.getLBrace()) as JetNamedFunction + } + + else -> throw AssertionError("Invalid containing element: ${containingElement.getText()}") } - val rBrace = classBody!!.getRBrace() - //TODO: Assert rbrace not null? It can be if the class isn't closed. - return classBody!!.addBefore(func, rBrace) as JetNamedFunction } } } @@ -244,7 +269,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { private fun getTypeParameterRenames(scope: JetScope): Map { val allTypeParametersNotInScope = LinkedHashSet() - allTypeParametersNotInScope.addAll(receiverTypeCandidate.typeParameters.toList()) + allTypeParametersNotInScope.addAll(receiverTypeCandidate?.typeParameters?.toList() ?: Collections.emptyList()) config.functionInfo.parameterInfos.stream() .flatMap { typeCandidates[it.typeInfo]!!.stream() } @@ -266,7 +291,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { typeRefsToShorten: MutableList, parameterTypeExpressions: List, returnTypeExpression: TypeExpression?) { if (isExtension) { - val receiverTypeRef = JetPsiFactory(func).createType(receiverTypeCandidate.theType.renderLong(typeParameterNameMap)) + val receiverTypeRef = JetPsiFactory(func).createType(receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap)) replaceWithLongerName(receiverTypeRef, receiverTypeCandidate.theType) val funcReceiverTypeRef = func.getReceiverTypeRef() @@ -310,8 +335,10 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { val fileTemplate = FileTemplateManager.getInstance()!!.getCodeTemplate(TEMPLATE_FROM_USAGE_FUNCTION_BODY) val properties = Properties() properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, if (isUnit) "Unit" else func.getReturnTypeRef()!!.getText()) - properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(receiverClassDescriptor).asString()) - properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, receiverClassDescriptor.getName().asString()) + receiverClassDescriptor?.let { + properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(it).asString()) + properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, it.getName().asString()) + } properties.setProperty(ATTRIBUTE_FUNCTION_NAME, config.functionInfo.name) val bodyText = try { @@ -339,7 +366,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, func: JetNamedFunction): TypeParameterListExpression { val typeParameterMap = HashMap>() - val receiverTypeParameterNames = receiverTypeCandidate.typeParameterNames + val receiverTypeParameterNames = receiverTypeCandidate?.let { it.typeParameterNames!! } ?: ArrayUtil.EMPTY_STRING_ARRAY config.functionInfo.parameterInfos.stream().flatMap { typeCandidates[it.typeInfo]!!.stream() }.forEach { typeParameterMap[it.renderedType!!] = it.typeParameterNames!! @@ -352,7 +379,7 @@ class FunctionBuilder(val config: FunctionBuilderConfiguration) { } // ((3, 3) is after "fun") builder.replaceElement(func, TextRange.create(3, 3), TYPE_PARAMETER_LIST_VARIABLE_NAME, null, false) - return TypeParameterListExpression(receiverTypeParameterNames!!, typeParameterMap) + return TypeParameterListExpression(receiverTypeParameterNames, typeParameterMap) } private fun setupParameterTypeTemplates(builder: TemplateBuilder, parameterList: JetParameterList): List { diff --git a/idea/testData/quickfix/autoImports/packageClass.before.Main.kt b/idea/testData/quickfix/autoImports/packageClass.before.Main.kt index 73a11e81935..82781df58ec 100644 --- a/idea/testData/quickfix/autoImports/packageClass.before.Main.kt +++ b/idea/testData/quickfix/autoImports/packageClass.before.Main.kt @@ -1,4 +1,5 @@ // "class com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFixBase" "false" +// ACTION: Create function 'FooPackage' from usage // ERROR: Unresolved reference: FooPackage package packageClass diff --git a/idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArg.kt b/idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArg.kt new file mode 100644 index 00000000000..056fe0b071e --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArg.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + + fun foo(arg: T, s: String, function: Function1): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test() { + val a: A = A(1).foo(2, "2") { (p: Int) -> p + 1 } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArgOnly.kt b/idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArgOnly.kt new file mode 100644 index 00000000000..96a0126f058 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterCallWithLambdaArgOnly.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + + fun foo(function: Function1): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test() { + val a: A = A(1).foo { (p: Int) -> p + 1 } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunExtraArgs.kt b/idea/testData/quickfix/createFromUsage/call/afterFunExtraArgs.kt new file mode 100644 index 00000000000..2a58044d97b --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunExtraArgs.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(a: Int): A = throw Exception() + fun foo(arg: T, s: String): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test() { + val a: A = A(1).foo(2, "2") +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunMissingArgs.kt b/idea/testData/quickfix/createFromUsage/call/afterFunMissingArgs.kt new file mode 100644 index 00000000000..98794fbb55d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunMissingArgs.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(i: Int, s: String): A = throw Exception() + fun foo(arg: T): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test() { + val a: A = A(1).foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunOnClassObject.kt b/idea/testData/quickfix/createFromUsage/call/afterFunOnClassObject.kt new file mode 100644 index 00000000000..57402cb6b9d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunOnClassObject.kt @@ -0,0 +1,14 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + class object { + + fun foo(i: Int): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + } +} + +fun test() { + val a: Int = A.foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunOnLibObject.kt b/idea/testData/quickfix/createFromUsage/call/afterFunOnLibObject.kt new file mode 100644 index 00000000000..1a83ac84c42 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunOnLibObject.kt @@ -0,0 +1,8 @@ +// "Create function 'foo' from usage" "true" + +fun test() { + val a: Int = Unit.foo(2) +} +fun Unit.foo(i: Int): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunOnLibType.kt b/idea/testData/quickfix/createFromUsage/call/afterFunOnLibType.kt new file mode 100644 index 00000000000..524ede7151e --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunOnLibType.kt @@ -0,0 +1,10 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) + +fun test() { + val a: A = 2.foo(A(1)) +} +fun Int.foo(A: A): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunOnUserObject.kt b/idea/testData/quickfix/createFromUsage/call/afterFunOnUserObject.kt new file mode 100644 index 00000000000..8b76dd99af3 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunOnUserObject.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +object A { + fun foo(i: Int): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test() { + val a: Int = A.foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunOnUserType.kt b/idea/testData/quickfix/createFromUsage/call/afterFunOnUserType.kt new file mode 100644 index 00000000000..02ffd51bc2d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunOnUserType.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(arg: T): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test() { + val a: A = A(1).foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunOnUserTypeWithTypeParams.kt b/idea/testData/quickfix/createFromUsage/call/afterFunOnUserTypeWithTypeParams.kt new file mode 100644 index 00000000000..3dbb24b54c2 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunOnUserTypeWithTypeParams.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(u: T): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test(u: U) { + val a: A = A(u).foo(u) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterFunWithExplicitParamNamesOnUserType.kt b/idea/testData/quickfix/createFromUsage/call/afterFunWithExplicitParamNamesOnUserType.kt new file mode 100644 index 00000000000..48220408f91 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterFunWithExplicitParamNamesOnUserType.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(abc: T, ghi: A, def: String): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test() { + val a: A = A(1).foo(abc = 1, ghi = A(2), def = "s") +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterInconsistentTypes.kt b/idea/testData/quickfix/createFromUsage/call/afterInconsistentTypes.kt new file mode 100644 index 00000000000..c1ac7b86acf --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterInconsistentTypes.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" +// ERROR: Type mismatch.
Required:kotlin.Int
Found:A<kotlin.Int>
+ +class A(val n: T) { + fun foo(s: String, arg: T): Any { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test(): Int { + return A(1).foo("s", 1) as A +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterLocalFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/afterLocalFunNoReceiver.kt new file mode 100644 index 00000000000..f85ad47fb0c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterLocalFunNoReceiver.kt @@ -0,0 +1,10 @@ +// "Create function 'foo' from usage" "true" + +fun test() { + fun foo(i: Int, s: String): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + fun nestedTest(): Int { + return foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterMemberFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/afterMemberFunNoReceiver.kt new file mode 100644 index 00000000000..1a5e7a1ff09 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterMemberFunNoReceiver.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A { + class B { + fun test(): Int { + return foo(2, "2") + } + fun foo(i: Int, s: String): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterObjectMemberFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/afterObjectMemberFunNoReceiver.kt new file mode 100644 index 00000000000..ba21868622d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterObjectMemberFunNoReceiver.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A { + object B { + fun test(): Int { + return foo(2, "2") + } + fun foo(i: Int, s: String): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterThisInClass.kt b/idea/testData/quickfix/createFromUsage/call/afterThisInClass.kt new file mode 100644 index 00000000000..1b125a55d90 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterThisInClass.kt @@ -0,0 +1,10 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun test(): A { + return this.foo(2, "2") + } + fun foo(i: Int, s: String): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterThisInExtension.kt b/idea/testData/quickfix/createFromUsage/call/afterThisInExtension.kt new file mode 100644 index 00000000000..0331592dc26 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterThisInExtension.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(i: Int, s: String): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun A.test(): A { + return this.foo(2, "2") +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass1.kt b/idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass1.kt new file mode 100644 index 00000000000..1aaaaff6af4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass1.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + inner class B(val m: U) { + fun test(): A { + return this.foo(2, "2") + } + fun foo(i: Int, s: String): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass2.kt b/idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass2.kt new file mode 100644 index 00000000000..9811ea97b73 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterThisInNestedClass2.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + inner class B(val m: U) { + fun test(): A { + return this@A.foo(2, "2") + } + } + fun foo(i: Int, s: String): A { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterTopLevelFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/afterTopLevelFunNoReceiver.kt new file mode 100644 index 00000000000..4f706dc17a7 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterTopLevelFunNoReceiver.kt @@ -0,0 +1,8 @@ +// "Create function 'foo' from usage" "true" + +fun test(): Int { + return foo(2, "2") +} +fun foo(i: Int, s: String): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/afterUnknownType.kt b/idea/testData/quickfix/createFromUsage/call/afterUnknownType.kt new file mode 100644 index 00000000000..afc1427d061 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/afterUnknownType.kt @@ -0,0 +1,12 @@ +// "Create function 'foo' from usage" "true" +// ERROR: Unresolved reference: s + +class A(val n: T) { + fun foo(s: Any, arg: T): T { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun test(): Int { + return A(1).foo(s, 1) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArg.kt b/idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArg.kt new file mode 100644 index 00000000000..23dc14489ec --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArg.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + +} + +fun test() { + val a: A = A(1).foo(2, "2") { (p: Int) -> p + 1 } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArgOnly.kt b/idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArgOnly.kt new file mode 100644 index 00000000000..bc7aacb3a9a --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArgOnly.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + +} + +fun test() { + val a: A = A(1).foo { (p: Int) -> p + 1 } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunExtraArgs.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunExtraArgs.kt new file mode 100644 index 00000000000..d4ee20f9c3d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunExtraArgs.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(a: Int): A = throw Exception() +} + +fun test() { + val a: A = A(1).foo(2, "2") +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunMissingArgs.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunMissingArgs.kt new file mode 100644 index 00000000000..82fad410ec4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunMissingArgs.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun foo(i: Int, s: String): A = throw Exception() +} + +fun test() { + val a: A = A(1).foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunOnClassObject.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunOnClassObject.kt new file mode 100644 index 00000000000..e9864e00e38 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunOnClassObject.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + class object { + + } +} + +fun test() { + val a: Int = A.foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunOnLibObject.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunOnLibObject.kt new file mode 100644 index 00000000000..b26db2e4ec7 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunOnLibObject.kt @@ -0,0 +1,5 @@ +// "Create function 'foo' from usage" "true" + +fun test() { + val a: Int = Unit.foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunOnLibType.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunOnLibType.kt new file mode 100644 index 00000000000..b42d7859c10 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunOnLibType.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) + +fun test() { + val a: A = 2.foo(A(1)) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserObject.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserObject.kt new file mode 100644 index 00000000000..32dbe17c166 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserObject.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +object A + +fun test() { + val a: Int = A.foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserType.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserType.kt new file mode 100644 index 00000000000..dffd2a90baf --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserType.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) + +fun test() { + val a: A = A(1).foo(2) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserTypeWithTypeParams.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserTypeWithTypeParams.kt new file mode 100644 index 00000000000..250057cddcf --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunOnUserTypeWithTypeParams.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) + +fun test(u: U) { + val a: A = A(u).foo(u) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeFunWithExplicitParamNamesOnUserType.kt b/idea/testData/quickfix/createFromUsage/call/beforeFunWithExplicitParamNamesOnUserType.kt new file mode 100644 index 00000000000..9357ff729cd --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeFunWithExplicitParamNamesOnUserType.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) + +fun test() { + val a: A = A(1).foo(abc = 1, ghi = A(2), def = "s") +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeInconsistentTypes.kt b/idea/testData/quickfix/createFromUsage/call/beforeInconsistentTypes.kt new file mode 100644 index 00000000000..5d1d407d2cb --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeInconsistentTypes.kt @@ -0,0 +1,8 @@ +// "Create function 'foo' from usage" "true" +// ERROR: Type mismatch.
Required:kotlin.Int
Found:A<kotlin.Int>
+ +class A(val n: T) + +fun test(): Int { + return A(1).foo("s", 1) as A +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeLocalFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/beforeLocalFunNoReceiver.kt new file mode 100644 index 00000000000..22e6c14b9cd --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeLocalFunNoReceiver.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +fun test() { + fun nestedTest(): Int { + return foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeMemberFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/beforeMemberFunNoReceiver.kt new file mode 100644 index 00000000000..ad366e7568f --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeMemberFunNoReceiver.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A { + class B { + fun test(): Int { + return foo(2, "2") + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeObjectMemberFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/beforeObjectMemberFunNoReceiver.kt new file mode 100644 index 00000000000..542c2ce8b34 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeObjectMemberFunNoReceiver.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A { + object B { + fun test(): Int { + return foo(2, "2") + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeThisInClass.kt b/idea/testData/quickfix/createFromUsage/call/beforeThisInClass.kt new file mode 100644 index 00000000000..3cd5c0b6e92 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeThisInClass.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + fun test(): A { + return this.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeThisInExtension.kt b/idea/testData/quickfix/createFromUsage/call/beforeThisInExtension.kt new file mode 100644 index 00000000000..cf87b8f0201 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeThisInExtension.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) + +fun A.test(): A { + return this.foo(2, "2") +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass1.kt b/idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass1.kt new file mode 100644 index 00000000000..9beeb01eb36 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass1.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + inner class B(val m: U) { + fun test(): A { + return this.foo(2, "2") + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass2.kt b/idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass2.kt new file mode 100644 index 00000000000..dccef68a1df --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass2.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +class A(val n: T) { + inner class B(val m: U) { + fun test(): A { + return this@A.foo(2, "2") + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeTopLevelFunNoReceiver.kt b/idea/testData/quickfix/createFromUsage/call/beforeTopLevelFunNoReceiver.kt new file mode 100644 index 00000000000..161cf0992de --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeTopLevelFunNoReceiver.kt @@ -0,0 +1,5 @@ +// "Create function 'foo' from usage" "true" + +fun test(): Int { + return foo(2, "2") +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/call/beforeUnknownType.kt b/idea/testData/quickfix/createFromUsage/call/beforeUnknownType.kt new file mode 100644 index 00000000000..4d3a2b92720 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/call/beforeUnknownType.kt @@ -0,0 +1,8 @@ +// "Create function 'foo' from usage" "true" +// ERROR: Unresolved reference: s + +class A(val n: T) + +fun test(): Int { + return A(1).foo(s, 1) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java index 2ecbb640dae..1746388b369 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java @@ -618,7 +618,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { @TestMetadata("idea/testData/quickfix/createFromUsage") @TestDataPath("$PROJECT_ROOT") - @InnerTestClasses({CreateFromUsage.BinaryOperations.class, CreateFromUsage.Component.class, CreateFromUsage.Get.class, CreateFromUsage.HasNext.class, CreateFromUsage.Invoke.class, CreateFromUsage.Iterator.class, CreateFromUsage.Next.class, CreateFromUsage.Set.class, CreateFromUsage.UnaryOperations.class}) + @InnerTestClasses({CreateFromUsage.BinaryOperations.class, CreateFromUsage.Call.class, CreateFromUsage.Component.class, CreateFromUsage.Get.class, CreateFromUsage.HasNext.class, CreateFromUsage.Invoke.class, CreateFromUsage.Iterator.class, CreateFromUsage.Next.class, CreateFromUsage.Set.class, CreateFromUsage.UnaryOperations.class}) @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) public static class CreateFromUsage extends AbstractQuickFixTest { public void testAllFilesPresentInCreateFromUsage() throws Exception { @@ -719,6 +719,142 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } + @TestMetadata("idea/testData/quickfix/createFromUsage/call") + @TestDataPath("$PROJECT_ROOT") + @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class) + public static class Call extends AbstractQuickFixTest { + public void testAllFilesPresentInCall() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/call"), Pattern.compile("^before(\\w+)\\.kt$"), true); + } + + @TestMetadata("beforeCallWithLambdaArg.kt") + public void testCallWithLambdaArg() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArg.kt"); + doTest(fileName); + } + + @TestMetadata("beforeCallWithLambdaArgOnly.kt") + public void testCallWithLambdaArgOnly() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeCallWithLambdaArgOnly.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunExtraArgs.kt") + public void testFunExtraArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunExtraArgs.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunMissingArgs.kt") + public void testFunMissingArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunMissingArgs.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunOnClassObject.kt") + public void testFunOnClassObject() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunOnClassObject.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunOnLibObject.kt") + public void testFunOnLibObject() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunOnLibObject.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunOnLibType.kt") + public void testFunOnLibType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunOnLibType.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunOnUserObject.kt") + public void testFunOnUserObject() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunOnUserObject.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunOnUserType.kt") + public void testFunOnUserType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunOnUserType.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunOnUserTypeWithTypeParams.kt") + public void testFunOnUserTypeWithTypeParams() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunOnUserTypeWithTypeParams.kt"); + doTest(fileName); + } + + @TestMetadata("beforeFunWithExplicitParamNamesOnUserType.kt") + public void testFunWithExplicitParamNamesOnUserType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeFunWithExplicitParamNamesOnUserType.kt"); + doTest(fileName); + } + + @TestMetadata("beforeInconsistentTypes.kt") + public void testInconsistentTypes() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeInconsistentTypes.kt"); + doTest(fileName); + } + + @TestMetadata("beforeLocalFunNoReceiver.kt") + public void testLocalFunNoReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeLocalFunNoReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("beforeMemberFunNoReceiver.kt") + public void testMemberFunNoReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeMemberFunNoReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("beforeObjectMemberFunNoReceiver.kt") + public void testObjectMemberFunNoReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeObjectMemberFunNoReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("beforeThisInClass.kt") + public void testThisInClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeThisInClass.kt"); + doTest(fileName); + } + + @TestMetadata("beforeThisInExtension.kt") + public void testThisInExtension() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeThisInExtension.kt"); + doTest(fileName); + } + + @TestMetadata("beforeThisInNestedClass1.kt") + public void testThisInNestedClass1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass1.kt"); + doTest(fileName); + } + + @TestMetadata("beforeThisInNestedClass2.kt") + public void testThisInNestedClass2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeThisInNestedClass2.kt"); + doTest(fileName); + } + + @TestMetadata("beforeTopLevelFunNoReceiver.kt") + public void testTopLevelFunNoReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeTopLevelFunNoReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("beforeUnknownType.kt") + public void testUnknownType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/call/beforeUnknownType.kt"); + doTest(fileName); + } + + } + @TestMetadata("idea/testData/quickfix/createFromUsage/component") @TestDataPath("$PROJECT_ROOT") @RunWith(org.jetbrains.jet.JUnit3RunnerWithInners.class)