Java to Kotlin converter: do not produce incorrect "code blocks" which are actually parsed as lambda's in Kotlin

#KT-4670 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-06-20 17:52:10 +04:00
parent 116c27117f
commit 2188af2bf9
15 changed files with 145 additions and 63 deletions
@@ -51,7 +51,8 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
} }
override fun visitBlockStatement(statement: PsiBlockStatement) { override fun visitBlockStatement(statement: PsiBlockStatement) {
result = converter.convertBlock(statement.getCodeBlock()) val block = converter.convertBlock(statement.getCodeBlock())
result = MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block))
} }
override fun visitBreakStatement(statement: PsiBreakStatement) { override fun visitBreakStatement(statement: PsiBreakStatement) {
@@ -82,7 +83,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
converter.convertExpression(condition, condition.getType()) converter.convertExpression(condition, condition.getType())
else else
converter.convertExpression(condition) converter.convertExpression(condition)
result = DoWhileStatement(expression, converter.convertStatement(statement.getBody()), statement.isInSingleLine()) result = DoWhileStatement(expression, convertStatementOrBlock(statement.getBody()), statement.isInSingleLine())
} }
override fun visitExpressionStatement(statement: PsiExpressionStatement) { override fun visitExpressionStatement(statement: PsiExpressionStatement) {
@@ -111,7 +112,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
&& condition != null && condition != null
&& update != null && update != null
&& update.getChildren().size == 1 && update.getChildren().size == 1
&& isPlusPlusExpression(update.getChildren().single()) && update.getChildren().single().isPlusPlusExpression()
&& (operationTokenType == JavaTokenType.LT || operationTokenType == JavaTokenType.LE) && (operationTokenType == JavaTokenType.LT || operationTokenType == JavaTokenType.LE)
&& loopVar != null && loopVar != null
&& loopVar.getNameIdentifier() != null && loopVar.getNameIdentifier() != null
@@ -121,19 +122,48 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
result = ForeachWithRangeStatement(loopVar.declarationIdentifier(), result = ForeachWithRangeStatement(loopVar.declarationIdentifier(),
converter.convertExpression(loopVar.getInitializer()), converter.convertExpression(loopVar.getInitializer()),
endExpression, endExpression,
converter.convertStatement(body), convertStatementOrBlock(body),
statement.isInSingleLine()) statement.isInSingleLine())
} }
else { else {
var forStatements = ArrayList<Statement>() val initializationConverted = converter.convertStatement(initialization)
forStatements.add(converter.convertStatement(initialization)) val updateConverted = converter.convertStatement(update)
val bodyAndUpdate = listOf(converter.convertStatement(body),
Block(listOf(converter.convertStatement(update)), LBrace(), RBrace())) val whileBody = if (updateConverted.isEmpty) {
forStatements.add(WhileStatement( converter.convertStatement(body)
if (condition == null) LiteralExpression("true") else converter.convertExpression(condition), }
Block(bodyAndUpdate, LBrace(), RBrace()), else if (body is PsiBlockStatement) {
statement.isInSingleLine())) val nameConflict = initialization is PsiDeclarationStatement && initialization.getDeclaredElements().any { loopVar ->
result = Block(forStatements, LBrace(), RBrace()) loopVar is PsiNamedElement && body.getCodeBlock().getStatements().any { statement ->
statement is PsiDeclarationStatement && statement.getDeclaredElements().any {
it is PsiNamedElement && it.getName() == loopVar.getName()
}
}
}
if (nameConflict) {
Block(listOf(converter.convertStatement(body), updateConverted), LBrace(), RBrace(), true)
}
else {
val block = converter.convertBlock(body.getCodeBlock(), true)
Block(block.statements + listOf(updateConverted), block.lBrace, block.rBrace, true).assignPrototypesFrom(block)
}
}
else {
Block(listOf(converter.convertStatement(body), updateConverted), LBrace(), RBrace(), true)
}
val whileStatement = WhileStatement(
if (condition != null) converter.convertExpression(condition) else LiteralExpression("true"),
whileBody,
statement.isInSingleLine())
if (initializationConverted.isEmpty) {
result = whileStatement
}
else {
val block = Block(listOf(initializationConverted, whileStatement), LBrace(), RBrace())
result = MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block))
}
} }
} }
@@ -147,7 +177,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
} }
result = ForeachStatement(converter.convertParameter(statement.getIterationParameter()), result = ForeachStatement(converter.convertParameter(statement.getIterationParameter()),
iterator, iterator,
converter.convertStatement(statement.getBody()), convertStatementOrBlock(statement.getBody()),
statement.isInSingleLine()) statement.isInSingleLine())
} }
@@ -155,8 +185,8 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
val condition = statement.getCondition() val condition = statement.getCondition()
val expression = converter.convertExpression(condition, PsiType.BOOLEAN) val expression = converter.convertExpression(condition, PsiType.BOOLEAN)
result = IfStatement(expression, result = IfStatement(expression,
converter.convertStatement(statement.getThenBranch()), convertStatementOrBlock(statement.getThenBranch()),
converter.convertStatement(statement.getElseBranch()), convertStatementOrBlock(statement.getElseBranch()),
statement.isInSingleLine()) statement.isInSingleLine())
} }
@@ -307,7 +337,7 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
converter.convertExpression(condition, condition!!.getType()) converter.convertExpression(condition, condition!!.getType())
else else
converter.convertExpression(condition) converter.convertExpression(condition)
result = WhileStatement(expression, converter.convertStatement(statement.getBody()), statement.isInSingleLine()) result = WhileStatement(expression, convertStatementOrBlock(statement.getBody()), statement.isInSingleLine())
} }
override fun visitReturnStatement(statement: PsiReturnStatement) { override fun visitReturnStatement(statement: PsiReturnStatement) {
@@ -324,9 +354,9 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
result = Statement.Empty result = Statement.Empty
} }
private fun isPlusPlusExpression(psiElement: PsiElement): Boolean { private fun PsiElement.isPlusPlusExpression(): Boolean {
return (psiElement is PsiPostfixExpression && psiElement.getOperationTokenType() == JavaTokenType.PLUSPLUS) || return (this is PsiPostfixExpression && this.getOperationTokenType() == JavaTokenType.PLUSPLUS) ||
(psiElement is PsiPrefixExpression && psiElement.getOperationTokenType() == JavaTokenType.PLUSPLUS) (this is PsiPrefixExpression && this.getOperationTokenType() == JavaTokenType.PLUSPLUS)
} }
private fun containsBreak(slice: List<PsiElement?>) = slice.any { it is PsiBreakStatement } private fun containsBreak(slice: List<PsiElement?>) = slice.any { it is PsiBreakStatement }
@@ -371,4 +401,11 @@ open class StatementVisitor(public val converter: Converter) : JavaElementVisito
return cases return cases
} }
private fun convertStatementOrBlock(statement: PsiStatement?): Statement {
return if (statement is PsiBlockStatement)
converter.convertBlock(statement.getCodeBlock())
else
converter.convertStatement(statement)
}
} }
@@ -31,7 +31,7 @@ import org.jetbrains.jet.j2k.test.AbstractJavaToKotlinConverterTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all") @SuppressWarnings("all")
@TestMetadata("j2k/tests/testData/ast") @TestMetadata("j2k/tests/testData/ast")
@InnerTestClasses({JavaToKotlinConverterTestGenerated.Annotations.class, JavaToKotlinConverterTestGenerated.AnonymousBlock.class, JavaToKotlinConverterTestGenerated.ArrayAccessExpression.class, JavaToKotlinConverterTestGenerated.ArrayInitializerExpression.class, JavaToKotlinConverterTestGenerated.ArrayType.class, JavaToKotlinConverterTestGenerated.AssertStatement.class, JavaToKotlinConverterTestGenerated.AssignmentExpression.class, JavaToKotlinConverterTestGenerated.BinaryExpression.class, JavaToKotlinConverterTestGenerated.BoxedType.class, JavaToKotlinConverterTestGenerated.BreakStatement.class, JavaToKotlinConverterTestGenerated.CallChainExpression.class, JavaToKotlinConverterTestGenerated.Class.class, JavaToKotlinConverterTestGenerated.ClassExpression.class, JavaToKotlinConverterTestGenerated.Comments.class, JavaToKotlinConverterTestGenerated.ConditionalExpression.class, JavaToKotlinConverterTestGenerated.Constructors.class, JavaToKotlinConverterTestGenerated.ContinueStatement.class, JavaToKotlinConverterTestGenerated.DeclarationStatement.class, JavaToKotlinConverterTestGenerated.DoWhileStatement.class, JavaToKotlinConverterTestGenerated.Enum.class, JavaToKotlinConverterTestGenerated.Equals.class, JavaToKotlinConverterTestGenerated.Field.class, JavaToKotlinConverterTestGenerated.For.class, JavaToKotlinConverterTestGenerated.ForeachStatement.class, JavaToKotlinConverterTestGenerated.Formatting.class, JavaToKotlinConverterTestGenerated.Function.class, JavaToKotlinConverterTestGenerated.Identifier.class, JavaToKotlinConverterTestGenerated.IfStatement.class, JavaToKotlinConverterTestGenerated.ImportStatement.class, JavaToKotlinConverterTestGenerated.InProjectionType.class, JavaToKotlinConverterTestGenerated.Inheritance.class, JavaToKotlinConverterTestGenerated.IsOperator.class, JavaToKotlinConverterTestGenerated.Issues.class, JavaToKotlinConverterTestGenerated.KotlinApiAccess.class, JavaToKotlinConverterTestGenerated.LabelStatement.class, JavaToKotlinConverterTestGenerated.List.class, JavaToKotlinConverterTestGenerated.LiteralExpression.class, JavaToKotlinConverterTestGenerated.LocalVariable.class, JavaToKotlinConverterTestGenerated.MethodCallExpression.class, JavaToKotlinConverterTestGenerated.Misc.class, JavaToKotlinConverterTestGenerated.NewClassExpression.class, JavaToKotlinConverterTestGenerated.Nullability.class, JavaToKotlinConverterTestGenerated.ObjectLiteral.class, JavaToKotlinConverterTestGenerated.OutProjectionType.class, JavaToKotlinConverterTestGenerated.PackageStatement.class, JavaToKotlinConverterTestGenerated.ParenthesizedExpression.class, JavaToKotlinConverterTestGenerated.PolyadicExpression.class, JavaToKotlinConverterTestGenerated.PostfixOperator.class, JavaToKotlinConverterTestGenerated.PrefixOperator.class, JavaToKotlinConverterTestGenerated.RawGenerics.class, JavaToKotlinConverterTestGenerated.ReturnStatement.class, JavaToKotlinConverterTestGenerated.Settings.class, JavaToKotlinConverterTestGenerated.StarProjectionType.class, JavaToKotlinConverterTestGenerated.StaticMembers.class, JavaToKotlinConverterTestGenerated.SuperExpression.class, JavaToKotlinConverterTestGenerated.Switch.class, JavaToKotlinConverterTestGenerated.SynchronizedStatement.class, JavaToKotlinConverterTestGenerated.ThisExpression.class, JavaToKotlinConverterTestGenerated.ThrowStatement.class, JavaToKotlinConverterTestGenerated.ToKotlinClasses.class, JavaToKotlinConverterTestGenerated.Trait.class, JavaToKotlinConverterTestGenerated.TryStatement.class, JavaToKotlinConverterTestGenerated.TryWithResource.class, JavaToKotlinConverterTestGenerated.TypeCastExpression.class, JavaToKotlinConverterTestGenerated.TypeParameters.class, JavaToKotlinConverterTestGenerated.VarArg.class, JavaToKotlinConverterTestGenerated.WhileStatement.class}) @InnerTestClasses({JavaToKotlinConverterTestGenerated.Annotations.class, JavaToKotlinConverterTestGenerated.AnonymousBlock.class, JavaToKotlinConverterTestGenerated.ArrayAccessExpression.class, JavaToKotlinConverterTestGenerated.ArrayInitializerExpression.class, JavaToKotlinConverterTestGenerated.ArrayType.class, JavaToKotlinConverterTestGenerated.AssertStatement.class, JavaToKotlinConverterTestGenerated.AssignmentExpression.class, JavaToKotlinConverterTestGenerated.BinaryExpression.class, JavaToKotlinConverterTestGenerated.Blocks.class, JavaToKotlinConverterTestGenerated.BoxedType.class, JavaToKotlinConverterTestGenerated.BreakStatement.class, JavaToKotlinConverterTestGenerated.CallChainExpression.class, JavaToKotlinConverterTestGenerated.Class.class, JavaToKotlinConverterTestGenerated.ClassExpression.class, JavaToKotlinConverterTestGenerated.Comments.class, JavaToKotlinConverterTestGenerated.ConditionalExpression.class, JavaToKotlinConverterTestGenerated.Constructors.class, JavaToKotlinConverterTestGenerated.ContinueStatement.class, JavaToKotlinConverterTestGenerated.DeclarationStatement.class, JavaToKotlinConverterTestGenerated.DoWhileStatement.class, JavaToKotlinConverterTestGenerated.Enum.class, JavaToKotlinConverterTestGenerated.Equals.class, JavaToKotlinConverterTestGenerated.Field.class, JavaToKotlinConverterTestGenerated.For.class, JavaToKotlinConverterTestGenerated.ForeachStatement.class, JavaToKotlinConverterTestGenerated.Formatting.class, JavaToKotlinConverterTestGenerated.Function.class, JavaToKotlinConverterTestGenerated.Identifier.class, JavaToKotlinConverterTestGenerated.IfStatement.class, JavaToKotlinConverterTestGenerated.ImportStatement.class, JavaToKotlinConverterTestGenerated.InProjectionType.class, JavaToKotlinConverterTestGenerated.Inheritance.class, JavaToKotlinConverterTestGenerated.IsOperator.class, JavaToKotlinConverterTestGenerated.Issues.class, JavaToKotlinConverterTestGenerated.KotlinApiAccess.class, JavaToKotlinConverterTestGenerated.LabelStatement.class, JavaToKotlinConverterTestGenerated.List.class, JavaToKotlinConverterTestGenerated.LiteralExpression.class, JavaToKotlinConverterTestGenerated.LocalVariable.class, JavaToKotlinConverterTestGenerated.MethodCallExpression.class, JavaToKotlinConverterTestGenerated.Misc.class, JavaToKotlinConverterTestGenerated.NewClassExpression.class, JavaToKotlinConverterTestGenerated.Nullability.class, JavaToKotlinConverterTestGenerated.ObjectLiteral.class, JavaToKotlinConverterTestGenerated.OutProjectionType.class, JavaToKotlinConverterTestGenerated.PackageStatement.class, JavaToKotlinConverterTestGenerated.ParenthesizedExpression.class, JavaToKotlinConverterTestGenerated.PolyadicExpression.class, JavaToKotlinConverterTestGenerated.PostfixOperator.class, JavaToKotlinConverterTestGenerated.PrefixOperator.class, JavaToKotlinConverterTestGenerated.RawGenerics.class, JavaToKotlinConverterTestGenerated.ReturnStatement.class, JavaToKotlinConverterTestGenerated.Settings.class, JavaToKotlinConverterTestGenerated.StarProjectionType.class, JavaToKotlinConverterTestGenerated.StaticMembers.class, JavaToKotlinConverterTestGenerated.SuperExpression.class, JavaToKotlinConverterTestGenerated.Switch.class, JavaToKotlinConverterTestGenerated.SynchronizedStatement.class, JavaToKotlinConverterTestGenerated.ThisExpression.class, JavaToKotlinConverterTestGenerated.ThrowStatement.class, JavaToKotlinConverterTestGenerated.ToKotlinClasses.class, JavaToKotlinConverterTestGenerated.Trait.class, JavaToKotlinConverterTestGenerated.TryStatement.class, JavaToKotlinConverterTestGenerated.TryWithResource.class, JavaToKotlinConverterTestGenerated.TypeCastExpression.class, JavaToKotlinConverterTestGenerated.TypeParameters.class, JavaToKotlinConverterTestGenerated.VarArg.class, JavaToKotlinConverterTestGenerated.WhileStatement.class})
public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConverterTest { public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConverterTest {
public void testAllFilesPresentInAst() throws Exception { public void testAllFilesPresentInAst() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast"), Pattern.compile("^(.+)\\.java$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast"), Pattern.compile("^(.+)\\.java$"), true);
@@ -441,6 +441,19 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
} }
@TestMetadata("j2k/tests/testData/ast/blocks")
public static class Blocks extends AbstractJavaToKotlinConverterTest {
public void testAllFilesPresentInBlocks() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("j2k/tests/testData/ast/blocks"), Pattern.compile("^(.+)\\.java$"), true);
}
@TestMetadata("Blocks.java")
public void testBlocks() throws Exception {
doTest("j2k/tests/testData/ast/blocks/Blocks.java");
}
}
@TestMetadata("j2k/tests/testData/ast/boxedType") @TestMetadata("j2k/tests/testData/ast/boxedType")
public static class BoxedType extends AbstractJavaToKotlinConverterTest { public static class BoxedType extends AbstractJavaToKotlinConverterTest {
public void testAllFilesPresentInBoxedType() throws Exception { public void testAllFilesPresentInBoxedType() throws Exception {
@@ -1109,6 +1122,11 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
doTest("j2k/tests/testData/ast/for/commonCaseForTest.java"); doTest("j2k/tests/testData/ast/for/commonCaseForTest.java");
} }
@TestMetadata("forRangeWithBlock.java")
public void testForRangeWithBlock() throws Exception {
doTest("j2k/tests/testData/ast/for/forRangeWithBlock.java");
}
@TestMetadata("forRangeWithLE.java") @TestMetadata("forRangeWithLE.java")
public void testForRangeWithLE() throws Exception { public void testForRangeWithLE() throws Exception {
doTest("j2k/tests/testData/ast/for/forRangeWithLE.java"); doTest("j2k/tests/testData/ast/for/forRangeWithLE.java");
@@ -2809,6 +2827,7 @@ public class JavaToKotlinConverterTestGenerated extends AbstractJavaToKotlinConv
suite.addTestSuite(AssertStatement.class); suite.addTestSuite(AssertStatement.class);
suite.addTestSuite(AssignmentExpression.class); suite.addTestSuite(AssignmentExpression.class);
suite.addTestSuite(BinaryExpression.class); suite.addTestSuite(BinaryExpression.class);
suite.addTestSuite(Blocks.class);
suite.addTestSuite(BoxedType.class); suite.addTestSuite(BoxedType.class);
suite.addTestSuite(BreakStatement.class); suite.addTestSuite(BreakStatement.class);
suite.addTestSuite(CallChainExpression.class); suite.addTestSuite(CallChainExpression.class);
+16
View File
@@ -0,0 +1,16 @@
//method
void foo() {
{
int a = 1;
bar(a);
}
{
int a = 2;
bar(a);
}
{
bar(3);
}
}
+13
View File
@@ -0,0 +1,13 @@
fun foo() {
run {
val a = 1
bar(a)
}
run {
val a = 2
bar(a)
}
run { bar(3) }
}
@@ -1,9 +1,7 @@
{ run {
init() init()
while (condition()) { while (condition()) {
body() body()
{ update()
update()
}
} }
} }
@@ -0,0 +1,3 @@
//statement
int[] array = new int[10];
for (int i = 0; i < 10; i++) {array[i] = i;}
@@ -0,0 +1,4 @@
val array = IntArray(10)
for (i in 0..10 - 1) {
array[i] = i
}
+4 -2
View File
@@ -1,3 +1,5 @@
//statement //statement
int[] array = new int[10]; for (int i = 0; i < 10; j++, i++) {
for (int i = 0; i < 10; i++) {array[i] = i;} System.out.println(i);
System.out.println(j);
}
+8 -3
View File
@@ -1,4 +1,9 @@
val array = IntArray(10) run {
for (i in 0..10 - 1) { val i = 0
array[i] = i while (i < 10) {
System.out.println(i)
System.out.println(j)
j++
i++
}
} }
@@ -1,13 +1,11 @@
{ run {
val i = 0 val i = 0
while (i < 0) { while (i < 0) {
{ run {
val i = 1 val i = 1
i++ i++
} }
{ j++
j++ i++
i++
}
} }
} }
@@ -1,10 +1,7 @@
{ run {
val i = 0 val i = 0
while (i < 0) { while (i < 0) {
{} j++
{ i++
j++
i++
}
} }
} }
@@ -1,9 +1,7 @@
{ run {
init() init()
while (true) { while (true) {
body() body()
{ update()
update()
}
} }
} }
@@ -1,8 +1,4 @@
{ while (condition()) {
while (condition()) { body()
body() update()
{
update()
}
}
} }
@@ -1,6 +1,4 @@
{ run {
init() init()
while (condition()) { while (condition()) body()
body()
}
} }
+3 -5
View File
@@ -1,10 +1,8 @@
{ run {
val i = 0 val i = 0
while (i < 0) { while (i < 0) {
return i return i
{ j++
j++ i++
i++
}
} }
} }