Adding block to control structure when needed

This commit is contained in:
Valentin Kipyatkov
2016-10-22 13:29:23 +03:00
parent a4aa9bab8d
commit 8f9f2027f2
5 changed files with 104 additions and 8 deletions
@@ -18,4 +18,7 @@ package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode
class KtContainerNodeForControlStructureBody(node: ASTNode) : KtContainerNode(node)
class KtContainerNodeForControlStructureBody(node: ASTNode) : KtContainerNode(node) {
val expression: KtExpression?
get() = findChildByClass<KtExpression>(KtExpression::class.java)
}
@@ -16,9 +16,11 @@
package org.jetbrains.kotlin.idea.replacement
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import java.util.*
@@ -95,11 +97,36 @@ internal class ExpressionReplacementPerformer(
* Returns statement in a block to insert statement before it
*/
private fun findOrCreateBlockToInsertStatement(): KtExpression {
//TODO: 1. There can be no block above at all
//TODO: 2. Create block for control statements without block
//TODO: 3. Sometimes it's not correct because of side effects
return elementToBeReplaced.parentsWithSelf
.filterIsInstance<KtExpression>()
.firstOrNull { it.parent is KtBlockExpression }!!
//TODO: Convert expression function body into block body
//TODO: Sometimes it's not correct because of side effects
for (element in elementToBeReplaced.parentsWithSelf) {
val parent = element.parent
when (element) {
is KtContainerNodeForControlStructureBody -> { // control statement without block
return element.expression!!.replaceWithBlock()
}
is KtExpression -> {
if (parent is KtWhenEntry) { // when entry without block
return element.replaceWithBlock()
}
if (parent is KtBlockExpression) return element
}
}
}
//TODO
throw UnsupportedOperationException()
}
}
private fun KtExpression.replaceWithBlock(): KtExpression {
elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, Unit)
val blockExpression = this.replaced(KtPsiFactory(this).createSingleStatementBlock(this))
elementToBeReplaced = blockExpression.findDescendantOfType<KtExpression> { it.getCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY) != null }!!
return blockExpression.statements.single()
}
}
private val ELEMENT_TO_BE_REPLACED_KEY = Key<Unit>("ELEMENT_TO_BE_REPLACED_KEY")
@@ -0,0 +1,23 @@
fun <caret>f(p1: Int, p2: Int): Int {
println(p1)
println(p2)
return p1 + p2
}
fun main(args: Array<String>) {
if (args.size > 0)
f(1, 2)
else
f(3, 4)
for (i in 1..10)
f(0, 1)
when (args.size) {
0, 1 -> println(f(1, 1))
else -> println(f(2, 2))
}
while (true)
println(f(5, 6))
}
@@ -0,0 +1,37 @@
fun main(args: Array<String>) {
if (args.size > 0) {
println(1)
println(2)
1 + 2
}
else {
println(3)
println(4)
3 + 4
}
for (i in 1..10) {
println(0)
println(1)
0 + 1
}
when (args.size) {
0, 1 -> {
println(1)
println(1)
println(1 + 1)
}
else -> {
println(2)
println(2)
println(2 + 2)
}
}
while (true) {
println(5)
println(6)
println(5 + 6)
}
}
@@ -98,6 +98,12 @@ public class InlineTestGenerated extends AbstractInlineTest {
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ReturnAtEnd extends AbstractInlineTest {
@TestMetadata("AddBlockToControlStatement.kt")
public void testAddBlockToControlStatement() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/returnAtEnd/AddBlockToControlStatement.kt");
doTest(fileName);
}
public void testAllFilesPresentInReturnAtEnd() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/inline/function/returnAtEnd"), Pattern.compile("^(\\w+)\\.kt$"), true);
}