Create From Usage: Use supplied type arguments to infer function type parameters

This commit is contained in:
Alexey Sedunov
2014-10-22 20:32:21 +04:00
parent a13f334df2
commit abbbd198fd
27 changed files with 393 additions and 38 deletions
@@ -51,7 +51,6 @@ import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
import org.jetbrains.jet.plugin.refactoring.EmptyValidator import org.jetbrains.jet.plugin.refactoring.EmptyValidator
import org.jetbrains.jet.plugin.refactoring.CollectingValidator import org.jetbrains.jet.plugin.refactoring.CollectingValidator
import org.jetbrains.jet.plugin.util.isUnit import org.jetbrains.jet.plugin.util.isUnit
import com.intellij.util.ArrayUtil
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.jet.lexer.JetTokens 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.plugin.refactoring.isMultiLine
import org.jetbrains.jet.lang.types.checker.JetTypeChecker import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import com.intellij.psi.SmartPointerManager 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 TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList"
private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt" 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<TypeParameterDescriptor> public val typeParameters: Array<TypeParameterDescriptor>
var renderedType: String? = null var renderedType: String? = null
private set private set
var typeParameterNames: Array<String>? = null var renderedTypeParameters: List<RenderedTypeParameter>? = null
private set private set
fun render(typeParameterNameMap: Map<TypeParameterDescriptor, String>) { fun render(typeParameterNameMap: Map<TypeParameterDescriptor, String>, fakeFunction: FunctionDescriptor) {
renderedType = theType.renderShort(typeParameterNameMap); 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() override fun toString() = theType.toString()
} }
class RenderedTypeParameter(
val typeParameter: TypeParameterDescriptor,
val fake: Boolean,
val text: String
)
fun List<TypeCandidate>.getTypeByRenderedType(renderedType: String): JetType? = fun List<TypeCandidate>.getTypeByRenderedType(renderedType: String): JetType? =
firstOrNull { it.renderedType == renderedType }?.theType firstOrNull { it.renderedType == renderedType }?.theType
@@ -140,7 +154,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
fun computeTypeCandidates( fun computeTypeCandidates(
typeInfo: TypeInfo, typeInfo: TypeInfo,
substitutions: Array<JetTypeSubstitution>, substitutions: List<JetTypeSubstitution>,
scope: JetScope): List<TypeCandidate> { scope: JetScope): List<TypeCandidate> {
if (typeInfo is TypeInfo.ByType && typeInfo.keepUnsubstituted) return computeTypeCandidates(typeInfo) if (typeInfo is TypeInfo.ByType && typeInfo.keepUnsubstituted) return computeTypeCandidates(typeInfo)
return typeCandidates.getOrPut(typeInfo) { return typeCandidates.getOrPut(typeInfo) {
@@ -201,6 +215,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val receiverClassDescriptor: ClassDescriptor? val receiverClassDescriptor: ClassDescriptor?
val typeParameterNameMap: Map<TypeParameterDescriptor, String> val typeParameterNameMap: Map<TypeParameterDescriptor, String>
val receiverTypeCandidate: TypeCandidate? val receiverTypeCandidate: TypeCandidate?
val substitutions: List<JetTypeSubstitution>
{ {
// gather relevant information // gather relevant information
@@ -213,7 +228,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
containingElement = placement.containingElement containingElement = placement.containingElement
} }
placement is CallablePlacement.WithReceiver -> { 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) } val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.classDescriptorToDeclaration(it) }
isExtension = !(classDeclaration is JetClassOrObject && classDeclaration.isWritable()) isExtension = !(classDeclaration is JetClassOrObject && classDeclaration.isWritable())
containingElement = if (isExtension) config.currentFile else classDeclaration as JetElement 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 // figure out type substitutions for type parameters
val classTypeParameters = receiverType?.getArguments() ?: Collections.emptyList() val substitutionMap = LinkedHashMap<JetType, JetType>()
val ownerTypeArguments = (placement as? CallablePlacement.WithReceiver)?.receiverTypeCandidate?.theType?.getArguments() collectSubstitutionsForReceiverTypeParameters(receiverType, substitutionMap)
?: Collections.emptyList() val typeArgumentsForFakeFunction = callableInfo.typeParameterInfos
assert(ownerTypeArguments.size == classTypeParameters.size) .map {
val substitutions = ownerTypeArguments.zip(classTypeParameters).map { val typeCandidates = computeTypeCandidates(it)
JetTypeSubstitution(it.first.getType(), it.second.getType()) assert (typeCandidates.size == 1, "Ambiguous type candidates for type parameter $it: $typeCandidates")
}.copyToArray() 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 { callableInfo.parameterInfos.forEach {
computeTypeCandidates(it.typeInfo, substitutions, scope) computeTypeCandidates(it.typeInfo, substitutions, scope)
@@ -261,18 +282,61 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
// figure out type parameter renames to avoid conflicts // figure out type parameter renames to avoid conflicts
typeParameterNameMap = getTypeParameterRenames(scope) typeParameterNameMap = getTypeParameterRenames(scope)
callableInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap) } callableInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap, fakeFunction) }
if (!skipReturnType) { 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<JetType, JetType>
) {
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<JetType>,
result: MutableMap<JetType, JetType>) {
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( private fun renderTypeCandidates(
typeInfo: TypeInfo, typeInfo: TypeInfo,
typeParameterNameMap: Map<TypeParameterDescriptor, String> typeParameterNameMap: Map<TypeParameterDescriptor, String>,
fakeFunction: FunctionDescriptor
) { ) {
typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap) } typeCandidates[typeInfo]?.forEach { it.render(typeParameterNameMap, fakeFunction) }
} }
private fun createDeclarationSkeleton(): JetCallableDeclaration { private fun createDeclarationSkeleton(): JetCallableDeclaration {
@@ -474,6 +538,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
func.getBodyExpression()!!.replace(newBodyExpression) func.getBodyExpression()!!.replace(newBodyExpression)
} }
private fun setupCallTypeArguments(callExpr: JetCallExpression, typeParameters: List<TypeParameterDescriptor>) {
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? { private fun setupReturnTypeTemplate(builder: TemplateBuilder, declaration: JetCallableDeclaration): TypeExpression? {
val returnTypeRef = declaration.getTypeReference() ?: return null val returnTypeRef = declaration.getTypeReference() ?: return null
val candidates = typeCandidates[callableInfo.returnTypeInfo]!! val candidates = typeCandidates[callableInfo.returnTypeInfo]!!
@@ -500,16 +574,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
} }
private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, declaration: JetCallableDeclaration): TypeParameterListExpression { private fun setupTypeParameterListTemplate(builder: TemplateBuilderImpl, declaration: JetCallableDeclaration): TypeParameterListExpression {
val typeParameterMap = HashMap<String, Array<String>>() val typeParameterMap = HashMap<String, List<RenderedTypeParameter>>()
val receiverTypeParameterNames = receiverTypeCandidate?.let { it.typeParameterNames!! } ?: ArrayUtil.EMPTY_STRING_ARRAY val receiverTypeParameterNames = receiverTypeCandidate?.let { it.renderedTypeParameters!! } ?: Collections.emptyList()
callableInfo.parameterInfos.stream().flatMap { typeCandidates[it.typeInfo]!!.stream() }.forEach { callableInfo.parameterInfos.stream().flatMap { typeCandidates[it.typeInfo]!!.stream() }.forEach {
typeParameterMap[it.renderedType!!] = it.typeParameterNames!! typeParameterMap[it.renderedType!!] = it.renderedTypeParameters!!
} }
if (declaration.getTypeReference() != null) { if (declaration.getTypeReference() != null) {
typeCandidates[callableInfo.returnTypeInfo]!!.forEach { typeCandidates[callableInfo.returnTypeInfo]!!.forEach {
typeParameterMap[it.renderedType!!] = it.typeParameterNames!! typeParameterMap[it.renderedType!!] = it.renderedTypeParameters!!
} }
} }
// ((3, 3) is after "fun") // ((3, 3) is after "fun")
@@ -618,6 +692,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
// file templates // file templates
if (newDeclaration is JetNamedFunction) { if (newDeclaration is JetNamedFunction) {
setupFunctionBody(newDeclaration) setupFunctionBody(newDeclaration)
(config.originalExpression as? JetCallExpression)?.let {
setupCallTypeArguments(it, expression.currentTypeParameters)
}
} }
// change short type names to fully qualified ones (to be shortened below) // change short type names to fully qualified ones (to be shortened below)
@@ -14,6 +14,7 @@ import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.types.ErrorUtils import org.jetbrains.jet.lang.types.ErrorUtils
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.psi.JetElement 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. * 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) } 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<JetType> =
builder.currentFileContext[BindingContext.TYPE, typeReference].getPossibleSupertypes(variance)
}
class ByType(val theType: JetType, variance: Variance, val keepUnsubstituted: Boolean = false): TypeInfo(variance) { class ByType(val theType: JetType, variance: Variance, val keepUnsubstituted: Boolean = false): TypeInfo(variance) {
override fun getPossibleTypes(builder: CallableBuilder): List<JetType> = override fun getPossibleTypes(builder: CallableBuilder): List<JetType> =
theType.getPossibleSupertypes(variance) theType.getPossibleSupertypes(variance)
@@ -45,8 +51,8 @@ abstract class TypeInfo(val variance: Variance) {
open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY
abstract fun getPossibleTypes(builder: CallableBuilder): List<JetType> abstract fun getPossibleTypes(builder: CallableBuilder): List<JetType>
protected fun JetType.getPossibleSupertypes(variance: Variance): List<JetType> { protected fun JetType?.getPossibleSupertypes(variance: Variance): List<JetType> {
if (ErrorUtils.containsErrorType(this)) return Collections.singletonList(KotlinBuiltIns.getInstance().getAnyType()) if (this == null || ErrorUtils.containsErrorType(this)) return Collections.singletonList(KotlinBuiltIns.getInstance().getAnyType())
val single = Collections.singletonList(this) val single = Collections.singletonList(this)
return when (variance) { return when (variance) {
Variance.IN_VARIANCE -> single + supertypes() 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(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) fun TypeInfo(theType: JetType, variance: Variance): TypeInfo = TypeInfo.ByType(theType, variance)
/** /**
@@ -75,7 +82,8 @@ abstract class CallableInfo (
val name: String, val name: String,
val receiverTypeInfo: TypeInfo, val receiverTypeInfo: TypeInfo,
val returnTypeInfo: TypeInfo, val returnTypeInfo: TypeInfo,
val possibleContainers: List<JetElement> val possibleContainers: List<JetElement>,
val typeParameterInfos: List<TypeInfo>
) { ) {
abstract val kind: CallableKind abstract val kind: CallableKind
abstract val parameterInfos: List<ParameterInfo> abstract val parameterInfos: List<ParameterInfo>
@@ -85,8 +93,9 @@ class FunctionInfo(name: String,
receiverTypeInfo: TypeInfo, receiverTypeInfo: TypeInfo,
returnTypeInfo: TypeInfo, returnTypeInfo: TypeInfo,
possibleContainers: List<JetElement> = Collections.emptyList(), possibleContainers: List<JetElement> = Collections.emptyList(),
override val parameterInfos: List<ParameterInfo> = Collections.emptyList() override val parameterInfos: List<ParameterInfo> = Collections.emptyList(),
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers) { typeParameterInfos: List<TypeInfo> = Collections.emptyList()
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos) {
override val kind: CallableKind get() = CallableKind.FUNCTION override val kind: CallableKind get() = CallableKind.FUNCTION
} }
@@ -94,8 +103,9 @@ class PropertyInfo(name: String,
receiverTypeInfo: TypeInfo, receiverTypeInfo: TypeInfo,
returnTypeInfo: TypeInfo, returnTypeInfo: TypeInfo,
val writable: Boolean, val writable: Boolean,
possibleContainers: List<JetElement> = Collections.emptyList() possibleContainers: List<JetElement> = Collections.emptyList(),
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers) { typeParameterInfos: List<TypeInfo> = Collections.emptyList()
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos) {
override val kind: CallableKind get() = CallableKind.PROPERTY override val kind: CallableKind get() = CallableKind.PROPERTY
override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList() override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList()
} }
@@ -17,6 +17,8 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
import org.jetbrains.jet.lang.types.JetType import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.psi.JetCallableDeclaration import org.jetbrains.jet.lang.psi.JetCallableDeclaration
import java.util.Collections import java.util.Collections
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
/** /**
* Special <code>Expression</code> for parameter names based on its type. * Special <code>Expression</code> for parameter names based on its type.
@@ -97,8 +99,10 @@ private class TypeExpression(public val typeCandidates: List<TypeCandidate>) : E
/** /**
* A sort-of dummy <code>Expression</code> for parameter lists, to allow us to update the parameter list as the user makes selections. * A sort-of dummy <code>Expression</code> for parameter lists, to allow us to update the parameter list as the user makes selections.
*/ */
private class TypeParameterListExpression(private val typeParameterNamesFromReceiverType: Array<String>, private class TypeParameterListExpression(private val typeParameterNamesFromReceiverType: List<RenderedTypeParameter>,
private val parameterTypeToTypeParameterNamesMap: Map<String, Array<String>>) : Expression() { private val parameterTypeToTypeParameterNamesMap: Map<String, List<RenderedTypeParameter>>) : Expression() {
public var currentTypeParameters: List<TypeParameterDescriptor> = Collections.emptyList()
private set
override fun calculateResult(context: ExpressionContext?): Result { override fun calculateResult(context: ExpressionContext?): Result {
context!! context!!
@@ -112,14 +116,14 @@ private class TypeParameterListExpression(private val typeParameterNamesFromRece
val callable = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetCallableDeclaration>()) ?: return TextResult("") val callable = PsiTreeUtil.getParentOfType(elementAt, javaClass<JetCallableDeclaration>()) ?: return TextResult("")
val parameters = callable.getValueParameterList()?.getParameters() ?: Collections.emptyList<JetParameter>() val parameters = callable.getValueParameterList()?.getParameters() ?: Collections.emptyList<JetParameter>()
val typeParameterNames = LinkedHashSet<String>() val renderedTypeParameters = LinkedHashSet<RenderedTypeParameter>()
typeParameterNames.addAll(typeParameterNamesFromReceiverType) renderedTypeParameters.addAll(typeParameterNamesFromReceiverType)
for (parameter in parameters) { for (parameter in parameters) {
val parameterTypeRef = parameter.getTypeReference() val parameterTypeRef = parameter.getTypeReference()
if (parameterTypeRef != null) { if (parameterTypeRef != null) {
val typeParameterNamesFromParameter = parameterTypeToTypeParameterNamesMap[parameterTypeRef.getText()] val typeParameterNamesFromParameter = parameterTypeToTypeParameterNamesMap[parameterTypeRef.getText()]
if (typeParameterNamesFromParameter != null) { if (typeParameterNamesFromParameter != null) {
typeParameterNames.addAll(typeParameterNamesFromParameter) renderedTypeParameters.addAll(typeParameterNamesFromParameter)
} }
} }
} }
@@ -127,11 +131,17 @@ private class TypeParameterListExpression(private val typeParameterNamesFromRece
if (returnTypeRef != null) { if (returnTypeRef != null) {
val typeParameterNamesFromReturnType = parameterTypeToTypeParameterNamesMap[returnTypeRef.getText()] val typeParameterNamesFromReturnType = parameterTypeToTypeParameterNamesMap[returnTypeRef.getText()]
if (typeParameterNamesFromReturnType != null) { 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) override fun calculateQuickResult(context: ExpressionContext?): Result = calculateResult(context)
@@ -59,7 +59,7 @@ public class CreateCallableFromUsageFix(
// TODO: Support generation of Java class members // TODO: Support generation of Java class members
val containers = receiverTypeCandidates val containers = receiverTypeCandidates
.map { candidate -> .map { candidate ->
val descriptor = DescriptorUtils.getClassDescriptorForType(candidate.theType) val descriptor = candidate.theType.getConstructor().getDeclarationDescriptor()
(DescriptorToDeclarationUtil.getDeclaration(file, descriptor) as? JetClassOrObject)?.let { candidate to it } (DescriptorToDeclarationUtil.getDeclaration(file, descriptor) as? JetClassOrObject)?.let { candidate to it }
} }
.filterNotNull() .filterNotNull()
@@ -84,8 +84,9 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetSingleIntentionActionF
it.getArgumentName()?.getReferenceExpression()?.getReferencedName() it.getArgumentName()?.getReferenceExpression()?.getReferencedName()
) )
} }
val typeParameters = callExpr.getTypeArguments().map { TypeInfo(it.getTypeReference(), Variance.INVARIANT) }
val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE) val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE)
FunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, possibleContainers, parameters) FunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, possibleContainers, parameters, typeParameters)
} }
is JetSimpleNameExpression -> { is JetSimpleNameExpression -> {
@@ -0,0 +1,14 @@
// "Create function 'foo' from usage" "true"
class B<T>(val t: T) {
fun <T1, T2> foo(arg: T1, arg1: T2): T1 {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class A<T>(val b: B<T>) {
fun test(): Int {
return b.foo<Int, String>(2, "2")
}
}
@@ -0,0 +1,14 @@
// "Create function 'foo' from usage" "true"
class B<T>(val t: T) {
fun <T1> foo(i: Int, arg: T1): Int {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class A<T>(val b: B<T>) {
fun test(): Int {
return b.foo<String>(2, "2")
}
}
@@ -0,0 +1,14 @@
// "Create function 'foo' from usage" "true"
class B<T>(val t: T) {
fun <T1, T2> foo(arg: T1, arg1: T2): T1 {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class A<T>(val b: B<T>) {
fun test(): Int {
return b.foo<Int, String>(2, "2")
}
}
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
class A<T>(val items: List<T>) {
fun test(): Int {
return items.foo<T, Int, String>(2, "2")
}
}
fun <E, T, T1> List<E>.foo(arg: T, arg1: T1): T {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
class A<T>(val items: List<T>) {
fun test(): Int {
return items.foo<T, Int>(2, "2")
}
}
fun <E, T> List<E>.foo(arg: T, s: String): T {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
class A<T>(val items: List<T>) {
fun test(): Int {
return items.foo<T, Int, String>(2, "2")
}
}
fun <E, T, T1> List<E>.foo(arg: T, arg1: T1): T {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,9 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return foo<String, Int>(2, "2")
}
fun <T, T1> foo(arg: T1, arg1: T): T1 {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,9 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return foo<String, Int>(2, "2")
}
fun <T, T1> foo(arg: T1, arg1: T): T1 {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,9 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return foo<String, Int>(2, "2")
}
fun <T, T1> foo(arg: T1, arg1: T): T1 {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,9 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return foo<Int>(2, "2")
}
fun <T> foo(arg: T, s: String): T {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
class B<T>(val t: T) {
}
class A<T>(val b: B<T>) {
fun test(): Int {
return b.<caret>foo<Int, String>(2, "2")
}
}
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
class B<T>(val t: T) {
}
class A<T>(val b: B<T>) {
fun test(): Int {
return b.<caret>foo<String>(2, "2")
}
}
@@ -0,0 +1,11 @@
// "Create function 'foo' from usage" "true"
class B<T>(val t: T) {
}
class A<T>(val b: B<T>) {
fun test(): Int {
return b.<caret>foo<T, Int, String>(2, "2")
}
}
@@ -0,0 +1,7 @@
// "Create function 'foo' from usage" "true"
class A<T>(val items: List<T>) {
fun test(): Int {
return items.<caret>foo<Int, String>(2, "2")
}
}
@@ -0,0 +1,7 @@
// "Create function 'foo' from usage" "true"
class A<T>(val items: List<T>) {
fun test(): Int {
return items.<caret>foo<Int>(2, "2")
}
}
@@ -0,0 +1,7 @@
// "Create function 'foo' from usage" "true"
class A<T>(val items: List<T>) {
fun test(): Int {
return items.<caret>foo<T, Int, String>(2, "2")
}
}
@@ -0,0 +1,5 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return <caret>foo<String, Int>(2, "2")
}
@@ -0,0 +1,5 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return <caret>foo<String, Int, Boolean>(2, "2")
}
@@ -0,0 +1,5 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return <caret>foo<kotlin.String, Int>(2, "2")
}
@@ -0,0 +1,5 @@
// "Create function 'foo' from usage" "true"
fun test(): Int {
return <caret>foo<Int>(2, "2")
}
@@ -239,6 +239,7 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/call") @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/call")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({})
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public static class Call extends AbstractQuickFixMultiFileTest { public static class Call extends AbstractQuickFixMultiFileTest {
public void testAllFilesPresentInCall() throws Exception { 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"); String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/funOnJavaType.before.Main.kt");
doTestWithExtraFile(fileName); doTestWithExtraFile(fileName);
} }
} }
} }
@@ -730,6 +730,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/call") @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/call")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({Call.TypeArguments.class})
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public static class Call extends AbstractQuickFixTest { public static class Call extends AbstractQuickFixTest {
public void testAllFilesPresentInCall() throws Exception { 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"); String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeUnresolvedSupertype.kt");
doTest(fileName); 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") @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/component")