Java to Kotlin converter: generate nullable type for private non-initialized fields
This commit is contained in:
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,3 +1,3 @@
|
|||||||
class C {
|
class C {
|
||||||
private val f: Foo = 0
|
private val f: Foo? = null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user