Java to Kotlin converter: generate nullable type for private non-initialized fields

This commit is contained in:
Valentin Kipyatkov
2014-06-26 14:58:57 +04:00
parent f8e4ecfa94
commit 44c706eb3e
8 changed files with 41 additions and 32 deletions
@@ -116,7 +116,7 @@ class ConstructorConverter(private val converter: Converter) {
val (field, `type`) = parameterToField[parameter]!! val (field, `type`) = parameterToField[parameter]!!
Parameter(field.declarationIdentifier(), Parameter(field.declarationIdentifier(),
`type`, `type`,
if (converter.isVal(field)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var, if (isVal(field)) Parameter.VarValModifier.Val else Parameter.VarValModifier.Var,
converter.convertAnnotations(parameter) + converter.convertAnnotations(field), converter.convertAnnotations(parameter) + converter.convertAnnotations(field),
converter.convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false)) converter.convertModifiers(field).filter { it in ACCESS_MODIFIERS }).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false))
} }
+3 -24
View File
@@ -23,7 +23,6 @@ import java.util.*
import com.intellij.psi.CommonClassNames.* import com.intellij.psi.CommonClassNames.*
import org.jetbrains.jet.lang.types.expressions.OperatorConventions.* import org.jetbrains.jet.lang.types.expressions.OperatorConventions.*
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.util.PsiUtil
public trait ConversionScope { public trait ConversionScope {
public fun contains(element: PsiElement): Boolean public fun contains(element: PsiElement): Boolean
@@ -235,14 +234,15 @@ public class Converter private(val project: Project, val settings: ConverterSett
val typeToDeclare = variableTypeToDeclare(field, val typeToDeclare = variableTypeToDeclare(field,
settings.specifyFieldTypeByDefault || modifiers.isPublic || modifiers.isProtected, settings.specifyFieldTypeByDefault || modifiers.isPublic || modifiers.isProtected,
isVal && modifiers.isPrivate) isVal && modifiers.isPrivate)
val initializer = convertExpression(field.getInitializer(), field.getType())
Field(name, Field(name,
annotations, annotations,
modifiers, modifiers,
typeToDeclare ?: typeConverter.convertVariableType(field), typeToDeclare ?: typeConverter.convertVariableType(field),
convertExpression(field.getInitializer(), field.getType()), initializer,
isVal, isVal,
typeToDeclare != null, typeToDeclare != null,
field.hasWriteAccesses(field.getContainingClass())) initializer.isEmpty && shouldGenerateDefaultInitializer(field))
} }
return converted.assignPrototype(field) return converted.assignPrototype(field)
} }
@@ -264,27 +264,6 @@ public class Converter private(val project: Project, val settings: ConverterSett
return if (convertedType == initializerType) null else convertedType return if (convertedType == initializerType) null else convertedType
} }
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()
return constructor != null &&
constructor.getContainingClass() == containingClass &&
parent.getParent() is PsiExpressionStatement &&
parent.getParent()?.getParent() == constructor.getBody()
}
return false
}
private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Member { private fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>): Member {
return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method) return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove).assignPrototype(method)
} }
@@ -120,6 +120,12 @@ class TypeConverter(val settings: ConverterSettings, val conversionScope: Conver
return nullability return nullability
} }
if (nullability == Nullability.Default) {
if (variable is PsiField && variable.hasModifierProperty(PsiModifier.PRIVATE) && shouldGenerateDefaultInitializer(variable)) {
return Nullability.Nullable
}
}
if (nullability == Nullability.Default) { if (nullability == Nullability.Default) {
val scope = searchScope(variable) val scope = searchScope(variable)
if (scope != null) { if (scope != null) {
+24
View File
@@ -69,6 +69,30 @@ fun getDefaultInitializer(field: Field): Expression {
return result.assignNoPrototype() return result.assignNoPrototype()
} }
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()
return constructor != null &&
constructor.getContainingClass() == containingClass &&
parent.getParent() is PsiExpressionStatement &&
parent.getParent()?.getParent() == constructor.getBody()
}
return false
}
fun shouldGenerateDefaultInitializer(field: PsiField)
= field.getInitializer() == null && !(isVal(field) && field.hasWriteAccesses(field.getContainingClass()))
fun isQualifierEmptyOrThis(ref: PsiReferenceExpression): Boolean { fun isQualifierEmptyOrThis(ref: PsiReferenceExpression): Boolean {
val qualifier = ref.getQualifierExpression() val qualifier = ref.getQualifierExpression()
return qualifier == null || (qualifier is PsiThisExpression && qualifier.getQualifier() == null) return qualifier == null || (qualifier is PsiThisExpression && qualifier.getQualifier() == null)
+2 -2
View File
@@ -26,7 +26,7 @@ class Field(
val initializer: Element, val initializer: Element,
val isVal: Boolean, val isVal: Boolean,
val explicitType: Boolean, val explicitType: Boolean,
private val hasWriteAccesses: Boolean private val defaultInitializer: Boolean
) : Member(annotations, modifiers) { ) : Member(annotations, modifiers) {
override fun generateCode(builder: CodeBuilder) { override fun generateCode(builder: CodeBuilder) {
@@ -40,7 +40,7 @@ class Field(
} }
var initializerToUse = initializer var initializerToUse = initializer
if (initializerToUse.isEmpty && !(isVal && hasWriteAccesses)) { if (initializerToUse.isEmpty && defaultInitializer) {
initializerToUse = getDefaultInitializer(this) initializerToUse = getDefaultInitializer(this)
} }
if (!initializerToUse.isEmpty) { if (!initializerToUse.isEmpty) {
@@ -1,4 +1,4 @@
enum class E { enum class E {
I I
private val name: String = 0 private val name: String? = null
} }
@@ -1,7 +1,7 @@
class Base { class Base {
private val myFirst: String = 0 private val myFirst: String? = null
} }
class Child : Base() { class Child : Base() {
private val mySecond: String = 0 private val mySecond: String? = null
} }
+1 -1
View File
@@ -1,3 +1,3 @@
class C { class C {
private val f: Foo = 0 private val f: Foo? = null
} }