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.*
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.j2k.ast.* import org.jetbrains.kotlin.j2k.ast.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.siblings
class ForConverter( class ForConverter(
private val statement: PsiForStatement, private val statement: PsiForStatement,
@@ -101,23 +104,44 @@ class ForConverter(
statement.isInSingleLine()).assignNoPrototype() statement.isInSingleLine()).assignNoPrototype()
if (initializationConverted.isEmpty) return whileStatement if (initializationConverted.isEmpty) return whileStatement
//TODO: we could omit "run { ... }" when it won't cause any name conflicts val kind = if (statement.parents(withItself = false).filter { it !is PsiLabeledStatement }.first() !is PsiCodeBlock) {
return RunBlockWithLoopStatement(initializationConverted, whileStatement) 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 initialization: Statement,
public val loop: Statement public val loop: Statement,
public val kind: WhileWithInitializationPseudoStatement.Kind
) : Statement() { ) : Statement() {
private val methodCall = run { public enum class Kind {
val statements = listOf(initialization, loop) SIMPLE
val block = Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype() WITH_BLOCK
MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block)) WITH_RUN_BLOCK
} }
private val statements = listOf(initialization, loop)
override fun generateCode(builder: CodeBuilder) { 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 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) { override fun visitLabeledStatement(statement: PsiLabeledStatement) {
//TODO: multiple labels
val statementConverted = codeConverter.convertStatement(statement.getStatement()) val statementConverted = codeConverter.convertStatement(statement.getStatement())
val identifier = converter.convertIdentifier(statement.getLabelIdentifier()) 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) val labeledLoop = LabeledStatement(identifier, statementConverted.loop).assignPrototype(statement)
result = ForConverter.RunBlockWithLoopStatement(statementConverted.initialization, labeledLoop) result = ForConverter.WhileWithInitializationPseudoStatement(statementConverted.initialization, labeledLoop, statementConverted.kind)
} }
else { else {
result = LabeledStatement(identifier, statementConverted) result = LabeledStatement(identifier, statementConverted)
@@ -1,7 +1,5 @@
run { init()
init() while (condition()) {
while (condition()) { body()
body() update()
update()
}
} }
@@ -1,7 +1,5 @@
run { var i = 0
var i = 0 while (i * 2 <= 10) {
while (i * 2 <= 10) { foo(i)
foo(i) i++
i++
}
} }
@@ -1,9 +1,7 @@
run { var i = 0
var i = 0 while (i < 10) {
while (i < 10) { System.out.println(i)
System.out.println(i) System.out.println(j)
System.out.println(j) j++
j++ i++
i++
}
} }
@@ -1,11 +1,9 @@
run { var i = 0
var i = 0 while (i < 0) {
while (i < 0) { run {
run { var i = 1
var i = 1
i++
}
j++
i++ i++
} }
j++
i++
} }
@@ -1,7 +1,5 @@
run { var i = 0
var i = 0 while (i < 0) {
while (i < 0) { j++
j++ i++
i++
}
} }
@@ -1,7 +1,5 @@
run { init()
init() while (true) {
while (true) { body()
body() update()
update()
}
} }
@@ -1,4 +1,2 @@
run { init()
init() while (condition()) body()
while (condition()) body()
}
@@ -1,8 +1,6 @@
run { var i = 0
var i = 0 while (i < 0) {
while (i < 0) { return i
return i j++
j++ i++
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 { public class TestClass {
companion object { companion object {
public fun main(args: Array<String>) { public fun main(args: Array<String>) {
run { var i = 0
var i = 0 while (i < 10) {
while (i < 10) { if (i == 4 || i == 8) {
if (i == 4 || i == 8) { i++
i++
++i
continue
}
System.err.println(i)
++i ++i
continue
} }
System.err.println(i)
++i
} }
} }
} }
@@ -1,20 +1,18 @@
public class TestClass { public class TestClass {
companion object { companion object {
public fun main(args: Array<String>) { public fun main(args: Array<String>) {
run { var i = 0
var i = 0 var j = 1
var j = 1 while (i < 10) {
while (i < 10) { if (i == 4 || i == 8) {
if (i == 4 || i == 8) { i++
i++
++i
j *= 2
continue
}
System.err.println(j)
++i ++i
j *= 2 j *= 2
continue
} }
System.err.println(j)
++i
j *= 2
} }
} }
} }
@@ -1,16 +1,14 @@
public class TestClass { public class TestClass {
companion object { companion object {
public fun main(args: Array<String>) { public fun main(args: Array<String>) {
run { var i = 1
var i = 1 while (i < 1000) {
while (i < 1000) { if (i == 4 || i == 8) {
if (i == 4 || i == 8) {
i *= 2
continue
}
System.err.println(i)
i *= 2 i *= 2
continue
} }
System.err.println(i)
i *= 2
} }
} }
} }
@@ -2,30 +2,26 @@
public class TestClass { public class TestClass {
companion object { companion object {
public fun main(args: Array<String>) { public fun main(args: Array<String>) {
run { var i = 1
var i = 1 @OuterLoop1 @OuterLoop2 while (i < 1000) {
@OuterLoop1 @OuterLoop2 while (i < 1000) { var j = 1
run { @InnerLoop while (j < 100) {
var j = 1 if (j == 3) {
@InnerLoop while (j < 100) { j *= 3
if (j == 3) { continue@InnerLoop
j *= 3
continue@InnerLoop
}
if (i == j) {
i *= 2
continue@OuterLoop1
}
System.err.println(j)
if (j == 9) {
j *= 3
continue
}
j *= 3
}
} }
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); 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") @TestMetadata("withContinue1.java")
public void testWithContinue1() throws Exception { public void testWithContinue1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/withContinue1.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/withContinue1.java");
@@ -2002,6 +2002,42 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName); 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") @TestMetadata("withContinue1.java")
public void testWithContinue1() throws Exception { public void testWithContinue1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/withContinue1.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/withContinue1.java");