KT-6798 J2K: Needless use of run {} function after converting for-loop

#KT-6798 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-03-26 19:04:05 +03:00
parent 46193f3ef7
commit 6dbb13c32d
28 changed files with 373 additions and 113 deletions
@@ -18,7 +18,10 @@ package org.jetbrains.kotlin.j2k
import com.intellij.psi.*
import com.intellij.psi.tree.IElementType
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.siblings
class ForConverter(
private val statement: PsiForStatement,
@@ -101,23 +104,44 @@ class ForConverter(
statement.isInSingleLine()).assignNoPrototype()
if (initializationConverted.isEmpty) return whileStatement
//TODO: we could omit "run { ... }" when it won't cause any name conflicts
return RunBlockWithLoopStatement(initializationConverted, whileStatement)
val kind = if (statement.parents(withItself = false).filter { it !is PsiLabeledStatement }.first() !is PsiCodeBlock) {
WhileWithInitializationPseudoStatement.Kind.WITH_BLOCK
}
else if (hasNameConflict())
WhileWithInitializationPseudoStatement.Kind.WITH_RUN_BLOCK
else
WhileWithInitializationPseudoStatement.Kind.SIMPLE
return WhileWithInitializationPseudoStatement(initializationConverted, whileStatement, kind)
}
public class RunBlockWithLoopStatement(
public class WhileWithInitializationPseudoStatement(
public val initialization: Statement,
public val loop: Statement
public val loop: Statement,
public val kind: WhileWithInitializationPseudoStatement.Kind
) : Statement() {
private val methodCall = run {
val statements = listOf(initialization, loop)
val block = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block))
public enum class Kind {
SIMPLE
WITH_BLOCK
WITH_RUN_BLOCK
}
private val statements = listOf(initialization, loop)
override fun generateCode(builder: CodeBuilder) {
methodCall.generateCode(builder)
if (kind == Kind.SIMPLE) {
builder.append(statements, "\n")
}
else {
val block = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
if (kind == Kind.WITH_BLOCK) {
block.generateCode(builder)
}
else {
val call = MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block))
call.generateCode(builder)
}
}
}
}
@@ -260,4 +284,42 @@ class ForConverter(
else -> null
}
}
private fun hasNameConflict(): Boolean {
val names = statement.getInitialization()?.declaredVariableNames() ?: return false
if (names.isEmpty()) return false
val factory = PsiElementFactory.SERVICE.getInstance(project)
for (name in names) {
val refExpr = try {
factory.createExpressionFromText(name, statement) as? PsiReferenceExpression ?: return true
}
catch(e: IncorrectOperationException) {
return true
}
if (refExpr.resolve() != null) return true
}
var hasConflict = false
for (laterStatement in statement.siblings(forward = true, withItself = false)) {
laterStatement.accept(object: JavaRecursiveElementVisitor() {
override fun visitDeclarationStatement(statement: PsiDeclarationStatement) {
super.visitDeclarationStatement(statement)
if (statement.declaredVariableNames().any { it in names }) {
hasConflict = true
}
}
})
}
return hasConflict
}
private fun PsiStatement.declaredVariableNames(): Collection<String> {
val declarationStatement = this as? PsiDeclarationStatement ?: return listOf()
return declarationStatement.getDeclaredElements()
.filterIsInstance<PsiVariable>()
.map { it.getName() }
}
}
@@ -144,12 +144,11 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
}
override fun visitLabeledStatement(statement: PsiLabeledStatement) {
//TODO: multiple labels
val statementConverted = codeConverter.convertStatement(statement.getStatement())
val identifier = converter.convertIdentifier(statement.getLabelIdentifier())
if (statementConverted is ForConverter.RunBlockWithLoopStatement) { // special case - if our loop gets converted to run { ... } we should move label inside
if (statementConverted is ForConverter.WhileWithInitializationPseudoStatement) { // special case - if our loop gets converted to while with initialization we should move the label to the loop
val labeledLoop = LabeledStatement(identifier, statementConverted.loop).assignPrototype(statement)
result = ForConverter.RunBlockWithLoopStatement(statementConverted.initialization, labeledLoop)
result = ForConverter.WhileWithInitializationPseudoStatement(statementConverted.initialization, labeledLoop, statementConverted.kind)
}
else {
result = LabeledStatement(identifier, statementConverted)
@@ -1,7 +1,5 @@
run {
init()
while (condition()) {
body()
update()
}
init()
while (condition()) {
body()
update()
}
@@ -1,7 +1,5 @@
run {
var i = 0
while (i * 2 <= 10) {
foo(i)
i++
}
var i = 0
while (i * 2 <= 10) {
foo(i)
i++
}
@@ -1,9 +1,7 @@
run {
var i = 0
while (i < 10) {
System.out.println(i)
System.out.println(j)
j++
i++
}
var i = 0
while (i < 10) {
System.out.println(i)
System.out.println(j)
j++
i++
}
@@ -1,11 +1,9 @@
run {
var i = 0
while (i < 0) {
run {
var i = 1
i++
}
j++
var i = 0
while (i < 0) {
run {
var i = 1
i++
}
j++
i++
}
@@ -1,7 +1,5 @@
run {
var i = 0
while (i < 0) {
j++
i++
}
var i = 0
while (i < 0) {
j++
i++
}
@@ -1,7 +1,5 @@
run {
init()
while (true) {
body()
update()
}
init()
while (true) {
body()
update()
}
@@ -1,4 +1,2 @@
run {
init()
while (condition()) body()
}
init()
while (condition()) body()
@@ -1,8 +1,6 @@
run {
var i = 0
while (i < 0) {
return i
j++
i++
}
var i = 0
while (i < 0) {
return i
j++
i++
}
@@ -0,0 +1,11 @@
class A {
void foo() {
for (int i = 1; i < 1000; i *= 2) {
System.out.println(i);
}
for (int i = 1; i < 2000; i *= 2) {
System.out.println(i);
}
}
}
@@ -0,0 +1,17 @@
class A {
fun foo() {
run {
var i = 1
while (i < 1000) {
System.out.println(i)
i *= 2
}
}
var i = 1
while (i < 2000) {
System.out.println(i)
i *= 2
}
}
}
@@ -0,0 +1,11 @@
class A {
void foo() {
for (int i = 1, j = 0; i < 1000; i *= 2, j++) {
System.out.println(i);
}
for (int j = 1; j < 2000; j *= 2) {
System.out.println(j);
}
}
}
@@ -0,0 +1,19 @@
class A {
fun foo() {
run {
var i = 1
var j = 0
while (i < 1000) {
System.out.println(i)
i *= 2
j++
}
}
var j = 1
while (j < 2000) {
System.out.println(j)
j *= 2
}
}
}
@@ -0,0 +1,11 @@
class A {
int i = 1;
void foo() {
for (int i = 1; i < 1000; i *= 2) {
System.out.println(i);
}
i = 10;
}
}
@@ -0,0 +1,15 @@
class A {
var i = 1
fun foo() {
run {
var i = 1
while (i < 1000) {
System.out.println(i)
i *= 2
}
}
i = 10
}
}
@@ -0,0 +1,11 @@
class A {
void foo(boolean p) {
for (int i = 1; i < 1000; i *= 2) {
System.out.println(i);
}
if (p) {
int i = 10
}
}
}
@@ -0,0 +1,15 @@
class A {
fun foo(p: Boolean) {
run {
var i = 1
while (i < 1000) {
System.out.println(i)
i *= 2
}
}
if (p) {
val i = 10
}
}
}
@@ -0,0 +1,11 @@
class A {
void foo(boolean p) {
if (p) {
int i = 10
}
for (int i = 1; i < 1000; i *= 2) {
System.out.println(i);
}
}
}
@@ -0,0 +1,13 @@
class A {
fun foo(p: Boolean) {
if (p) {
val i = 10
}
var i = 1
while (i < 1000) {
System.out.println(i)
i *= 2
}
}
}
@@ -0,0 +1,8 @@
class A {
void foo(boolean p) {
if (p)
for (int i = 1; i < 1000; i *= 2) {
System.out.println(i);
}
}
}
@@ -0,0 +1,11 @@
class A {
fun foo(p: Boolean) {
if (p) {
var i = 1
while (i < 1000) {
System.out.println(i)
i *= 2
}
}
}
}
@@ -1,17 +1,15 @@
public class TestClass {
companion object {
public fun main(args: Array<String>) {
run {
var i = 0
while (i < 10) {
if (i == 4 || i == 8) {
i++
++i
continue
}
System.err.println(i)
var i = 0
while (i < 10) {
if (i == 4 || i == 8) {
i++
++i
continue
}
System.err.println(i)
++i
}
}
}
@@ -1,20 +1,18 @@
public class TestClass {
companion object {
public fun main(args: Array<String>) {
run {
var i = 0
var j = 1
while (i < 10) {
if (i == 4 || i == 8) {
i++
++i
j *= 2
continue
}
System.err.println(j)
var i = 0
var j = 1
while (i < 10) {
if (i == 4 || i == 8) {
i++
++i
j *= 2
continue
}
System.err.println(j)
++i
j *= 2
}
}
}
@@ -1,16 +1,14 @@
public class TestClass {
companion object {
public fun main(args: Array<String>) {
run {
var i = 1
while (i < 1000) {
if (i == 4 || i == 8) {
i *= 2
continue
}
System.err.println(i)
var i = 1
while (i < 1000) {
if (i == 4 || i == 8) {
i *= 2
continue
}
System.err.println(i)
i *= 2
}
}
}
@@ -2,30 +2,26 @@
public class TestClass {
companion object {
public fun main(args: Array<String>) {
run {
var i = 1
@OuterLoop1 @OuterLoop2 while (i < 1000) {
run {
var j = 1
@InnerLoop while (j < 100) {
if (j == 3) {
j *= 3
continue@InnerLoop
}
if (i == j) {
i *= 2
continue@OuterLoop1
}
System.err.println(j)
if (j == 9) {
j *= 3
continue
}
j *= 3
}
var i = 1
@OuterLoop1 @OuterLoop2 while (i < 1000) {
var j = 1
@InnerLoop while (j < 100) {
if (j == 3) {
j *= 3
continue@InnerLoop
}
i *= 2
if (i == j) {
i *= 2
continue@OuterLoop1
}
System.err.println(j)
if (j == 9) {
j *= 3
continue
}
j *= 3
}
i *= 2
}
}
}
@@ -2002,6 +2002,42 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("nameConflict1.java")
public void testNameConflict1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict1.java");
doTest(fileName);
}
@TestMetadata("nameConflict2.java")
public void testNameConflict2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict2.java");
doTest(fileName);
}
@TestMetadata("nameConflict3.java")
public void testNameConflict3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict3.java");
doTest(fileName);
}
@TestMetadata("nameConflict4.java")
public void testNameConflict4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict4.java");
doTest(fileName);
}
@TestMetadata("nameConflict5.java")
public void testNameConflict5() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict5.java");
doTest(fileName);
}
@TestMetadata("notInBlock.java")
public void testNotInBlock() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/notInBlock.java");
doTest(fileName);
}
@TestMetadata("withContinue1.java")
public void testWithContinue1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/withContinue1.java");
@@ -2002,6 +2002,42 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("nameConflict1.java")
public void testNameConflict1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict1.java");
doTest(fileName);
}
@TestMetadata("nameConflict2.java")
public void testNameConflict2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict2.java");
doTest(fileName);
}
@TestMetadata("nameConflict3.java")
public void testNameConflict3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict3.java");
doTest(fileName);
}
@TestMetadata("nameConflict4.java")
public void testNameConflict4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict4.java");
doTest(fileName);
}
@TestMetadata("nameConflict5.java")
public void testNameConflict5() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/nameConflict5.java");
doTest(fileName);
}
@TestMetadata("notInBlock.java")
public void testNotInBlock() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/notInBlock.java");
doTest(fileName);
}
@TestMetadata("withContinue1.java")
public void testWithContinue1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/withContinue1.java");