From 2fd3af73ebef9e639de04f3a841c0e793811889a Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 12 Jun 2020 17:48:47 +0900 Subject: [PATCH] Convert reference to lambda: fix it works correctly when referenced function has default argument #KT-17222 Fixed #KT-24138 Fixed #KT-39532 --- .../ConvertReferenceToLambdaIntention.kt | 35 +++++++++++-------- .../referencedFunctionWithDefaultArugment.kt | 7 ++++ ...rencedFunctionWithDefaultArugment.kt.after | 7 ++++ .../referencedFunctionWithDefaultArugment2.kt | 7 ++++ ...encedFunctionWithDefaultArugment2.kt.after | 7 ++++ .../referencedFunctionWithDefaultArugment3.kt | 7 ++++ ...encedFunctionWithDefaultArugment3.kt.after | 7 ++++ .../intentions/IntentionTestGenerated.java | 15 ++++++++ 8 files changed, 77 insertions(+), 15 deletions(-) create mode 100644 idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt create mode 100644 idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt.after create mode 100644 idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt create mode 100644 idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt.after create mode 100644 idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt create mode 100644 idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertReferenceToLambdaIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertReferenceToLambdaIntention.kt index 7469e885483..e4ec9319c59 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertReferenceToLambdaIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertReferenceToLambdaIntention.kt @@ -37,27 +37,32 @@ class ConvertReferenceToLambdaIntention : SelfTargetingOffsetIndependentIntentio val context = element.analyze(BodyResolveMode.PARTIAL) val reference = element.callableReference val targetDescriptor = context[REFERENCE_TARGET, reference] as? CallableMemberDescriptor ?: return - val parameterNamesAndTypes = targetDescriptor.valueParameters.map { it.name.asString() to it.type } - val receiverExpression = element.receiverExpression - val receiverType = receiverExpression?.let { - (context[DOUBLE_COLON_LHS, it] as? DoubleColonLHS.Type)?.type - } - - val receiverNameAndType = receiverType?.let { - KotlinNameSuggester.suggestNamesByType(it, validator = { name -> - name !in parameterNamesAndTypes.map { pair -> pair.first } - }, defaultName = "receiver").first() to it - } - val valueArgumentParent = element.parent as? KtValueArgument val callGrandParent = valueArgumentParent?.parent?.parent as? KtCallExpression val resolvedCall = callGrandParent?.getResolvedCall(context) val matchingParameterType = resolvedCall?.getParameterForArgument(valueArgumentParent)?.type val matchingParameterIsExtension = matchingParameterType?.isExtensionFunctionType ?: false - val acceptsReceiverAsParameter = receiverNameAndType != null && !matchingParameterIsExtension && - (targetDescriptor.dispatchReceiverParameter != null || - targetDescriptor.extensionReceiverParameter != null) + val receiverExpression = element.receiverExpression + val receiverType = receiverExpression?.let { + (context[DOUBLE_COLON_LHS, it] as? DoubleColonLHS.Type)?.type + } + val acceptsReceiverAsParameter = receiverType != null && !matchingParameterIsExtension && + (targetDescriptor.dispatchReceiverParameter != null || targetDescriptor.extensionReceiverParameter != null) + + val parameterNamesAndTypes = targetDescriptor.valueParameters.map { it.name.asString() to it.type }.let { + if (matchingParameterType != null) { + val parameterSize = matchingParameterType.arguments.size - (if (acceptsReceiverAsParameter) 2 else 1) + if (parameterSize >= 0) it.take(parameterSize) else it + } else { + it + } + } + val receiverNameAndType = receiverType?.let { + KotlinNameSuggester.suggestNamesByType(it, validator = { name -> + name !in parameterNamesAndTypes.map { pair -> pair.first } + }, defaultName = "receiver").first() to it + } val factory = KtPsiFactory(element) val targetName = reference.text diff --git a/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt new file mode 100644 index 00000000000..952bace1693 --- /dev/null +++ b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt @@ -0,0 +1,7 @@ +fun test() { + foo1(::convert2) +} + +fun foo1(convert: (Int) -> Unit) {} + +fun convert2(i: Int, j: Int = 0) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt.after b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt.after new file mode 100644 index 00000000000..fef965967fb --- /dev/null +++ b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt.after @@ -0,0 +1,7 @@ +fun test() { + foo1 { convert2(it) } +} + +fun foo1(convert: (Int) -> Unit) {} + +fun convert2(i: Int, j: Int = 0) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt new file mode 100644 index 00000000000..684c6237b1e --- /dev/null +++ b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt @@ -0,0 +1,7 @@ +fun test() { + foo2(::convert3) +} + +fun foo2(convert: (Int, Int) -> Unit) {} + +fun convert3(i: Int, j: Int, k: Int = 0) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt.after b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt.after new file mode 100644 index 00000000000..9a1dfaaeeea --- /dev/null +++ b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt.after @@ -0,0 +1,7 @@ +fun test() { + foo2 { i, j -> convert3(i, j) } +} + +fun foo2(convert: (Int, Int) -> Unit) {} + +fun convert3(i: Int, j: Int, k: Int = 0) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt new file mode 100644 index 00000000000..def0e7084dd --- /dev/null +++ b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt @@ -0,0 +1,7 @@ +fun test() { + bar(Int::foo) +} + +fun Int.foo(x: Int, y: Int = 42) = x + y + +fun bar(f: (Int, Int) -> Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt.after b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt.after new file mode 100644 index 00000000000..5805b6efd75 --- /dev/null +++ b/idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt.after @@ -0,0 +1,7 @@ +fun test() { + bar { i, x -> i.foo(x) } +} + +fun Int.foo(x: Int, y: Int = 42) = x + y + +fun bar(f: (Int, Int) -> Int) {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 6c41aecf52d..7c8686cd87e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -6500,6 +6500,21 @@ public class IntentionTestGenerated extends AbstractIntentionTest { runTest("idea/testData/intentions/convertReferenceToLambda/receiverParameter.kt"); } + @TestMetadata("referencedFunctionWithDefaultArugment.kt") + public void testReferencedFunctionWithDefaultArugment() throws Exception { + runTest("idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment.kt"); + } + + @TestMetadata("referencedFunctionWithDefaultArugment2.kt") + public void testReferencedFunctionWithDefaultArugment2() throws Exception { + runTest("idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment2.kt"); + } + + @TestMetadata("referencedFunctionWithDefaultArugment3.kt") + public void testReferencedFunctionWithDefaultArugment3() throws Exception { + runTest("idea/testData/intentions/convertReferenceToLambda/referencedFunctionWithDefaultArugment3.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("idea/testData/intentions/convertReferenceToLambda/simple.kt");