diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt index 8c54260d450..a4ee3dc28c1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiFactory.kt @@ -63,7 +63,7 @@ public class JetPsiFactory(private val project: Project) { } public fun createExpression(text: String): JetExpression { - return createProperty("val x = $text").getInitializer()!! + return createProperty("val x = $text").getInitializer() ?: error("Failed to create expression from text: '$text'") } public fun createClassLiteral(className: String): JetClassLiteralExpression = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUnparsingUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUnparsingUtils.java deleted file mode 100644 index 3cf86d6ca92..00000000000 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetPsiUnparsingUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.psi; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class JetPsiUnparsingUtils { - private JetPsiUnparsingUtils() { - } - - @NotNull - public static String toBinaryExpression(@Nullable JetExpression left, @NotNull String op, @Nullable JetElement right) { - return toBinaryExpression(JetPsiUtil.getText(left), op, JetPsiUtil.getText(right)); - } - - @NotNull - public static String toBinaryExpression(@NotNull String left, @NotNull String op, @NotNull String right) { - return left + " " + op + " " + right; - } - - @NotNull - public static String parenthesizeIfNeeded(@Nullable JetExpression expression) { - String text = JetPsiUtil.getText(expression); - - return (expression instanceof JetParenthesizedExpression || - expression instanceof JetConstantExpression || - expression instanceof JetSimpleNameExpression || - expression instanceof JetDotQualifiedExpression) - ? text : "(" + text + ")"; - } - - @NotNull - public static String parenthesizeTextIfNeeded(@NotNull String expressionText) { - return (expressionText.startsWith("(") && expressionText.endsWith(")")) ? expressionText : "(" + expressionText + ")"; - } -} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetWhenConditionInRange.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetWhenConditionInRange.java index 0f0052d1687..616f2d227ef 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetWhenConditionInRange.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetWhenConditionInRange.java @@ -52,6 +52,7 @@ public class JetWhenConditionInRange extends JetWhenCondition { return visitor.visitWhenConditionInRange(this, data); } + @NotNull public JetSimpleNameExpression getOperationReference() { return (JetSimpleNameExpression) findChildByType(JetNodeTypes.OPERATION_REFERENCE); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt index 7de9d240438..1bb57317ba6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt @@ -72,6 +72,7 @@ public fun JetPsiFactory.createExpressionByPattern(pattern: String, vararg args: .filter { args[it.getKey()] is String } .flatMap { it.getValue() } .map { it.range } + .filterNot { it.isEmpty() } .sortBy { -it.getStartOffset() } // reformat whole text except for String arguments (as they can contain user's formatting to be preserved) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt index 98ffca33464..cc02be25348 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt @@ -23,41 +23,34 @@ import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.JetPsiUnparsingUtils.parenthesizeIfNeeded -import org.jetbrains.kotlin.psi.JetPsiUnparsingUtils.toBinaryExpression import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.psi.typeRefHelpers.getTypeReference public val TRANSFORM_WITHOUT_CHECK: String = "Expression must be checked before applying transformation" -fun JetWhenCondition.toExpressionText(subject: JetExpression?): String { - return when (this) { +fun JetWhenCondition.toExpression(subject: JetExpression?): JetExpression { + val factory = JetPsiFactory(this) + when (this) { is JetWhenConditionIsPattern -> { val op = if (isNegated()) "!is" else "is" - toBinaryExpression(subject, op, getTypeReference()) + return factory.createExpressionByPattern("$0 $op $1", subject ?: "_", getTypeReference() ?: "") } - is JetWhenConditionInRange -> { - toBinaryExpression(subject, getOperationReference()!!.getText()!!, getRangeExpression()) - } - is JetWhenConditionWithExpression -> { - val conditionExpression = getExpression() - if (subject != null) { - toBinaryExpression(parenthesizeIfNeeded(subject), "==", parenthesizeIfNeeded(conditionExpression)) - } - else { - JetPsiUtil.getText(this) - } - } - else -> { - assert(this is JetWhenConditionWithExpression, TRANSFORM_WITHOUT_CHECK) - val conditionExpression = (this as JetWhenConditionWithExpression).getExpression() - if (subject != null) { - toBinaryExpression(parenthesizeIfNeeded(subject), "==", parenthesizeIfNeeded(conditionExpression)) + is JetWhenConditionInRange -> { + val op = getOperationReference().getText() + return factory.createExpressionByPattern("$0 $op $1", subject ?: "_", getRangeExpression() ?: "") + } + + is JetWhenConditionWithExpression -> { + return if (subject != null) { + factory.createExpressionByPattern("$0 == $1", subject, getExpression() ?: "") } else { - JetPsiUtil.getText(this) + getExpression() } } + + else -> throw IllegalArgumentException("Unknown JetWhenCondition type: $this") } } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/EliminateWhenSubjectIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/EliminateWhenSubjectIntention.kt index d4ad435b7c0..d152ab31f76 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/EliminateWhenSubjectIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/EliminateWhenSubjectIntention.kt @@ -18,7 +18,7 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingIntention -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.toExpressionText +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.toExpression import org.jetbrains.kotlin.psi.JetPsiFactory import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.psi.JetWhenExpression @@ -46,7 +46,7 @@ public class EliminateWhenSubjectIntention : JetSelfTargetingIntention 0) appendFixedText(",") - appendNonFormattedText(condition.toExpressionText(subject)) + appendExpression(condition.toExpression(subject)) } } appendFixedText("->") diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt index 71117d374fa..f18fa347f98 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt @@ -18,7 +18,7 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingIntention -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.toExpressionText +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.toExpression import org.jetbrains.kotlin.psi.* public class WhenToIfIntention : JetSelfTargetingIntention(javaClass(), "Replace 'when' with 'if'") { @@ -32,7 +32,8 @@ public class WhenToIfIntention : JetSelfTargetingIntention(ja } override fun applyTo(element: JetWhenExpression, editor: Editor) { - val ifExpression = JetPsiFactory(element).buildExpression { + val factory = JetPsiFactory(element) + val ifExpression = factory.buildExpression { for ((i, entry) in element.getEntries().withIndex()) { if (i > 0) { appendFixedText("else ") @@ -43,9 +44,9 @@ public class WhenToIfIntention : JetSelfTargetingIntention(ja appendFixedText("\n") } else { - val branchConditionText = combineWhenConditions(entry.getConditions(), element.getSubjectExpression()) + val condition = factory.combineWhenConditions(entry.getConditions(), element.getSubjectExpression()) appendFixedText("if (") - appendNonFormattedText(branchConditionText) + appendExpression(condition) appendFixedText(")") appendExpression(branch) appendFixedText("\n") @@ -56,14 +57,19 @@ public class WhenToIfIntention : JetSelfTargetingIntention(ja element.replace(ifExpression) } - private fun combineWhenConditions(conditions: Array, subject: JetExpression?): String { - return when (conditions.size()) { - 0 -> "" - 1 -> conditions[0].toExpressionText(subject) + private fun JetPsiFactory.combineWhenConditions(conditions: Array, subject: JetExpression?): JetExpression? { + when (conditions.size()) { + 0 -> return null + + 1 -> return conditions[0].toExpression(subject) + else -> { - conditions - .map { condition -> JetPsiUnparsingUtils.parenthesizeTextIfNeeded(condition.toExpressionText(subject)) } - .joinToString(separator = " || ") + return buildExpression { + for ((i, condition) in conditions.withIndex()) { + if (i > 0) appendFixedText("||") + appendExpression(condition.toExpression(subject)) + } + } } } } diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithMultiConditions.kt.after b/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithMultiConditions.kt.after index 677a6a448e5..fa46b51bcf1 100644 --- a/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithMultiConditions.kt.after +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithMultiConditions.kt.after @@ -1,5 +1,5 @@ fun test(n: Int): String { - return if ((n < 0) || (n > 1000)) "unknown" + return if (n < 0 || n > 1000) "unknown" else if (n <= 10) "small" else if (n <= 100) "average" else "big" diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithRangeTestsAndMultiConditions.kt.after b/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithRangeTestsAndMultiConditions.kt.after index 9e918445401..bcd4c1b42d9 100644 --- a/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithRangeTestsAndMultiConditions.kt.after +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/whenWithRangeTestsAndMultiConditions.kt.after @@ -1,6 +1,6 @@ fun test(n: Int): String { - return if ((n in 0..5) || (n in 5..10)) "small" - else if ((n in 10..50) || (n in 50..100)) "average" - else if ((n in 100..500) || (n in 500..1000)) "big" + return if (n in 0..5 || n in 5..10) "small" + else if (n in 10..50 || n in 50..100) "average" + else if (n in 100..500 || n in 500..1000) "big" else "unknown" } \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoEnd.kt b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoEnd.kt new file mode 100644 index 00000000000..df2af6deb72 --- /dev/null +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoEnd.kt @@ -0,0 +1,7 @@ +fun test(n: Int): String { + return when (n) { + is -> "String" + in -> "1..10" + else -> "unknown" + } +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoEnd.kt.after b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoEnd.kt.after new file mode 100644 index 00000000000..9f4e369e145 --- /dev/null +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoEnd.kt.after @@ -0,0 +1,5 @@ +fun test(n: Int): String { + return if (n is ) "String" + else if (n in) "1..10" + else "unknown" +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoSubject.kt b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoSubject.kt new file mode 100644 index 00000000000..250b8f5a7c3 --- /dev/null +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoSubject.kt @@ -0,0 +1,10 @@ +// ERROR: Expected condition of type kotlin.Boolean +// ERROR: Expected condition of type kotlin.Boolean + +fun test(n: Int): String { + return when { + is String -> "String" + in 1..10 -> "1..10" + else -> "unknown" + } +} \ No newline at end of file diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoSubject.kt.after b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoSubject.kt.after new file mode 100644 index 00000000000..e5de39e3fec --- /dev/null +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoSubject.kt.after @@ -0,0 +1,8 @@ +// ERROR: Expected condition of type kotlin.Boolean +// ERROR: Expected condition of type kotlin.Boolean + +fun test(n: Int): String { + return if (_ is String) "String" + else if (_ in 1..10) "1..10" + else "unknown" +} \ 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 500d33dcaa6..6f98237cdd0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -1732,6 +1732,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/branched/ifWhen/whenToIf/whenWithoutSubject.kt"); doTest(fileName); } + + @TestMetadata("wrongIsAndInNoEnd.kt") + public void testWrongIsAndInNoEnd() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoEnd.kt"); + doTest(fileName); + } + + @TestMetadata("wrongIsAndInNoSubject.kt") + public void testWrongIsAndInNoSubject() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/branched/ifWhen/whenToIf/wrongIsAndInNoSubject.kt"); + doTest(fileName); + } } }