diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetFunctionLiteralArgument.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetFunctionLiteralArgument.kt index 0248deb1d9b..7fbf4262953 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetFunctionLiteralArgument.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetFunctionLiteralArgument.kt @@ -21,19 +21,18 @@ import com.intellij.psi.PsiWhiteSpace public class JetFunctionLiteralArgument(node: ASTNode) : JetValueArgument(node), FunctionLiteralArgument { - private fun assertFL() = throw AssertionError("Function literal argument doesn't contain function literal expression: " + - "${super.getArgumentExpression()?.getText()} (it should be guaranteed by parser)") + override fun getArgumentExpression() = super.getArgumentExpression()!! - override fun getArgumentExpression() = super.getArgumentExpression() ?: assertFL() - - override fun getFunctionLiteral(): JetFunctionLiteralExpression = unpackFunctionLiteral(getArgumentExpression()) - - private fun unpackFunctionLiteral(expression: JetExpression?): JetFunctionLiteralExpression = - when (expression) { - is JetFunctionLiteralExpression -> expression - is JetLabeledExpression -> unpackFunctionLiteral(expression.getBaseExpression()) - is JetAnnotatedExpression -> unpackFunctionLiteral(expression.getBaseExpression()) - else -> assertFL() - } + override fun getFunctionLiteral(): JetFunctionLiteralExpression = getArgumentExpression().unpackFunctionLiteral()!! } +public fun JetExpression.unpackFunctionLiteral(): JetFunctionLiteralExpression? { + return when (this) { + is JetFunctionLiteralExpression -> this + is JetLabeledExpression -> getBaseExpression()?.unpackFunctionLiteral() + is JetAnnotatedExpression -> getBaseExpression()?.unpackFunctionLiteral() + else -> null + } +} + + diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt index 37a333c60f6..49b4870b03a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt @@ -95,7 +95,10 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: CodeEditUtil.setNodeGeneratedRecursively(expression.getNode(), false) for ((pointer, n) in pointers) { - val element = pointer.getElement()!! + var element = pointer.getElement()!! + if (element is JetFunctionLiteral) { + element = element.getParent() as JetFunctionLiteralExpression + } element.replace(args[n] as PsiElement) } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt index 3d547b2f051..ed2b05594b3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt @@ -20,7 +20,7 @@ import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveLambdaOutsideParentheses +import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveFunctionLiteralOutsideParentheses import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext @@ -29,9 +29,10 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode public class MoveLambdaOutsideParenthesesIntention : JetSelfTargetingIntention(javaClass(), "Move lambda argument out of parentheses") { override fun isApplicableTo(element: JetCallExpression, caretOffset: Int): Boolean { - val argument = element.getValueArgumentsInParentheses().lastOrNull() ?: return false + if (element.getFunctionLiteralArguments().isNotEmpty()) return false + val argument = element.getValueArguments().lastOrNull() ?: return false val expression = argument.getArgumentExpression() ?: return false - val functionLiteral = getFunctionLiteral(expression) ?: return false + val functionLiteral = expression.unpackFunctionLiteral() ?: return false val callee = element.getCalleeExpression() if (callee is JetSimpleNameExpression) { @@ -54,15 +55,7 @@ public class MoveLambdaOutsideParenthesesIntention : JetSelfTargetingIntention expression.getFunctionLiteral() - is JetLabeledExpression -> getFunctionLiteral(expression.getBaseExpression()) - else -> null - } - } - override fun applyTo(element: JetCallExpression, editor: Editor) { - element.moveLambdaOutsideParentheses() + element.moveFunctionLiteralOutsideParentheses() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java index 2ebaf301ac3..c441b9d62a7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java @@ -391,8 +391,9 @@ public class JetFunctionCallUsage extends JetUsageInfo { element.deleteChildRange(KotlinPackage.first(lambdaArguments), KotlinPackage.last(lambdaArguments)); } + //TODO: this is not correct! JetValueArgument lastArgument = KotlinPackage.lastOrNull(newArgumentList.getArguments()); - boolean hasTrailingLambdaInArgumentListAfter = lastArgument != null && lastArgument.getArgumentExpression() instanceof JetFunctionLiteralExpression; + boolean hasTrailingLambdaInArgumentListAfter = lastArgument != null && PsiPackage.unpackFunctionLiteral(lastArgument.getArgumentExpression()) != null; arguments = (JetValueArgumentList) arguments.replace(newArgumentList); @@ -430,11 +431,11 @@ public class JetFunctionCallUsage extends JetUsageInfo { } if (hasTrailingLambdaInArgumentListAfter) { - JetCallElement newCallElement = - (JetCallElement) (newElement instanceof JetQualifiedExpression + JetCallExpression newCallExpression = + (JetCallExpression) (newElement instanceof JetQualifiedExpression ? ((JetQualifiedExpression) newElement).getSelectorExpression() : newElement); - PsiModificationUtilPackage.moveLambdaOutsideParentheses(newCallElement); + PsiModificationUtilPackage.moveFunctionLiteralOutsideParentheses(newCallExpression); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt b/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt index ad320366ef0..19c3e7f115b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/psiModificationUtil.kt @@ -54,7 +54,7 @@ fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith( psiFactory.createArgument(replacement) } - val functionLiteralArgument = newCallExpression.getFunctionLiteralArguments().head!! + val functionLiteralArgument = newCallExpression.getFunctionLiteralArguments().firstOrNull()!! val valueArgumentList = newCallExpression.getValueArgumentList() ?: psiFactory.createCallArguments("()") valueArgumentList.addArgument(argument) @@ -69,20 +69,20 @@ fun JetFunctionLiteralArgument.moveInsideParenthesesAndReplaceWith( return oldCallExpression.replace(newCallExpression) as JetCallExpression } -fun JetCallElement.moveLambdaOutsideParentheses() { - val args = getValueArgumentsInParentheses() - val functionLiteral = args.last!!.getArgumentExpression()?.getText() - val calleeText = getCalleeExpression()?.getText() - if (calleeText == null || functionLiteral == null) return +fun JetCallExpression.moveFunctionLiteralOutsideParentheses() { + assert(getFunctionLiteralArguments().isEmpty()) + val argumentList = getValueArgumentList()!! + val argument = argumentList.getArguments().last() + val expression = argument.getArgumentExpression()!! + assert(expression.unpackFunctionLiteral() != null) - val params = args.subList(0, args.size - 1).map { it.asElement().getText() ?: "" }.joinToString(", ", "(", ")") - - val newCall = - if (params == "()") { - "$calleeText $functionLiteral" - } - else { - "$calleeText$params $functionLiteral" - } - replace(JetPsiFactory(this).createExpression(newCall)) + val dummyCall = JetPsiFactory(this).createExpressionByPattern("foo()$0={}$", expression) as JetCallExpression + val functionLiteralArgument = dummyCall.getFunctionLiteralArguments().single() + this.add(functionLiteralArgument) + if (argumentList.getArguments().size() > 1) { + argumentList.removeArgument(argument) + } + else { + argumentList.delete() + } } \ No newline at end of file diff --git a/idea/testData/intentions/moveLambdaOutsideParentheses/ambigousOverload.kt.after b/idea/testData/intentions/moveLambdaOutsideParentheses/ambigousOverload.kt.after index 553d7079b70..4582e73279d 100644 --- a/idea/testData/intentions/moveLambdaOutsideParentheses/ambigousOverload.kt.after +++ b/idea/testData/intentions/moveLambdaOutsideParentheses/ambigousOverload.kt.after @@ -3,7 +3,7 @@ // ERROR: Unresolved reference: it fun foo() { - bar { it } + bar { it } } fun bar(a: Int = 0, f: (Int) -> Int) { } diff --git a/idea/testData/intentions/moveLambdaOutsideParentheses/functionalValueCall.kt.after b/idea/testData/intentions/moveLambdaOutsideParentheses/functionalValueCall.kt.after index d2425d2c89d..c11566bcac9 100644 --- a/idea/testData/intentions/moveLambdaOutsideParentheses/functionalValueCall.kt.after +++ b/idea/testData/intentions/moveLambdaOutsideParentheses/functionalValueCall.kt.after @@ -1,3 +1,3 @@ fun foo(p: (Int, () -> Int) -> Unit) { - p(1) { 2 } + p(1) { 2 } } diff --git a/idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableAlreadyHasFunctionLiteral.kt b/idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableAlreadyHasFunctionLiteral.kt new file mode 100644 index 00000000000..ecf6245bccd --- /dev/null +++ b/idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableAlreadyHasFunctionLiteral.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo() { + bar({ it }) {it} +} + +fun bar(p1: (Int) -> Int, p2: (Int) -> Int) { } diff --git a/idea/testData/intentions/moveLambdaOutsideParentheses/lambdaWithCommas3.kt.after b/idea/testData/intentions/moveLambdaOutsideParentheses/lambdaWithCommas3.kt.after index 6587e445215..f7dc6e8fb75 100644 --- a/idea/testData/intentions/moveLambdaOutsideParentheses/lambdaWithCommas3.kt.after +++ b/idea/testData/intentions/moveLambdaOutsideParentheses/lambdaWithCommas3.kt.after @@ -3,7 +3,7 @@ fun bar(a: Int, b: Int, f: () -> Unit) {} fun foo(a: Int, b: Int) = 2 fun test() { - bar(1, 2) { + bar(1, 2 /* , , */) { val a = foo(1, 2) } } \ 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 48620a1fb4f..1ff4fb30dba 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -4951,6 +4951,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("inapplicableAlreadyHasFunctionLiteral.kt") + public void testInapplicableAlreadyHasFunctionLiteral() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableAlreadyHasFunctionLiteral.kt"); + doTest(fileName); + } + @TestMetadata("inapplicableOptionalParametersAfter.kt") public void testInapplicableOptionalParametersAfter() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/moveLambdaOutsideParentheses/inapplicableOptionalParametersAfter.kt");