From e4a1c8dcef6d8734bf8005efa670249d14d4be8a Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 5 Jun 2020 10:47:42 +0900 Subject: [PATCH] Anonymous function to lambda: add lambda parameter if type parameter is used, even if parameter is unused #KT-39393 Fixed --- .../AnonymousFunctionToLambdaIntention.kt | 26 ++++++++++++------- .../typeParameterWithUnusedParameter.kt | 6 +++++ .../typeParameterWithUnusedParameter.kt.after | 5 ++++ .../typeParameterWithUnusedParameter2.kt | 6 +++++ ...typeParameterWithUnusedParameter2.kt.after | 5 ++++ .../typeParameterWithVararg.kt | 9 +++++++ .../typeParameterWithVararg.kt.after | 7 +++++ .../intentions/IntentionTestGenerated.java | 15 +++++++++++ 8 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt create mode 100644 idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt.after create mode 100644 idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt create mode 100644 idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt.after create mode 100644 idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt create mode 100644 idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AnonymousFunctionToLambdaIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AnonymousFunctionToLambdaIntention.kt index a52de9af71d..4c091da161a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/AnonymousFunctionToLambdaIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AnonymousFunctionToLambdaIntention.kt @@ -9,7 +9,6 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.util.TextRange import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.searches.ReferencesSearch -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.core.getLastLambdaExpression @@ -23,6 +22,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument +import org.jetbrains.kotlin.resolve.calls.components.isVararg +import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance class AnonymousFunctionToLambdaIntention : SelfTargetingRangeIntention( @@ -46,10 +47,18 @@ class AnonymousFunctionToLambdaIntention : SelfTargetingRangeIntention() ?: return val callElement = argument.getStrictParentOfType() ?: return - val functionParameterTypes = if (callElement.typeArgumentList == null) { - callElement.resolveToCall()?.getParameterForArgument(argument)?.original?.type?.arguments.orEmpty() + val typeParameterIndexes = if (callElement.typeArgumentList == null) { + val functionalType = callElement.resolveToCall()?.getParameterForArgument(argument)?.let { + if (it.isVararg) it.original.type.arguments.firstOrNull()?.type else it.original.type + } + val typeArguments = functionalType?.arguments?.let { + if (it.isNotEmpty()) it.dropLast(1) else it + }.orEmpty() + typeArguments.mapIndexedNotNull { index, typeProjection -> + if (typeProjection.type.isTypeParameter()) index else null + }.toSet() } else { - emptyList() + emptySet() } val commentSaver = CommentSaver(element) @@ -62,18 +71,17 @@ class AnonymousFunctionToLambdaIntention : SelfTargetingRangeIntention 1 || parameters.any { parameter -> ReferencesSearch.search(parameter, LocalSearchScope(body)).any() } + val needParameters = typeParameterIndexes.isNotEmpty() || parameters.count() > 1 || parameters.any { parameter -> + ReferencesSearch.search(parameter, LocalSearchScope(body)).any() + } if (needParameters) { parameters.forEachIndexed { index, parameter -> if (index > 0) { appendFixedText(",") } appendName(parameter.nameAsSafeName) - val needParameterType = - functionParameterTypes.getOrNull(index)?.type?.constructor?.declarationDescriptor is TypeParameterDescriptor val typeReference = parameter.typeReference - if (needParameterType && typeReference != null) { + if (typeReference != null && index in typeParameterIndexes) { appendFixedText(": ") appendTypeReference(typeReference) } diff --git a/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt new file mode 100644 index 00000000000..7abfac3d542 --- /dev/null +++ b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt @@ -0,0 +1,6 @@ +fun foo(fn: (T) -> Unit) {} + +fun test() { + foo(fun(x: String) { + }) +} \ No newline at end of file diff --git a/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt.after b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt.after new file mode 100644 index 00000000000..b5f299e1c8a --- /dev/null +++ b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt.after @@ -0,0 +1,5 @@ +fun foo(fn: (T) -> Unit) {} + +fun test() { + foo { x: String -> } +} \ No newline at end of file diff --git a/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt new file mode 100644 index 00000000000..26690318984 --- /dev/null +++ b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt @@ -0,0 +1,6 @@ +fun foo(fn: (String) -> T) {} + +fun test() { + foo(fun(x: String) { + }) +} \ No newline at end of file diff --git a/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt.after b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt.after new file mode 100644 index 00000000000..cd0ecd0328e --- /dev/null +++ b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt.after @@ -0,0 +1,5 @@ +fun foo(fn: (String) -> T) {} + +fun test() { + foo { } +} \ No newline at end of file diff --git a/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt new file mode 100644 index 00000000000..2fcfcea09ae --- /dev/null +++ b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt @@ -0,0 +1,9 @@ +fun foo(p: String) {} + +fun bar(vararg fn: (T) -> Unit) {} + +fun test() { + bar(fun(x: String) { + foo(x) + }) +} \ No newline at end of file diff --git a/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt.after b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt.after new file mode 100644 index 00000000000..885bf1073b4 --- /dev/null +++ b/idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt.after @@ -0,0 +1,7 @@ +fun foo(p: String) {} + +fun bar(vararg fn: (T) -> Unit) {} + +fun test() { + bar({ x: String -> foo(x) }) +} \ 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 e84426b1f15..756dc590dfe 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -2115,6 +2115,21 @@ public class IntentionTestGenerated extends AbstractIntentionTest { public void testTypeParameter3() throws Exception { runTest("idea/testData/intentions/anonymousFunctionToLambda/typeParameter3.kt"); } + + @TestMetadata("typeParameterWithUnusedParameter.kt") + public void testTypeParameterWithUnusedParameter() throws Exception { + runTest("idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter.kt"); + } + + @TestMetadata("typeParameterWithUnusedParameter2.kt") + public void testTypeParameterWithUnusedParameter2() throws Exception { + runTest("idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithUnusedParameter2.kt"); + } + + @TestMetadata("typeParameterWithVararg.kt") + public void testTypeParameterWithVararg() throws Exception { + runTest("idea/testData/intentions/anonymousFunctionToLambda/typeParameterWithVararg.kt"); + } } @TestMetadata("idea/testData/intentions/branched")