KT-18482: "Move lambda argument to parenthesis" action generate uncompilable code fixed (#1226)

* KT-18482 fixed

* Moved code to separate method and changed code to cover few more cases.

* Code style fixes.
This commit is contained in:
Dimach
2017-08-07 16:43:44 +03:00
committed by Dmitry Jemerov
parent a4551fb0fb
commit d624ed4aff
6 changed files with 71 additions and 9 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
@@ -43,7 +44,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isError import org.jetbrains.kotlin.types.isError
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
inline fun <reified T: PsiElement> PsiElement.replaced(newElement: T): T { inline fun <reified T : PsiElement> PsiElement.replaced(newElement: T): T {
val result = replace(newElement) val result = replace(newElement)
return if (result is T) return if (result is T)
result result
@@ -51,7 +52,8 @@ inline fun <reified T: PsiElement> PsiElement.replaced(newElement: T): T {
(result as KtParenthesizedExpression).expression as T (result as KtParenthesizedExpression).expression as T
} }
@Suppress("UNCHECKED_CAST") fun <T: PsiElement> T.copied(): T = copy() as T @Suppress("UNCHECKED_CAST")
fun <T : PsiElement> T.copied(): T = copy() as T
fun KtLambdaArgument.moveInsideParentheses(bindingContext: BindingContext): KtCallExpression { fun KtLambdaArgument.moveInsideParentheses(bindingContext: BindingContext): KtCallExpression {
return moveInsideParenthesesAndReplaceWith(this.getArgumentExpression(), bindingContext) return moveInsideParenthesesAndReplaceWith(this.getArgumentExpression(), bindingContext)
@@ -70,7 +72,8 @@ fun KtLambdaArgument.moveInsideParenthesesAndReplaceWith(
val newCallExpression = oldCallExpression.copy() as KtCallExpression val newCallExpression = oldCallExpression.copy() as KtCallExpression
val psiFactory = KtPsiFactory(project) val psiFactory = KtPsiFactory(project)
val argument = if (newCallExpression.getValueArgumentsInParentheses().any { it.isNamed() }) {
val argument = if (shouldLambdaParameterBeNamed(newCallExpression.getValueArgumentsInParentheses(), oldCallExpression)) {
psiFactory.createArgument(replacement, functionLiteralArgumentName) psiFactory.createArgument(replacement, functionLiteralArgumentName)
} }
else { else {
@@ -92,6 +95,13 @@ fun KtLambdaArgument.moveInsideParenthesesAndReplaceWith(
return oldCallExpression.replace(newCallExpression) as KtCallExpression return oldCallExpression.replace(newCallExpression) as KtCallExpression
} }
private fun shouldLambdaParameterBeNamed(args: List<ValueArgument>, callExpr: KtCallExpression): Boolean {
if (args.any { it.isNamed() }) return true
val calee = (callExpr.calleeExpression?.mainReference?.resolve() as? KtFunction) ?: return true
return if (calee.valueParameters.any { it.isVarArg }) true else calee.valueParameters.size - 1 > args.size
}
fun KtCallExpression.moveFunctionLiteralOutsideParentheses() { fun KtCallExpression.moveFunctionLiteralOutsideParentheses() {
assert(lambdaArguments.isEmpty()) assert(lambdaArguments.isEmpty())
val argumentList = valueArgumentList!! val argumentList = valueArgumentList!!
@@ -169,7 +179,7 @@ fun PsiElement.deleteSingle() {
CodeEditUtil.removeChild(parent?.node ?: return, node ?: return) CodeEditUtil.removeChild(parent?.node ?: return, node ?: return)
} }
fun KtClass.getOrCreateCompanionObject() : KtObjectDeclaration { fun KtClass.getOrCreateCompanionObject(): KtObjectDeclaration {
companionObjects.firstOrNull()?.let { return it } companionObjects.firstOrNull()?.let { return it }
return addDeclaration(KtPsiFactory(this).createCompanionObject()) return addDeclaration(KtPsiFactory(this).createCompanionObject())
} }
@@ -354,11 +364,11 @@ fun KtTypeParameterListOwner.addTypeParameter(typeParameter: KtTypeParameter): K
val list = KtPsiFactory(this).createTypeParameterList("<X>") val list = KtPsiFactory(this).createTypeParameterList("<X>")
list.parameters[0].replace(typeParameter) list.parameters[0].replace(typeParameter)
val leftAnchor = when (this) { val leftAnchor = when (this) {
is KtClass -> nameIdentifier ?: getClassOrInterfaceKeyword() is KtClass -> nameIdentifier ?: getClassOrInterfaceKeyword()
is KtNamedFunction -> funKeyword is KtNamedFunction -> funKeyword
is KtProperty -> valOrVarKeyword is KtProperty -> valOrVarKeyword
else -> null else -> null
} ?: return null } ?: return null
return (addAfter(list, leftAnchor) as KtTypeParameterList).parameters.first() return (addAfter(list, leftAnchor) as KtTypeParameterList).parameters.first()
} }
@@ -0,0 +1,10 @@
// IS_APPLICABLE: true
fun foo() {
bar <caret>{
it * 3
}
}
fun bar(a : Int = 2, b: (Int) -> Int) {
b(a)
}
@@ -0,0 +1,10 @@
// IS_APPLICABLE: true
fun foo() {
bar(b = {
it * 3
})
}
fun bar(a : Int = 2, b: (Int) -> Int) {
b(a)
}
@@ -0,0 +1,10 @@
// IS_APPLICABLE: true
fun foo() {
bar(1) <caret>{
it * 3
}
}
fun bar(c: Int, a: Int = 2, b: (Int) -> Int) {
b(a)
}
@@ -0,0 +1,10 @@
// IS_APPLICABLE: true
fun foo() {
bar(1, b = {
it * 3
})
}
fun bar(c: Int, a: Int = 2, b: (Int) -> Int) {
b(a)
}
@@ -11132,6 +11132,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("moveLambda13.kt")
public void testMoveLambda13() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaInsideParentheses/moveLambda13.kt");
doTest(fileName);
}
@TestMetadata("moveLambda14.kt")
public void testMoveLambda14() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaInsideParentheses/moveLambda14.kt");
doTest(fileName);
}
@TestMetadata("moveLambda2.kt") @TestMetadata("moveLambda2.kt")
public void testMoveLambda2() throws Exception { public void testMoveLambda2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaInsideParentheses/moveLambda2.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaInsideParentheses/moveLambda2.kt");