Anonymous function to lambda: add lambda parameter if type parameter is used, even if parameter is unused
#KT-39393 Fixed
This commit is contained in:
committed by
igoriakovlev
parent
311b2d7969
commit
e4a1c8dcef
+17
-9
@@ -9,7 +9,6 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
import org.jetbrains.kotlin.idea.core.getLastLambdaExpression
|
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.psi.psiUtil.parents
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument
|
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
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
|
|
||||||
class AnonymousFunctionToLambdaIntention : SelfTargetingRangeIntention<KtNamedFunction>(
|
class AnonymousFunctionToLambdaIntention : SelfTargetingRangeIntention<KtNamedFunction>(
|
||||||
@@ -46,10 +47,18 @@ class AnonymousFunctionToLambdaIntention : SelfTargetingRangeIntention<KtNamedFu
|
|||||||
override fun applyTo(element: KtNamedFunction, editor: Editor?) {
|
override fun applyTo(element: KtNamedFunction, editor: Editor?) {
|
||||||
val argument = element.getStrictParentOfType<KtValueArgument>() ?: return
|
val argument = element.getStrictParentOfType<KtValueArgument>() ?: return
|
||||||
val callElement = argument.getStrictParentOfType<KtCallElement>() ?: return
|
val callElement = argument.getStrictParentOfType<KtCallElement>() ?: return
|
||||||
val functionParameterTypes = if (callElement.typeArgumentList == null) {
|
val typeParameterIndexes = if (callElement.typeArgumentList == null) {
|
||||||
callElement.resolveToCall()?.getParameterForArgument(argument)?.original?.type?.arguments.orEmpty()
|
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 {
|
} else {
|
||||||
emptyList()
|
emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
val commentSaver = CommentSaver(element)
|
val commentSaver = CommentSaver(element)
|
||||||
@@ -62,18 +71,17 @@ class AnonymousFunctionToLambdaIntention : SelfTargetingRangeIntention<KtNamedFu
|
|||||||
|
|
||||||
val parameters = element.valueParameters
|
val parameters = element.valueParameters
|
||||||
|
|
||||||
val needParameters =
|
val needParameters = typeParameterIndexes.isNotEmpty() || parameters.count() > 1 || parameters.any { parameter ->
|
||||||
parameters.count() > 1 || parameters.any { parameter -> ReferencesSearch.search(parameter, LocalSearchScope(body)).any() }
|
ReferencesSearch.search(parameter, LocalSearchScope(body)).any()
|
||||||
|
}
|
||||||
if (needParameters) {
|
if (needParameters) {
|
||||||
parameters.forEachIndexed { index, parameter ->
|
parameters.forEachIndexed { index, parameter ->
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
appendFixedText(",")
|
appendFixedText(",")
|
||||||
}
|
}
|
||||||
appendName(parameter.nameAsSafeName)
|
appendName(parameter.nameAsSafeName)
|
||||||
val needParameterType =
|
|
||||||
functionParameterTypes.getOrNull(index)?.type?.constructor?.declarationDescriptor is TypeParameterDescriptor
|
|
||||||
val typeReference = parameter.typeReference
|
val typeReference = parameter.typeReference
|
||||||
if (needParameterType && typeReference != null) {
|
if (typeReference != null && index in typeParameterIndexes) {
|
||||||
appendFixedText(": ")
|
appendFixedText(": ")
|
||||||
appendTypeReference(typeReference)
|
appendTypeReference(typeReference)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun <T> foo(fn: (T) -> Unit) {}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(<caret>fun(x: String) {
|
||||||
|
})
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun <T> foo(fn: (T) -> Unit) {}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo { x: String -> }
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun <T> foo(fn: (String) -> T) {}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(<caret>fun(x: String) {
|
||||||
|
})
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun <T> foo(fn: (String) -> T) {}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fun foo(p: String) {}
|
||||||
|
|
||||||
|
fun <T> bar(vararg fn: (T) -> Unit) {}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
bar(<caret>fun(x: String) {
|
||||||
|
foo(x)
|
||||||
|
})
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(p: String) {}
|
||||||
|
|
||||||
|
fun <T> bar(vararg fn: (T) -> Unit) {}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
bar({ x: String -> foo(x) })
|
||||||
|
}
|
||||||
@@ -2115,6 +2115,21 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
public void testTypeParameter3() throws Exception {
|
public void testTypeParameter3() throws Exception {
|
||||||
runTest("idea/testData/intentions/anonymousFunctionToLambda/typeParameter3.kt");
|
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")
|
@TestMetadata("idea/testData/intentions/branched")
|
||||||
|
|||||||
Reference in New Issue
Block a user