Convert to anonymous object: don't remove type arguments

#KT-30208 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-03-28 00:12:35 +09:00
committed by Dmitry Gridin
parent 075620daad
commit f2c7b6b8c4
10 changed files with 60 additions and 4 deletions
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
@@ -54,6 +55,7 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpres
lambda: KtLambdaExpression,
functionDescriptor: FunctionDescriptor,
functionName: String = "",
typeParameters: Map<String, KtTypeReference> = emptyMap(),
replaceElement: (KtNamedFunction) -> KtExpression = { lambda.replaced(it) }
): KtExpression? {
val functionLiteral = lambda.functionLiteral
@@ -76,7 +78,12 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpres
name(functionName)
for (parameter in functionDescriptor.valueParameters) {
val type = parameter.type.let { if (it.isFlexible()) it.makeNotNullable() else it }
param(parameter.name.asString(), typeSourceCode.renderType(type))
val renderType = typeSourceCode.renderType(type)
if (type.isTypeParameter()) {
param(parameter.name.asString(), typeParameters[renderType]?.text ?: renderType)
} else {
param(parameter.name.asString(), renderType)
}
}
functionDescriptor.returnType?.takeIf { !it.isUnit() }?.let {
val lastStatement = bodyExpression.statements.lastOrNull()
@@ -86,7 +93,12 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpres
lastStatement.replace(psiFactory.createExpressionByPattern("return $0", lastStatement))
}
}
returnType(typeSourceCode.renderType(it))
val renderType = typeSourceCode.renderType(it)
if (it.isTypeParameter()) {
returnType(typeParameters[renderType]?.text ?: renderType)
} else {
returnType(renderType)
}
} ?: noReturnType()
blockBody(" " + bodyExpression.text)
}.asString()
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -84,9 +85,14 @@ class SamConversionToAnonymousObjectIntention : SelfTargetingRangeIntention<KtCa
functionName: String
) {
val interfaceName = call.calleeExpression?.text ?: return
LambdaToAnonymousFunctionIntention.convertLambdaToFunction(lambda, functionDescriptor, functionName = functionName) {
val typeArguments = call.typeArguments.mapNotNull { it.typeReference }
val typeArgumentsText =
if (typeArguments.isEmpty()) "" else typeArguments.joinToString(prefix = "<", postfix = ">", separator = ", ") { it.text }
val classDescriptor = functionDescriptor.containingDeclaration as? ClassDescriptor
val typeParameters = classDescriptor?.declaredTypeParameters?.map { it.name.asString() }?.zip(typeArguments)?.toMap().orEmpty()
LambdaToAnonymousFunctionIntention.convertLambdaToFunction(lambda, functionDescriptor, functionName, typeParameters) {
it.addModifier(KtTokens.OVERRIDE_KEYWORD)
call.replaced(KtPsiFactory(it).createExpression("object : $interfaceName { ${it.text} }"))
call.replaced(KtPsiFactory(it).createExpression("object : $interfaceName$typeArgumentsText { ${it.text} }"))
}
}
@@ -0,0 +1,3 @@
public interface A <T, U> {
U foo(T x);
}
@@ -0,0 +1,3 @@
public interface A <T, U> {
U foo(T x);
}
@@ -0,0 +1 @@
val a = <caret>A<Int, String> { "" }
@@ -0,0 +1,5 @@
val a = object : A<Int, String> {
override fun foo(it: Int): String {
return ""
}
}
@@ -0,0 +1,6 @@
// "Convert to anonymous object" "true"
interface B<T, U> {
fun bar(x: T): U
}
val b = <caret>B<Int, String> { "" }
@@ -0,0 +1,10 @@
// "Convert to anonymous object" "true"
interface B<T, U> {
fun bar(x: T): U
}
val b = object : B<Int, String> {
override fun bar(x: Int): String {
return ""
}
}
@@ -16178,6 +16178,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
runTest("idea/testData/intentions/samConversionToAnonymousObject/simple3.kt");
}
@TestMetadata("typeArgument.kt")
public void testTypeArgument() throws Exception {
runTest("idea/testData/intentions/samConversionToAnonymousObject/typeArgument.kt");
}
@TestMetadata("usedSameFunction.kt")
public void testUsedSameFunction() throws Exception {
runTest("idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.kt");
@@ -2499,6 +2499,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
runTest("idea/testData/quickfix/convertToAnonymousObject/simple.kt");
}
@TestMetadata("typeArgument.kt")
public void testTypeArgument() throws Exception {
runTest("idea/testData/quickfix/convertToAnonymousObject/typeArgument.kt");
}
@TestMetadata("unit.kt")
public void testUnit() throws Exception {
runTest("idea/testData/quickfix/convertToAnonymousObject/unit.kt");