From abbbd198fd2503351591ad9b4f04e62bb054c1a1 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 22 Oct 2014 20:32:21 +0400 Subject: [PATCH] Create From Usage: Use supplied type arguments to infer function type parameters --- .../callableBuilder/CallableBuilder.kt | 121 ++++++++++++++---- .../callableBuilder/CallableInfo.kt | 24 +++- .../callableBuilder/templateExpressions.kt | 24 +++- .../CreateCallableFromUsageFix.kt | 2 +- ...FunctionOrPropertyFromCallActionFactory.kt | 3 +- .../call/typeArguments/afterClassMember.kt | 14 ++ .../afterClassMemberPartialSubstitution.kt | 14 ++ .../afterClassMemberWithReceiverArg.kt | 14 ++ .../call/typeArguments/afterExtension.kt | 11 ++ .../afterExtensionPartialSubstitution.kt | 11 ++ .../afterExtensionWithReceiverArg.kt | 11 ++ .../call/typeArguments/afterNoReceiver.kt | 9 ++ .../typeArguments/afterNoReceiverExtraArgs.kt | 9 ++ .../typeArguments/afterNoReceiverLongName.kt | 9 ++ .../afterNoReceiverPartialSubstitution.kt | 9 ++ .../call/typeArguments/beforeClassMember.kt | 11 ++ .../beforeClassMemberPartialSubstitution.kt | 11 ++ .../beforeClassMemberWithReceiverArg.kt | 11 ++ .../call/typeArguments/beforeExtension.kt | 7 + .../beforeExtensionPartialSubstitution.kt | 7 + .../beforeExtensionWithReceiverArg.kt | 7 + .../call/typeArguments/beforeNoReceiver.kt | 5 + .../beforeNoReceiverExtraArgs.kt | 5 + .../typeArguments/beforeNoReceiverLongName.kt | 5 + .../beforeNoReceiverPartialSubstitution.kt | 5 + .../QuickFixMultiFileTestGenerated.java | 2 + .../quickfix/QuickFixTestGenerated.java | 70 ++++++++++ 27 files changed, 393 insertions(+), 38 deletions(-) create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMember.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberPartialSubstitution.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberWithReceiverArg.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtension.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtensionPartialSubstitution.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtensionWithReceiverArg.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverExtraArgs.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverLongName.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverPartialSubstitution.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMember.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberPartialSubstitution.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberWithReceiverArg.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtension.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionPartialSubstitution.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionWithReceiverArg.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiver.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverExtraArgs.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverLongName.kt create mode 100644 idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverPartialSubstitution.kt diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index f3dae2f19e7..1aeb53d6e2f 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -51,7 +51,6 @@ import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils import org.jetbrains.jet.plugin.refactoring.EmptyValidator import org.jetbrains.jet.plugin.refactoring.CollectingValidator import org.jetbrains.jet.plugin.util.isUnit -import com.intellij.util.ArrayUtil import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiElement import org.jetbrains.jet.lexer.JetTokens @@ -59,6 +58,13 @@ import org.jetbrains.jet.plugin.util.application.runWriteAction import org.jetbrains.jet.plugin.refactoring.isMultiLine import org.jetbrains.jet.lang.types.checker.JetTypeChecker import com.intellij.psi.SmartPointerManager +import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.jet.lang.descriptors.annotations.Annotations +import org.jetbrains.jet.lang.resolve.name.FqName +import org.jetbrains.jet.lang.descriptors.impl.MutablePackageFragmentDescriptor +import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl +import java.util.LinkedHashMap +import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList" private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt" @@ -71,12 +77,14 @@ class TypeCandidate(val theType: JetType, scope: JetScope? = null) { public val typeParameters: Array var renderedType: String? = null private set - var typeParameterNames: Array? = null + var renderedTypeParameters: List? = null private set - fun render(typeParameterNameMap: Map) { + fun render(typeParameterNameMap: Map, fakeFunction: FunctionDescriptor) { renderedType = theType.renderShort(typeParameterNameMap); - typeParameterNames = typeParameters.map { typeParameterNameMap[it]!! }.copyToArray() + renderedTypeParameters = typeParameters.map { + RenderedTypeParameter(it, it.getContainingDeclaration() == fakeFunction, typeParameterNameMap[it]!!) + } } { @@ -93,6 +101,12 @@ class TypeCandidate(val theType: JetType, scope: JetScope? = null) { override fun toString() = theType.toString() } +class RenderedTypeParameter( + val typeParameter: TypeParameterDescriptor, + val fake: Boolean, + val text: String +) + fun List.getTypeByRenderedType(renderedType: String): JetType? = firstOrNull { it.renderedType == renderedType }?.theType @@ -140,7 +154,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { fun computeTypeCandidates( typeInfo: TypeInfo, - substitutions: Array, + substitutions: List, scope: JetScope): List { if (typeInfo is TypeInfo.ByType && typeInfo.keepUnsubstituted) return computeTypeCandidates(typeInfo) return typeCandidates.getOrPut(typeInfo) { @@ -201,6 +215,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { val receiverClassDescriptor: ClassDescriptor? val typeParameterNameMap: Map val receiverTypeCandidate: TypeCandidate? + val substitutions: List { // gather relevant information @@ -213,7 +228,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { containingElement = placement.containingElement } placement is CallablePlacement.WithReceiver -> { - receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(placement.receiverTypeCandidate.theType) + receiverClassDescriptor = + placement.receiverTypeCandidate.theType.getConstructor().getDeclarationDescriptor() as? ClassDescriptor val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.classDescriptorToDeclaration(it) } isExtension = !(classDeclaration is JetClassOrObject && classDeclaration.isWritable()) containingElement = if (isExtension) config.currentFile else classDeclaration as JetElement @@ -239,13 +255,18 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } // figure out type substitutions for type parameters - val classTypeParameters = receiverType?.getArguments() ?: Collections.emptyList() - val ownerTypeArguments = (placement as? CallablePlacement.WithReceiver)?.receiverTypeCandidate?.theType?.getArguments() - ?: Collections.emptyList() - assert(ownerTypeArguments.size == classTypeParameters.size) - val substitutions = ownerTypeArguments.zip(classTypeParameters).map { - JetTypeSubstitution(it.first.getType(), it.second.getType()) - }.copyToArray() + val substitutionMap = LinkedHashMap() + collectSubstitutionsForReceiverTypeParameters(receiverType, substitutionMap) + val typeArgumentsForFakeFunction = callableInfo.typeParameterInfos + .map { + val typeCandidates = computeTypeCandidates(it) + assert (typeCandidates.size == 1, "Ambiguous type candidates for type parameter $it: $typeCandidates") + typeCandidates.first().theType + } + .subtract(substitutionMap.keySet()) + val fakeFunction = createFakeFunctionDescriptor(scope, typeArgumentsForFakeFunction.size) + collectSubstitutionsForCallableTypeParameters(fakeFunction, typeArgumentsForFakeFunction, substitutionMap) + substitutions = substitutionMap.map { JetTypeSubstitution(it.key, it.value) } callableInfo.parameterInfos.forEach { computeTypeCandidates(it.typeInfo, substitutions, scope) @@ -261,18 +282,61 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { // figure out type parameter renames to avoid conflicts typeParameterNameMap = getTypeParameterRenames(scope) - callableInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap) } + callableInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap, fakeFunction) } if (!skipReturnType) { - renderTypeCandidates(callableInfo.returnTypeInfo, typeParameterNameMap) + renderTypeCandidates(callableInfo.returnTypeInfo, typeParameterNameMap, fakeFunction) } - receiverTypeCandidate?.render(typeParameterNameMap) + receiverTypeCandidate?.render(typeParameterNameMap, fakeFunction) + } + + private fun collectSubstitutionsForReceiverTypeParameters( + receiverType: JetType?, + result: MutableMap + ) { + val classTypeParameters = receiverType?.getArguments() ?: Collections.emptyList() + val ownerTypeArguments = (placement as? CallablePlacement.WithReceiver)?.receiverTypeCandidate?.theType?.getArguments() + ?: Collections.emptyList() + assert(ownerTypeArguments.size == classTypeParameters.size) + ownerTypeArguments.zip(classTypeParameters).forEach { result[it.first.getType()] = it.second.getType() } + } + + private fun collectSubstitutionsForCallableTypeParameters( + fakeFunction: FunctionDescriptor, + typeArguments: Set, + result: MutableMap) { + for ((typeArgument, typeParameter) in typeArguments zip fakeFunction.getTypeParameters()) { + result[typeArgument] = typeParameter.getDefaultType() + } + } + + private fun createFakeFunctionDescriptor(scope: JetScope, typeParameterCount: Int): FunctionDescriptor { + val fakeFunction = SimpleFunctionDescriptorImpl.create( + MutablePackageFragmentDescriptor(currentFileModule, FqName("fake")), + Annotations.EMPTY, + Name.identifier("fake"), + CallableMemberDescriptor.Kind.SYNTHESIZED, + SourceElement.NO_SOURCE + ) + val validator = CollectingValidator { scope.getClassifier(Name.identifier(it)) == null } + val typeParameters = typeParameterCount.indices.map { + TypeParameterDescriptorImpl.createWithDefaultBound( + fakeFunction, + Annotations.EMPTY, + false, + Variance.INVARIANT, + Name.identifier(validator.validateName("T")), + it + ) + } + return fakeFunction.initialize(null, null, typeParameters, Collections.emptyList(), null, null, Visibilities.INTERNAL) } private fun renderTypeCandidates( typeInfo: TypeInfo, - typeParameterNameMap: Map + typeParameterNameMap: Map, + fakeFunction: FunctionDescriptor ) { - typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap) } + typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap, fakeFunction) } } private fun createDeclarationSkeleton(): JetCallableDeclaration { @@ -474,6 +538,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { func.getBodyExpression()!!.replace(newBodyExpression) } + private fun setupCallTypeArguments(callExpr: JetCallExpression, typeParameters: List) { + val oldTypeArgumentList = callExpr.getTypeArgumentList() ?: return + val renderedTypeArgs = typeParameters.map { typeParameter -> + val type = substitutions.first { it.byType.getConstructor().getDeclarationDescriptor() == typeParameter }.forType + IdeDescriptorRenderers.SOURCE_CODE.renderType(type) + } + oldTypeArgumentList.replace(JetPsiFactory(callExpr).createTypeArguments(renderedTypeArgs.joinToString(", ", "<", ">"))) + elementsToShorten.add(callExpr.getTypeArgumentList()) + } + private fun setupReturnTypeTemplate(builder: TemplateBuilder, declaration: JetCallableDeclaration): TypeExpression? { val returnTypeRef = declaration.getTypeReference() ?: return null val candidates = typeCandidates[callableInfo.returnTypeInfo]!! @@ -500,16 +574,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, declaration: JetCallableDeclaration): TypeParameterListExpression { - val typeParameterMap = HashMap>() - val receiverTypeParameterNames = receiverTypeCandidate?.let { it.typeParameterNames!! } ?: ArrayUtil.EMPTY_STRING_ARRAY + val typeParameterMap = HashMap>() + val receiverTypeParameterNames = receiverTypeCandidate?.let { it.renderedTypeParameters!! } ?: Collections.emptyList() callableInfo.parameterInfos.stream().flatMap { typeCandidates[it.typeInfo]!!.stream() }.forEach { - typeParameterMap[it.renderedType!!] = it.typeParameterNames!! + typeParameterMap[it.renderedType!!] = it.renderedTypeParameters!! } if (declaration.getTypeReference() != null) { typeCandidates[callableInfo.returnTypeInfo]!!.forEach { - typeParameterMap[it.renderedType!!] = it.typeParameterNames!! + typeParameterMap[it.renderedType!!] = it.renderedTypeParameters!! } } // ((3, 3) is after "fun") @@ -618,6 +692,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { // file templates if (newDeclaration is JetNamedFunction) { setupFunctionBody(newDeclaration) + (config.originalExpression as? JetCallExpression)?.let { + setupCallTypeArguments(it, expression.currentTypeParameters) + } } // change short type names to fully qualified ones (to be shortened below) diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt index 08e722af81f..fffaddd2577 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/CallableInfo.kt @@ -14,6 +14,7 @@ import org.jetbrains.jet.lang.types.TypeUtils import org.jetbrains.jet.lang.types.ErrorUtils import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns import org.jetbrains.jet.lang.psi.JetElement +import org.jetbrains.jet.lang.psi.JetTypeReference /** * Represents a concrete type or a set of types yet to be inferred from an expression. @@ -32,6 +33,11 @@ abstract class TypeInfo(val variance: Variance) { expression.guessTypes(builder.currentFileContext, builder.currentFileModule).flatMap { it.getPossibleSupertypes(variance) } } + class ByTypeReference(val typeReference: JetTypeReference, variance: Variance): TypeInfo(variance) { + override fun getPossibleTypes(builder: CallableBuilder): List = + builder.currentFileContext[BindingContext.TYPE, typeReference].getPossibleSupertypes(variance) + } + class ByType(val theType: JetType, variance: Variance, val keepUnsubstituted: Boolean = false): TypeInfo(variance) { override fun getPossibleTypes(builder: CallableBuilder): List = theType.getPossibleSupertypes(variance) @@ -45,8 +51,8 @@ abstract class TypeInfo(val variance: Variance) { open val possibleNamesFromExpression: Array get() = ArrayUtil.EMPTY_STRING_ARRAY abstract fun getPossibleTypes(builder: CallableBuilder): List - protected fun JetType.getPossibleSupertypes(variance: Variance): List { - if (ErrorUtils.containsErrorType(this)) return Collections.singletonList(KotlinBuiltIns.getInstance().getAnyType()) + protected fun JetType?.getPossibleSupertypes(variance: Variance): List { + if (this == null || ErrorUtils.containsErrorType(this)) return Collections.singletonList(KotlinBuiltIns.getInstance().getAnyType()) val single = Collections.singletonList(this) return when (variance) { Variance.IN_VARIANCE -> single + supertypes() @@ -56,6 +62,7 @@ abstract class TypeInfo(val variance: Variance) { } fun TypeInfo(expressionOfType: JetExpression, variance: Variance): TypeInfo = TypeInfo.ByExpression(expressionOfType, variance) +fun TypeInfo(typeReference: JetTypeReference, variance: Variance): TypeInfo = TypeInfo.ByTypeReference(typeReference, variance) fun TypeInfo(theType: JetType, variance: Variance): TypeInfo = TypeInfo.ByType(theType, variance) /** @@ -75,7 +82,8 @@ abstract class CallableInfo ( val name: String, val receiverTypeInfo: TypeInfo, val returnTypeInfo: TypeInfo, - val possibleContainers: List + val possibleContainers: List, + val typeParameterInfos: List ) { abstract val kind: CallableKind abstract val parameterInfos: List @@ -85,8 +93,9 @@ class FunctionInfo(name: String, receiverTypeInfo: TypeInfo, returnTypeInfo: TypeInfo, possibleContainers: List = Collections.emptyList(), - override val parameterInfos: List = Collections.emptyList() -) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers) { + override val parameterInfos: List = Collections.emptyList(), + typeParameterInfos: List = Collections.emptyList() +) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos) { override val kind: CallableKind get() = CallableKind.FUNCTION } @@ -94,8 +103,9 @@ class PropertyInfo(name: String, receiverTypeInfo: TypeInfo, returnTypeInfo: TypeInfo, val writable: Boolean, - possibleContainers: List = Collections.emptyList() -) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers) { + possibleContainers: List = Collections.emptyList(), + typeParameterInfos: List = Collections.emptyList() +) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos) { override val kind: CallableKind get() = CallableKind.PROPERTY override val parameterInfos: List get() = Collections.emptyList() } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/templateExpressions.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/templateExpressions.kt index 68bd923e6b0..7e20b84fd0f 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/templateExpressions.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/callableBuilder/templateExpressions.kt @@ -17,6 +17,8 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder import org.jetbrains.jet.lang.types.JetType import org.jetbrains.jet.lang.psi.JetCallableDeclaration import java.util.Collections +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor /** * Special Expression for parameter names based on its type. @@ -97,8 +99,10 @@ private class TypeExpression(public val typeCandidates: List) : E /** * A sort-of dummy Expression for parameter lists, to allow us to update the parameter list as the user makes selections. */ -private class TypeParameterListExpression(private val typeParameterNamesFromReceiverType: Array, - private val parameterTypeToTypeParameterNamesMap: Map>) : Expression() { +private class TypeParameterListExpression(private val typeParameterNamesFromReceiverType: List, + private val parameterTypeToTypeParameterNamesMap: Map>) : Expression() { + public var currentTypeParameters: List = Collections.emptyList() + private set override fun calculateResult(context: ExpressionContext?): Result { context!! @@ -112,14 +116,14 @@ private class TypeParameterListExpression(private val typeParameterNamesFromRece val callable = PsiTreeUtil.getParentOfType(elementAt, javaClass()) ?: return TextResult("") val parameters = callable.getValueParameterList()?.getParameters() ?: Collections.emptyList() - val typeParameterNames = LinkedHashSet() - typeParameterNames.addAll(typeParameterNamesFromReceiverType) + val renderedTypeParameters = LinkedHashSet() + renderedTypeParameters.addAll(typeParameterNamesFromReceiverType) for (parameter in parameters) { val parameterTypeRef = parameter.getTypeReference() if (parameterTypeRef != null) { val typeParameterNamesFromParameter = parameterTypeToTypeParameterNamesMap[parameterTypeRef.getText()] if (typeParameterNamesFromParameter != null) { - typeParameterNames.addAll(typeParameterNamesFromParameter) + renderedTypeParameters.addAll(typeParameterNamesFromParameter) } } } @@ -127,11 +131,17 @@ private class TypeParameterListExpression(private val typeParameterNamesFromRece if (returnTypeRef != null) { val typeParameterNamesFromReturnType = parameterTypeToTypeParameterNamesMap[returnTypeRef.getText()] if (typeParameterNamesFromReturnType != null) { - typeParameterNames.addAll(typeParameterNamesFromReturnType) + renderedTypeParameters.addAll(typeParameterNamesFromReturnType) } } - return TextResult(if (typeParameterNames.empty) "" else typeParameterNames.joinToString(", ", " <", ">")) + + val sortedRenderedTypeParameters = renderedTypeParameters.sortBy { if (it.fake) it.typeParameter.getIndex() else -1} + currentTypeParameters = sortedRenderedTypeParameters.map { it.typeParameter } + + return TextResult( + if (sortedRenderedTypeParameters.empty) "" else sortedRenderedTypeParameters.map { it.text }.joinToString(", ", " <", ">") + ) } override fun calculateQuickResult(context: ExpressionContext?): Result = calculateResult(context) diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateCallableFromUsageFix.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateCallableFromUsageFix.kt index 4210c01c134..c48fa552afa 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateCallableFromUsageFix.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateCallableFromUsageFix.kt @@ -59,7 +59,7 @@ public class CreateCallableFromUsageFix( // TODO: Support generation of Java class members val containers = receiverTypeCandidates .map { candidate -> - val descriptor = DescriptorUtils.getClassDescriptorForType(candidate.theType) + val descriptor = candidate.theType.getConstructor().getDeclarationDescriptor() (DescriptorToDeclarationUtil.getDeclaration(file, descriptor) as? JetClassOrObject)?.let { candidate to it } } .filterNotNull() diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionOrPropertyFromCallActionFactory.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionOrPropertyFromCallActionFactory.kt index 5534112a2b4..6b0a825e196 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionOrPropertyFromCallActionFactory.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/createFromUsage/createFunction/CreateFunctionOrPropertyFromCallActionFactory.kt @@ -84,8 +84,9 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetSingleIntentionActionF it.getArgumentName()?.getReferenceExpression()?.getReferencedName() ) } + val typeParameters = callExpr.getTypeArguments().map { TypeInfo(it.getTypeReference(), Variance.INVARIANT) } val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE) - FunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, possibleContainers, parameters) + FunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, possibleContainers, parameters, typeParameters) } is JetSimpleNameExpression -> { diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMember.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMember.kt new file mode 100644 index 00000000000..f256a9608f2 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMember.kt @@ -0,0 +1,14 @@ +// "Create function 'foo' from usage" "true" + +class B(val t: T) { + fun foo(arg: T1, arg1: T2): T1 { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} + +class A(val b: B) { + fun test(): Int { + return b.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberPartialSubstitution.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberPartialSubstitution.kt new file mode 100644 index 00000000000..9c40285c823 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberPartialSubstitution.kt @@ -0,0 +1,14 @@ +// "Create function 'foo' from usage" "true" + +class B(val t: T) { + fun foo(i: Int, arg: T1): Int { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} + +class A(val b: B) { + fun test(): Int { + return b.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberWithReceiverArg.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberWithReceiverArg.kt new file mode 100644 index 00000000000..f256a9608f2 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterClassMemberWithReceiverArg.kt @@ -0,0 +1,14 @@ +// "Create function 'foo' from usage" "true" + +class B(val t: T) { + fun foo(arg: T1, arg1: T2): T1 { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} + +class A(val b: B) { + fun test(): Int { + return b.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtension.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtension.kt new file mode 100644 index 00000000000..2cbc280d14c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtension.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val items: List) { + fun test(): Int { + return items.foo(2, "2") + } +} + +fun List.foo(arg: T, arg1: T1): T { + 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/createFunction/call/typeArguments/afterExtensionPartialSubstitution.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtensionPartialSubstitution.kt new file mode 100644 index 00000000000..62cab0ae6a4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtensionPartialSubstitution.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val items: List) { + fun test(): Int { + return items.foo(2, "2") + } +} + +fun List.foo(arg: T, s: String): T { + 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/createFunction/call/typeArguments/afterExtensionWithReceiverArg.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtensionWithReceiverArg.kt new file mode 100644 index 00000000000..2cbc280d14c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterExtensionWithReceiverArg.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class A(val items: List) { + fun test(): Int { + return items.foo(2, "2") + } +} + +fun List.foo(arg: T, arg1: T1): T { + 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/createFunction/call/typeArguments/afterNoReceiver.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiver.kt new file mode 100644 index 00000000000..18c353a50bd --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiver.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +fun test(): Int { + return foo(2, "2") +} + +fun foo(arg: T1, arg1: T): T1 { + 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/createFunction/call/typeArguments/afterNoReceiverExtraArgs.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverExtraArgs.kt new file mode 100644 index 00000000000..18c353a50bd --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverExtraArgs.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +fun test(): Int { + return foo(2, "2") +} + +fun foo(arg: T1, arg1: T): T1 { + 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/createFunction/call/typeArguments/afterNoReceiverLongName.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverLongName.kt new file mode 100644 index 00000000000..18c353a50bd --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverLongName.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +fun test(): Int { + return foo(2, "2") +} + +fun foo(arg: T1, arg1: T): T1 { + 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/createFunction/call/typeArguments/afterNoReceiverPartialSubstitution.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverPartialSubstitution.kt new file mode 100644 index 00000000000..c5d806a0fc7 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/afterNoReceiverPartialSubstitution.kt @@ -0,0 +1,9 @@ +// "Create function 'foo' from usage" "true" + +fun test(): Int { + return foo(2, "2") +} + +fun foo(arg: T, s: String): T { + 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/createFunction/call/typeArguments/beforeClassMember.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMember.kt new file mode 100644 index 00000000000..146d964bdc8 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMember.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class B(val t: T) { + +} + +class A(val b: B) { + fun test(): Int { + return b.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberPartialSubstitution.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberPartialSubstitution.kt new file mode 100644 index 00000000000..b67cf8a5e0f --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberPartialSubstitution.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class B(val t: T) { + +} + +class A(val b: B) { + fun test(): Int { + return b.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberWithReceiverArg.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberWithReceiverArg.kt new file mode 100644 index 00000000000..a817c613873 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberWithReceiverArg.kt @@ -0,0 +1,11 @@ +// "Create function 'foo' from usage" "true" + +class B(val t: T) { + +} + +class A(val b: B) { + fun test(): Int { + return b.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtension.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtension.kt new file mode 100644 index 00000000000..7de707b2a45 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtension.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val items: List) { + fun test(): Int { + return items.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionPartialSubstitution.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionPartialSubstitution.kt new file mode 100644 index 00000000000..0b686707884 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionPartialSubstitution.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val items: List) { + fun test(): Int { + return items.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionWithReceiverArg.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionWithReceiverArg.kt new file mode 100644 index 00000000000..3d0eba2d648 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionWithReceiverArg.kt @@ -0,0 +1,7 @@ +// "Create function 'foo' from usage" "true" + +class A(val items: List) { + fun test(): Int { + return items.foo(2, "2") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiver.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiver.kt new file mode 100644 index 00000000000..05ac7d2f7f3 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiver.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/createFunction/call/typeArguments/beforeNoReceiverExtraArgs.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverExtraArgs.kt new file mode 100644 index 00000000000..1ab2f36b67e --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverExtraArgs.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/createFunction/call/typeArguments/beforeNoReceiverLongName.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverLongName.kt new file mode 100644 index 00000000000..32a5fd9e227 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverLongName.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/createFunction/call/typeArguments/beforeNoReceiverPartialSubstitution.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverPartialSubstitution.kt new file mode 100644 index 00000000000..1a042f35896 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverPartialSubstitution.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/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java index 19dc62781b3..e6b4374afc5 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixMultiFileTestGenerated.java @@ -239,6 +239,7 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/call") @TestDataPath("$PROJECT_ROOT") + @InnerTestClasses({}) @RunWith(JUnit3RunnerWithInners.class) public static class Call extends AbstractQuickFixMultiFileTest { public void testAllFilesPresentInCall() throws Exception { @@ -250,6 +251,7 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/funOnJavaType.before.Main.kt"); doTestWithExtraFile(fileName); } + } } diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java index c36f058a199..e1127d720b5 100644 --- a/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/QuickFixTestGenerated.java @@ -730,6 +730,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/call") @TestDataPath("$PROJECT_ROOT") + @InnerTestClasses({Call.TypeArguments.class}) @RunWith(JUnit3RunnerWithInners.class) public static class Call extends AbstractQuickFixTest { public void testAllFilesPresentInCall() throws Exception { @@ -933,6 +934,75 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeUnresolvedSupertype.kt"); doTest(fileName); } + + @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeArguments extends AbstractQuickFixTest { + public void testAllFilesPresentInTypeArguments() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments"), Pattern.compile("^before(\\w+)\\.kt$"), true); + } + + @TestMetadata("beforeClassMember.kt") + public void testClassMember() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMember.kt"); + doTest(fileName); + } + + @TestMetadata("beforeClassMemberPartialSubstitution.kt") + public void testClassMemberPartialSubstitution() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberPartialSubstitution.kt"); + doTest(fileName); + } + + @TestMetadata("beforeClassMemberWithReceiverArg.kt") + public void testClassMemberWithReceiverArg() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeClassMemberWithReceiverArg.kt"); + doTest(fileName); + } + + @TestMetadata("beforeExtension.kt") + public void testExtension() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtension.kt"); + doTest(fileName); + } + + @TestMetadata("beforeExtensionPartialSubstitution.kt") + public void testExtensionPartialSubstitution() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionPartialSubstitution.kt"); + doTest(fileName); + } + + @TestMetadata("beforeExtensionWithReceiverArg.kt") + public void testExtensionWithReceiverArg() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeExtensionWithReceiverArg.kt"); + doTest(fileName); + } + + @TestMetadata("beforeNoReceiver.kt") + public void testNoReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("beforeNoReceiverExtraArgs.kt") + public void testNoReceiverExtraArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverExtraArgs.kt"); + doTest(fileName); + } + + @TestMetadata("beforeNoReceiverLongName.kt") + public void testNoReceiverLongName() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverLongName.kt"); + doTest(fileName); + } + + @TestMetadata("beforeNoReceiverPartialSubstitution.kt") + public void testNoReceiverPartialSubstitution() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/typeArguments/beforeNoReceiverPartialSubstitution.kt"); + doTest(fileName); + } + } } @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/component")