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.bindingContextUtil.getTargetFunctionDescriptor
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.isFlexible 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.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
@@ -54,6 +55,7 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpres
lambda: KtLambdaExpression, lambda: KtLambdaExpression,
functionDescriptor: FunctionDescriptor, functionDescriptor: FunctionDescriptor,
functionName: String = "", functionName: String = "",
typeParameters: Map<String, KtTypeReference> = emptyMap(),
replaceElement: (KtNamedFunction) -> KtExpression = { lambda.replaced(it) } replaceElement: (KtNamedFunction) -> KtExpression = { lambda.replaced(it) }
): KtExpression? { ): KtExpression? {
val functionLiteral = lambda.functionLiteral val functionLiteral = lambda.functionLiteral
@@ -76,7 +78,12 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpres
name(functionName) name(functionName)
for (parameter in functionDescriptor.valueParameters) { for (parameter in functionDescriptor.valueParameters) {
val type = parameter.type.let { if (it.isFlexible()) it.makeNotNullable() else it } 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 { functionDescriptor.returnType?.takeIf { !it.isUnit() }?.let {
val lastStatement = bodyExpression.statements.lastOrNull() val lastStatement = bodyExpression.statements.lastOrNull()
@@ -86,7 +93,12 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpres
lastStatement.replace(psiFactory.createExpressionByPattern("return $0", lastStatement)) 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() } ?: noReturnType()
blockBody(" " + bodyExpression.text) blockBody(" " + bodyExpression.text)
}.asString() }.asString()
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.LowPriorityAction import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -84,9 +85,14 @@ class SamConversionToAnonymousObjectIntention : SelfTargetingRangeIntention<KtCa
functionName: String functionName: String
) { ) {
val interfaceName = call.calleeExpression?.text ?: return 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) 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"); 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") @TestMetadata("usedSameFunction.kt")
public void testUsedSameFunction() throws Exception { public void testUsedSameFunction() throws Exception {
runTest("idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.kt"); runTest("idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.kt");
@@ -2499,6 +2499,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
runTest("idea/testData/quickfix/convertToAnonymousObject/simple.kt"); 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") @TestMetadata("unit.kt")
public void testUnit() throws Exception { public void testUnit() throws Exception {
runTest("idea/testData/quickfix/convertToAnonymousObject/unit.kt"); runTest("idea/testData/quickfix/convertToAnonymousObject/unit.kt");