From e38b1b4fd888bc68ed369bf9cdf1655df2e46f79 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 23 Jun 2014 20:11:06 +0400 Subject: [PATCH] Java to Kotlin converter: more cases when field can be declared as val --- j2k/src/org/jetbrains/jet/j2k/Converter.kt | 27 +++++++++++++++++-- .../org/jetbrains/jet/j2k/TypeConverter.kt | 9 +++++++ j2k/src/org/jetbrains/jet/j2k/Utils.kt | 9 ------- .../ast/constructors/genericIdentifier.kt | 2 +- .../testData/ast/constructors/identifier.kt | 2 +- .../fieldsWithPrimaryPrivateConstructor.kt | 2 +- .../ast/enum/primaryPrivateConstructor.kt | 2 +- j2k/tests/testData/ast/field/valOrVar.java | 17 ++++++++++++ j2k/tests/testData/ast/field/valOrVar.kt | 15 ++++++++++- .../inheritance/classOneExtendsBaseGeneric.kt | 2 +- ...dsBaseWithZeroParamsNonEmptyConstructor.kt | 2 +- j2k/tests/testData/ast/issues/kt-638.kt | 2 +- .../ast/nullability/FieldComparedWithNull2.kt | 2 +- .../ast/nullability/FieldComparedWithNull4.kt | 2 +- 14 files changed, 74 insertions(+), 21 deletions(-) diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index 297c7cfbcca..b33d95ba986 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -285,7 +285,7 @@ public class Converter private(val project: Project, val settings: ConverterSett else { val initializer = field.getInitializer() val convertedType = typeConverter.convertVariableType(field) - val isVal = field.isEffectivelyFinal() + val isVal = isVal(field) val omitType = !settings.specifyFieldTypeByDefault && initializer != null && (modifiers.isPrivate && (isVal || convertedType == typeConverter.convertExpressionType(initializer)) || @@ -302,6 +302,29 @@ public class Converter private(val project: Project, val settings: ConverterSett return converted.assignPrototype(field) } + private fun isVal(field: PsiField): Boolean { + if (field.hasModifierProperty(PsiModifier.FINAL)) return true + if (!field.hasModifierProperty(PsiModifier.PRIVATE)) return false + val containingClass = field.getContainingClass() ?: return false + val writes = findVariableUsages(field, containingClass).filter { PsiUtil.isAccessedForWriting(it) } + if (writes.size == 0) return true + if (writes.size > 1) return false + val write = writes.single() + val parent = write.getParent() + if (parent is PsiAssignmentExpression && + parent.getOperationSign().getTokenType() == JavaTokenType.EQ && + isQualifierEmptyOrThis(write)) { + val constructor = write.getContainingConstructor() + if (constructor != null && + constructor.getContainingClass() == containingClass && + parent.getParent() is PsiExpressionStatement && + parent.getParent()?.getParent() == constructor.getBody()) { + return true + } + } + return false + } + private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet): Function { return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method) } @@ -428,7 +451,7 @@ public class Converter private(val project: Project, val settings: ConverterSett val (field, `type`) = parameterToField[parameter]!! Parameter(field.declarationIdentifier(), `type`, - if (field.hasModifierProperty(PsiModifier.FINAL)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var, + if (isVal(field)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var, convertAnnotations(parameter) + convertAnnotations(field), convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), inheritBlankLinesBefore = false) } diff --git a/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt b/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt index 5b8b4c8e6c5..4458f631e5c 100644 --- a/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/TypeConverter.kt @@ -263,4 +263,13 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver } return false } + + private fun PsiVariable.isEffectivelyFinal(): Boolean { + if (hasModifierProperty(PsiModifier.FINAL)) return true + return when(this) { + is PsiLocalVariable -> !hasWriteAccesses(getContainingMethod()) + is PsiField -> if (hasModifierProperty(PsiModifier.PRIVATE)) !hasWriteAccesses(getContainingClass()) else false + else -> false + } + } } diff --git a/j2k/src/org/jetbrains/jet/j2k/Utils.kt b/j2k/src/org/jetbrains/jet/j2k/Utils.kt index 6ab6d8bd4b5..eee0ce84cfe 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Utils.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Utils.kt @@ -93,12 +93,3 @@ fun PsiElement.isInSingleLine(): Boolean { } return true } - -fun PsiVariable.isEffectivelyFinal(): Boolean { - if (hasModifierProperty(PsiModifier.FINAL)) return true - return when(this) { - is PsiLocalVariable -> !hasWriteAccesses(getContainingMethod()) - is PsiField -> if (hasModifierProperty(PsiModifier.PRIVATE)) !hasWriteAccesses(getContainingClass()) else false - else -> false - } -} diff --git a/j2k/tests/testData/ast/constructors/genericIdentifier.kt b/j2k/tests/testData/ast/constructors/genericIdentifier.kt index 6669d4aaa2f..e569d2ca8b5 100644 --- a/j2k/tests/testData/ast/constructors/genericIdentifier.kt +++ b/j2k/tests/testData/ast/constructors/genericIdentifier.kt @@ -1,4 +1,4 @@ -public class Identifier private(private val myName: T, private var myHasDollar: Boolean) { +public class Identifier private(private val myName: T, private val myHasDollar: Boolean) { private var myNullable = true public fun getName(): T { diff --git a/j2k/tests/testData/ast/constructors/identifier.kt b/j2k/tests/testData/ast/constructors/identifier.kt index ee98211f791..3a3e6e9be5f 100644 --- a/j2k/tests/testData/ast/constructors/identifier.kt +++ b/j2k/tests/testData/ast/constructors/identifier.kt @@ -1,4 +1,4 @@ -public class Identifier private(private val myName: String, private var myHasDollar: Boolean) { +public class Identifier private(private val myName: String, private val myHasDollar: Boolean) { private var myNullable = true public fun getName(): String { diff --git a/j2k/tests/testData/ast/enum/fieldsWithPrimaryPrivateConstructor.kt b/j2k/tests/testData/ast/enum/fieldsWithPrimaryPrivateConstructor.kt index 53ff728122c..83a774702ba 100644 --- a/j2k/tests/testData/ast/enum/fieldsWithPrimaryPrivateConstructor.kt +++ b/j2k/tests/testData/ast/enum/fieldsWithPrimaryPrivateConstructor.kt @@ -1,4 +1,4 @@ -enum class Color private(private var code: Int) { +enum class Color private(private val code: Int) { WHITE : Color(21) BLACK : Color(22) RED : Color(23) diff --git a/j2k/tests/testData/ast/enum/primaryPrivateConstructor.kt b/j2k/tests/testData/ast/enum/primaryPrivateConstructor.kt index 583a45b4a47..838467a0bff 100644 --- a/j2k/tests/testData/ast/enum/primaryPrivateConstructor.kt +++ b/j2k/tests/testData/ast/enum/primaryPrivateConstructor.kt @@ -1,6 +1,6 @@ package demo -enum class Color private(private var code: Int) { +enum class Color private(private val code: Int) { public fun getCode(): Int { return code diff --git a/j2k/tests/testData/ast/field/valOrVar.java b/j2k/tests/testData/ast/field/valOrVar.java index 1d292dbfc26..02ccbef2060 100644 --- a/j2k/tests/testData/ast/field/valOrVar.java +++ b/j2k/tests/testData/ast/field/valOrVar.java @@ -5,6 +5,23 @@ class A { private int field3 = 0; final int field4 = 0; int field5 = 0; + private int field6; + private int field7; + private int field8; + private int field9; + private int field10; + private int field11; + + A(int p1, int p2, A a) { + field6 = p1; + field7 = 10; + this.field8 = p2; + this.field9 = 10; + if (p1 > 0) { + this.field10 = 10; + } + a.field11 = 10; + } void foo() { field3 = field2; diff --git a/j2k/tests/testData/ast/field/valOrVar.kt b/j2k/tests/testData/ast/field/valOrVar.kt index 68817a0b885..182656bca28 100644 --- a/j2k/tests/testData/ast/field/valOrVar.kt +++ b/j2k/tests/testData/ast/field/valOrVar.kt @@ -1,11 +1,24 @@ -class A() { +class A(private val field6: Int, private val field8: Int, a: A) { private val field1 = 0 private val field2 = 0 private var field3 = 0 val field4 = 0 var field5 = 0 + private val field7: Int + private val field9: Int + private var field10: Int = 0 + private var field11: Int = 0 fun foo() { field3 = field2 } + + { + field7 = 10 + this.field9 = 10 + if (field6 > 0) { + this.field10 = 10 + } + a.field11 = 10 + } } \ No newline at end of file diff --git a/j2k/tests/testData/ast/inheritance/classOneExtendsBaseGeneric.kt b/j2k/tests/testData/ast/inheritance/classOneExtendsBaseGeneric.kt index 3013c8a3582..d4499d9b10c 100644 --- a/j2k/tests/testData/ast/inheritance/classOneExtendsBaseGeneric.kt +++ b/j2k/tests/testData/ast/inheritance/classOneExtendsBaseGeneric.kt @@ -1,3 +1,3 @@ class Base(name: T) -class One(name: T, private var mySecond: K) : Base(name) \ No newline at end of file +class One(name: T, private val mySecond: K) : Base(name) \ No newline at end of file diff --git a/j2k/tests/testData/ast/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.kt b/j2k/tests/testData/ast/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.kt index 800d1f056c8..89d621b7bbf 100644 --- a/j2k/tests/testData/ast/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.kt +++ b/j2k/tests/testData/ast/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.kt @@ -1,3 +1,3 @@ class Base(name: String) -class One(name: String, private var mySecond: String) : Base(name) \ No newline at end of file +class One(name: String, private val mySecond: String) : Base(name) \ No newline at end of file diff --git a/j2k/tests/testData/ast/issues/kt-638.kt b/j2k/tests/testData/ast/issues/kt-638.kt index cd59e50c894..6e7be82d223 100644 --- a/j2k/tests/testData/ast/issues/kt-638.kt +++ b/j2k/tests/testData/ast/issues/kt-638.kt @@ -1,4 +1,4 @@ -public class Identifier private(private val myName: T, private var myHasDollar: Boolean) { +public class Identifier private(private val myName: T, private val myHasDollar: Boolean) { private var myNullable = true public fun getName(): T { diff --git a/j2k/tests/testData/ast/nullability/FieldComparedWithNull2.kt b/j2k/tests/testData/ast/nullability/FieldComparedWithNull2.kt index 9b5e3dc0528..0d5c31887dc 100644 --- a/j2k/tests/testData/ast/nullability/FieldComparedWithNull2.kt +++ b/j2k/tests/testData/ast/nullability/FieldComparedWithNull2.kt @@ -1,4 +1,4 @@ -class C(private var s: String?) { +class C(private val s: String?) { fun foo() { if (s != null) { diff --git a/j2k/tests/testData/ast/nullability/FieldComparedWithNull4.kt b/j2k/tests/testData/ast/nullability/FieldComparedWithNull4.kt index 95ca8178272..54fad2fe62c 100644 --- a/j2k/tests/testData/ast/nullability/FieldComparedWithNull4.kt +++ b/j2k/tests/testData/ast/nullability/FieldComparedWithNull4.kt @@ -1,4 +1,4 @@ -class C(private var s: String?) { +class C(private val s: String?) { { if (s == null) { System.out.print("null")