diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt index e485db61244..5e1161dffcd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/typeUtils.kt @@ -19,18 +19,18 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder import com.intellij.refactoring.psi.SearchUtils import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.cfg.pseudocode.* -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.idea.references.JetSimpleNameReference +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass @@ -44,21 +44,36 @@ internal fun JetType.contains(inner: JetType): Boolean { return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() } } -private fun DeclarationDescriptor.render( - typeParameterNameMap: Map, - fq: Boolean -): String = when { - this is TypeParameterDescriptor -> typeParameterNameMap[this] ?: getName().asString() - fq -> DescriptorUtils.getFqName(this).asString() - else -> getName().asString() -} - private fun JetType.render(typeParameterNameMap: Map, fq: Boolean): String { - val arguments = getArguments().map { if (it.isStarProjection) "*" else it.getType().render(typeParameterNameMap, fq) } - val typeString = getConstructor().getDeclarationDescriptor()!!.render(typeParameterNameMap, fq) - val typeArgumentString = if (arguments.isNotEmpty()) arguments.joinToString(", ", "<", ">") else "" - val nullifier = if (isMarkedNullable()) "?" else "" - return "$typeString$typeArgumentString$nullifier" + val substitution = typeParameterNameMap + .mapValues { + val name = Name.identifier(it.value) + + val typeParameter = it.key + + var wrappingTypeParameter: TypeParameterDescriptor + var wrappingTypeConstructor: TypeConstructor + + wrappingTypeParameter = object : TypeParameterDescriptor by typeParameter { + override fun getName() = name + override fun getTypeConstructor() = wrappingTypeConstructor + } + + wrappingTypeConstructor = object : TypeConstructor by typeParameter.typeConstructor { + override fun getDeclarationDescriptor() = wrappingTypeParameter + } + + val wrappingType = object : JetType by typeParameter.defaultType { + override fun getConstructor() = wrappingTypeConstructor + } + + TypeProjectionImpl(wrappingType) + } + .mapKeys { it.key.typeConstructor } + + val typeToRender = TypeSubstitutor.create(substitution).substitute(this, Variance.INVARIANT)!! + val renderer = if (fq) IdeDescriptorRenderers.SOURCE_CODE else IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES + return renderer.renderType(typeToRender) } internal fun JetType.renderShort(typeParameterNameMap: Map) = render(typeParameterNameMap, false) diff --git a/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArg.kt.after b/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArg.kt.after index 5913a77ebdb..27eff900050 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArg.kt.after +++ b/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArg.kt.after @@ -4,6 +4,6 @@ fun test() { val a = Foo(2, "2") { p: Int -> p + 1 } } -class Foo(i: Int, s: String, function: Function1) { +class Foo(i: Int, s: String, function: (Int) -> Int) { } diff --git a/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArgOnly.kt.after b/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArgOnly.kt.after index 4141e375f11..8f06f94d84d 100644 --- a/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArgOnly.kt.after +++ b/idea/testData/quickfix/createFromUsage/createClass/callExpression/callWithLambdaArgOnly.kt.after @@ -4,6 +4,6 @@ fun test() { val a = Foo { p: Int -> p + 1 } } -class Foo(function: Function1) { +class Foo(function: (Int) -> Int) { } diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArg.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArg.kt.after index 067ac187a8c..2239bfa74ae 100644 --- a/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArg.kt.after +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArg.kt.after @@ -1,7 +1,7 @@ // "Create member function 'foo'" "true" class A(val n: T) { - fun foo(t: T, s: String, function: Function1): A { + fun foo(t: T, s: String, function: (T) -> T): A { throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. } diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArgOnly.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArgOnly.kt.after index 7a61c1307d9..553c9a35f21 100644 --- a/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArgOnly.kt.after +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/callWithLambdaArgOnly.kt.after @@ -1,7 +1,7 @@ // "Create member function 'foo'" "true" class A(val n: T) { - fun foo(function: Function1): A { + fun foo(function: (T) -> T): A { throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. } diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/functionalType.kt b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalType.kt new file mode 100644 index 00000000000..e1699d8679c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalType.kt @@ -0,0 +1,5 @@ +// "Create function 'bar'" "true" + +fun foo(block: (Int) -> String) { + bar(block) +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/call/functionalType.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalType.kt.after new file mode 100644 index 00000000000..6511dbd18f6 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/call/functionalType.kt.after @@ -0,0 +1,9 @@ +// "Create function 'bar'" "true" + +fun foo(block: (Int) -> String) { + bar(block) +} + +fun bar(block: (Int) -> String) { + throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 83a9bae19ea..ddd8065a779 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -1784,6 +1784,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("functionalType.kt") + public void testFunctionalType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/functionalType.kt"); + doTest(fileName); + } + @TestMetadata("inLambda.kt") public void testInLambda() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/inLambda.kt");