J2K: get/set-methods converted to properties (but lot of TODOs left)
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.j2k
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.kotlin.j2k.ast.*
|
||||
import org.jetbrains.kotlin.j2k.usageProcessing.AccessorToPropertyProcessing
|
||||
@@ -26,32 +25,52 @@ import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import java.util.*
|
||||
|
||||
class FieldCorrectionInfo(val name: String, val access: Modifier?, val setterAccess: Modifier?) {
|
||||
val identifier = Identifier(name).assignNoPrototype()
|
||||
}
|
||||
|
||||
enum class AccessorKind {
|
||||
GETTER,
|
||||
SETTER
|
||||
}
|
||||
|
||||
enum class ClassKind {
|
||||
FINAL_CLASS,
|
||||
OPEN_CLASS,
|
||||
INTERFACE,
|
||||
OBJECT,
|
||||
ANONYMOUS_OBJECT,
|
||||
FINAL_ENUM,
|
||||
OPEN_ENUM,
|
||||
ANNOTATION_CLASS;
|
||||
|
||||
fun isObject() = this == OBJECT || this == ANONYMOUS_OBJECT
|
||||
fun isOpen() = this == OPEN_CLASS || this == INTERFACE || this == OPEN_ENUM
|
||||
fun isEnum() = this == FINAL_ENUM || this == OPEN_ENUM
|
||||
}
|
||||
|
||||
class ClassBodyConverter(private val psiClass: PsiClass,
|
||||
private val converter: Converter,
|
||||
private val isOpenClass: Boolean,
|
||||
private val isObject: Boolean,
|
||||
private val isAnonymousObject: Boolean = false) {
|
||||
private val membersToRemove = HashSet<PsiMember>()
|
||||
private val fieldCorrections = HashMap<PsiField, FieldCorrectionInfo>()
|
||||
private val classKind: ClassKind,
|
||||
private val converter: Converter
|
||||
) {
|
||||
private val fieldsToDrop = HashSet<PsiField>()
|
||||
|
||||
public fun convertBody(): ClassBody {
|
||||
processAccessorsToDrop()
|
||||
val memberToPropertyInfo = converter.propertyDetectionCache[psiClass]
|
||||
|
||||
val overloadReducer = OverloadReducer(psiClass.getMethods().filter { it !in membersToRemove } /* do not allow OverloadReducer to use accessors converted to properties */,
|
||||
isOpenClass,
|
||||
for ((member, propertyInfo) in memberToPropertyInfo) {
|
||||
if (member is PsiMethod) {
|
||||
if (member == propertyInfo.getMethod) {
|
||||
converter.addUsageProcessing(AccessorToPropertyProcessing(member, AccessorKind.GETTER, propertyInfo.name))
|
||||
}
|
||||
else {
|
||||
converter.addUsageProcessing(AccessorToPropertyProcessing(member, AccessorKind.SETTER, propertyInfo.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val overloadReducer = OverloadReducer(psiClass.methods.filter { !memberToPropertyInfo.containsKey(it) } /* do not allow OverloadReducer to use accessors converted to properties */,
|
||||
classKind.isOpen(),
|
||||
converter.referenceSearcher)
|
||||
|
||||
val constructorConverter = if (psiClass.getName() != null && !isObject)
|
||||
ConstructorConverter(psiClass, converter, fieldCorrections, overloadReducer)
|
||||
val constructorConverter = if (psiClass.getName() != null && !classKind.isObject())
|
||||
ConstructorConverter(psiClass, converter, { memberToPropertyInfo[it] }, overloadReducer, classKind)
|
||||
else
|
||||
null
|
||||
|
||||
@@ -59,24 +78,22 @@ class ClassBodyConverter(private val psiClass: PsiClass,
|
||||
for (element in psiClass.getChildren()) {
|
||||
if (element is PsiMember) {
|
||||
if (element is PsiAnnotationMethod) continue // converted in convertAnnotationType()
|
||||
if (isObject && element.isConstructor()) continue // no constructor in object
|
||||
if (classKind.isObject() && element.isConstructor()) continue // no constructor in object
|
||||
if (element is PsiMethod && overloadReducer.shouldDropMethod(element)) continue
|
||||
|
||||
val converted = converter.convertMember(element, membersToRemove, constructorConverter, overloadReducer)
|
||||
val converted = convertMember(element, constructorConverter, overloadReducer, memberToPropertyInfo)
|
||||
if (converted != null) {
|
||||
convertedMembers.put(element, converted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (member in membersToRemove) {
|
||||
convertedMembers.remove(member)
|
||||
}
|
||||
fieldsToDrop.forEach { convertedMembers.remove(it) }
|
||||
|
||||
val lBrace = LBrace().assignPrototype(psiClass.getLBrace())
|
||||
val rBrace = RBrace().assignPrototype(psiClass.getRBrace())
|
||||
|
||||
if (isObject) {
|
||||
if (classKind.isObject()) {
|
||||
val psiMembers = convertedMembers.keySet()
|
||||
if (psiMembers.all { it is PsiMethod }) { // for object with no fields we can use faster external usage processing
|
||||
converter.addUsageProcessing(ToObjectWithOnlyMethodsProcessing(psiClass))
|
||||
@@ -90,7 +107,7 @@ class ClassBodyConverter(private val psiClass: PsiClass,
|
||||
}
|
||||
}
|
||||
|
||||
return ClassBody(null, null, convertedMembers.values().toList(), emptyList(), lBrace, rBrace, false, false)
|
||||
return ClassBody(null, null, convertedMembers.values().toList(), emptyList(), lBrace, rBrace, classKind)
|
||||
}
|
||||
|
||||
val useCompanionObject = shouldGenerateCompanionObject(convertedMembers)
|
||||
@@ -117,7 +134,7 @@ class ClassBodyConverter(private val psiClass: PsiClass,
|
||||
}
|
||||
|
||||
if (primaryConstructorSignature != null
|
||||
&& !isAnonymousObject
|
||||
&& classKind != ClassKind.ANONYMOUS_OBJECT
|
||||
&& primaryConstructorSignature.annotations.isEmpty
|
||||
&& primaryConstructorSignature.accessModifier == null
|
||||
&& primaryConstructorSignature.parameterList.parameters.isEmpty()
|
||||
@@ -126,18 +143,37 @@ class ClassBodyConverter(private val psiClass: PsiClass,
|
||||
primaryConstructorSignature = null // no "()" after class name is needed in this case
|
||||
}
|
||||
|
||||
return ClassBody(primaryConstructorSignature, constructorConverter?.baseClassParams, members, companionObjectMembers, lBrace, rBrace, psiClass.isEnum(), isAnonymousObject)
|
||||
return ClassBody(primaryConstructorSignature, constructorConverter?.baseClassParams, members, companionObjectMembers, lBrace, rBrace, classKind)
|
||||
}
|
||||
|
||||
private fun Converter.convertMember(member: PsiMember,
|
||||
membersToRemove: MutableSet<PsiMember>,
|
||||
constructorConverter: ConstructorConverter?,
|
||||
overloadReducer: OverloadReducer): Member? {
|
||||
return when (member) {
|
||||
is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter, overloadReducer, isOpenClass)
|
||||
is PsiField -> convertField(member, fieldCorrections[member])
|
||||
is PsiClass -> convertClass(member)
|
||||
is PsiClassInitializer -> convertInitializer(member)
|
||||
private fun convertMember(
|
||||
member: PsiMember,
|
||||
constructorConverter: ConstructorConverter?,
|
||||
overloadReducer: OverloadReducer,
|
||||
memberToPropertyInfo: Map<PsiMember, PropertyInfo>
|
||||
): Member? {
|
||||
when (member) {
|
||||
is PsiMethod -> {
|
||||
memberToPropertyInfo[member]?.let { propertyInfo ->
|
||||
if (propertyInfo.field != null) return null // just drop the method, property will be generated when converting the field
|
||||
return if (member == propertyInfo.getMethod || propertyInfo.getMethod == null)
|
||||
converter.convertProperty(propertyInfo, classKind)
|
||||
else
|
||||
null // drop the method, property will be generated when converting the field or the getter
|
||||
}
|
||||
|
||||
return converter.convertMethod(member, fieldsToDrop, constructorConverter, overloadReducer, classKind)
|
||||
}
|
||||
|
||||
is PsiField -> {
|
||||
val propertyInfo = memberToPropertyInfo[member]!!
|
||||
return converter.convertProperty(propertyInfo, classKind)
|
||||
}
|
||||
|
||||
is PsiClass -> return converter.convertClass(member)
|
||||
|
||||
is PsiClassInitializer -> return converter.convertInitializer(member)
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown member: $member")
|
||||
}
|
||||
}
|
||||
@@ -154,93 +190,4 @@ class ClassBodyConverter(private val psiClass: PsiClass,
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun processAccessorsToDrop() {
|
||||
val fieldToGetterInfo = HashMap<PsiField, AccessorInfo>()
|
||||
val fieldToSetterInfo = HashMap<PsiField, AccessorInfo>()
|
||||
val fieldsWithConflict = HashSet<PsiField>()
|
||||
for (method in psiClass.getMethods()) {
|
||||
val info = getAccessorInfo(method) ?: continue
|
||||
if (method.getHierarchicalMethodSignature().getSuperSignatures().isNotEmpty()) continue // overrides or implements something
|
||||
val map = if (info.kind == AccessorKind.GETTER) fieldToGetterInfo else fieldToSetterInfo
|
||||
|
||||
val prevInfo = map[info.field]
|
||||
if (prevInfo != null) {
|
||||
fieldsWithConflict.add(info.field)
|
||||
continue
|
||||
}
|
||||
|
||||
map[info.field] = info
|
||||
}
|
||||
|
||||
for ((field, getterInfo) in fieldToGetterInfo) {
|
||||
val propertyName = getterInfo.propertyName
|
||||
val setterInfo = run {
|
||||
val info = fieldToSetterInfo[field]
|
||||
if (info?.propertyName == propertyName) info else null
|
||||
}
|
||||
|
||||
membersToRemove.add(getterInfo.method)
|
||||
if (setterInfo != null) {
|
||||
membersToRemove.add(setterInfo.method)
|
||||
}
|
||||
|
||||
val getterAccess = converter.convertModifiers(getterInfo.method, isOpenClass).accessModifier()
|
||||
val setterAccess = if (setterInfo != null)
|
||||
converter.convertModifiers(setterInfo.method, isOpenClass).accessModifier()
|
||||
else
|
||||
converter.convertModifiers(field, false).accessModifier()
|
||||
//TODO: check that setter access is not bigger
|
||||
fieldCorrections[field] = FieldCorrectionInfo(propertyName, getterAccess, setterAccess)
|
||||
|
||||
converter.addUsageProcessing(AccessorToPropertyProcessing(getterInfo.method, AccessorKind.GETTER, propertyName))
|
||||
if (setterInfo != null) {
|
||||
converter.addUsageProcessing(AccessorToPropertyProcessing(setterInfo.method, AccessorKind.SETTER, propertyName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessorInfo(val method: PsiMethod, val field: PsiField, val kind: AccessorKind, val propertyName: String)
|
||||
|
||||
private fun getAccessorInfo(method: PsiMethod): AccessorInfo? {
|
||||
val name = method.getName()
|
||||
val static = method.hasModifierProperty(PsiModifier.STATIC)
|
||||
if (name.startsWith("get") && method.getParameterList().getParametersCount() == 0) {
|
||||
val body = method.getBody() ?: return null
|
||||
val returnStatement = (body.getStatements().singleOrNull() as? PsiReturnStatement) ?: return null
|
||||
val field = fieldByExpression(returnStatement.getReturnValue(), static) ?: return null
|
||||
if (field.getType() != method.getReturnType()) return null
|
||||
if (converter.typeConverter.variableMutability(field) != converter.typeConverter.methodMutability(method)) return null
|
||||
val propertyName = StringUtil.decapitalize(name.substring("get".length()))
|
||||
return AccessorInfo(method, field, AccessorKind.GETTER, propertyName)
|
||||
}
|
||||
else if (name.startsWith("set") && method.getParameterList().getParametersCount() == 1) {
|
||||
val body = method.getBody() ?: return null
|
||||
val statement = (body.getStatements().singleOrNull() as? PsiExpressionStatement) ?: return null
|
||||
val assignment = statement.getExpression() as? PsiAssignmentExpression ?: return null
|
||||
if (assignment.getOperationTokenType() != JavaTokenType.EQ) return null
|
||||
val field = fieldByExpression(assignment.getLExpression(), static) ?: return null
|
||||
val parameter = method.getParameterList().getParameters().single()
|
||||
if ((assignment.getRExpression() as? PsiReferenceExpression)?.resolve() != parameter) return null
|
||||
if (field.getType() != parameter.getType()) return null
|
||||
val propertyName = StringUtil.decapitalize(name.substring("set".length()))
|
||||
return AccessorInfo(method, field, AccessorKind.SETTER, propertyName)
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun fieldByExpression(expression: PsiExpression?, static: Boolean): PsiField? {
|
||||
val refExpr = expression as? PsiReferenceExpression ?: return null
|
||||
if (static) {
|
||||
if (!refExpr.isQualifierEmptyOrClass(psiClass)) return null
|
||||
}
|
||||
else {
|
||||
if (!refExpr.isQualifierEmptyOrThis()) return null
|
||||
}
|
||||
val field = refExpr.resolve() as? PsiField ?: return null
|
||||
if (field.getContainingClass() != psiClass || field.hasModifierProperty(PsiModifier.STATIC) != static) return null
|
||||
return field
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.j2k
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.CommonClassNames.*
|
||||
import org.jetbrains.kotlin.j2k.ast.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
class CodeConverter(
|
||||
public val converter: Converter,
|
||||
@@ -69,10 +70,12 @@ class CodeConverter(
|
||||
val isVal = variable.hasModifierProperty(PsiModifier.FINAL) ||
|
||||
variable.getInitializer() == null/* we do not know actually and prefer val until we have better analysis*/ ||
|
||||
!variable.hasWriteAccesses(converter.referenceSearcher, variable.getContainingMethod())
|
||||
val type = typeConverter.convertVariableType(variable)
|
||||
val explicitType = type.check { settings.specifyLocalVariableTypeByDefault || converter.shouldDeclareVariableType(variable, type, isVal) }
|
||||
return LocalVariable(variable.declarationIdentifier(),
|
||||
converter.convertAnnotations(variable),
|
||||
converter.convertModifiers(variable, false),
|
||||
converter.variableTypeToDeclare(variable, settings.specifyLocalVariableTypeByDefault, isVal),
|
||||
explicitType,
|
||||
convertExpression(variable.getInitializer(), variable.getType()),
|
||||
isVal).assignPrototype(variable)
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ import java.util.*
|
||||
class ConstructorConverter(
|
||||
private val psiClass: PsiClass,
|
||||
private val converter: Converter,
|
||||
private val fieldCorrections: Map<PsiField, FieldCorrectionInfo>,
|
||||
private val overloadReducer: OverloadReducer
|
||||
private val fieldToPropertyInfo: (PsiField) -> PropertyInfo?,
|
||||
private val overloadReducer: OverloadReducer,
|
||||
private val classKind: ClassKind
|
||||
) {
|
||||
private val constructors = psiClass.getConstructors().asList()
|
||||
|
||||
@@ -75,10 +76,10 @@ class ConstructorConverter(
|
||||
public fun convertConstructor(constructor: PsiMethod,
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
membersToRemove: MutableSet<PsiMember>,
|
||||
fieldsToDrop: MutableSet<PsiField>,
|
||||
postProcessBody: (Block) -> Block): Constructor? {
|
||||
val result = if (constructor == primaryConstructor) {
|
||||
convertPrimaryConstructor(annotations, modifiers, membersToRemove, postProcessBody)
|
||||
convertPrimaryConstructor(annotations, modifiers, fieldsToDrop, postProcessBody)
|
||||
}
|
||||
else {
|
||||
if (overloadReducer.shouldDropMethod(constructor)) return null
|
||||
@@ -116,7 +117,7 @@ class ConstructorConverter(
|
||||
|
||||
private fun convertPrimaryConstructor(annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
membersToRemove: MutableSet<PsiMember>,
|
||||
fieldsToDrop: MutableSet<PsiField>,
|
||||
postProcessBody: (Block) -> Block): PrimaryConstructor {
|
||||
val params = primaryConstructor!!.getParameterList().getParameters()
|
||||
val parameterToField = HashMap<PsiParameter, Pair<PsiField, Type>>()
|
||||
@@ -143,15 +144,14 @@ class ConstructorConverter(
|
||||
continue
|
||||
}
|
||||
|
||||
val fieldCorrection = fieldCorrections[field]
|
||||
// we cannot specify different setter access for constructor parameter
|
||||
if (fieldCorrection != null && !field.isVal(converter.referenceSearcher) && fieldCorrection.access != fieldCorrection.setterAccess) continue
|
||||
val propertyInfo = fieldToPropertyInfo(field)
|
||||
if (propertyInfo != null && (propertyInfo.needExplicitGetter || propertyInfo.needExplicitSetter)) continue
|
||||
|
||||
parameterToField.put(parameter, field to type)
|
||||
statementsToRemove.add(initializationStatement)
|
||||
membersToRemove.add(field)
|
||||
fieldsToDrop.add(field)
|
||||
|
||||
val fieldName = fieldCorrection?.name ?: field.getName()!!
|
||||
val fieldName = propertyInfo?.name ?: field.getName()!!
|
||||
if (fieldName != parameter.getName()) {
|
||||
parameterUsageReplacementMap.put(parameter.getName()!!, fieldName)
|
||||
}
|
||||
@@ -200,17 +200,13 @@ class ConstructorConverter(
|
||||
}
|
||||
else {
|
||||
val (field, type) = parameterToField[parameter]!!
|
||||
val fieldCorrection = fieldCorrections[field]
|
||||
val name = fieldCorrection?.identifier ?: field.declarationIdentifier()
|
||||
val accessModifiers = if (fieldCorrection != null)
|
||||
Modifiers(listOf()).with(fieldCorrection.access).assignNoPrototype()
|
||||
else
|
||||
converter.convertModifiers(field, false).filter { it in ACCESS_MODIFIERS }
|
||||
FunctionParameter(name,
|
||||
val propertyInfo = fieldToPropertyInfo(field)!!
|
||||
val parameterModifiers = converter.convertModifiers(propertyInfo, classKind)
|
||||
FunctionParameter(propertyInfo.identifier,
|
||||
type,
|
||||
if (field.isVal(converter.referenceSearcher)) FunctionParameter.VarValModifier.Val else FunctionParameter.VarValModifier.Var,
|
||||
converter.convertAnnotations(parameter) + converter.convertAnnotations(field),
|
||||
accessModifiers,
|
||||
if (propertyInfo.isVal) FunctionParameter.VarValModifier.Val else FunctionParameter.VarValModifier.Var,
|
||||
converter.convertAnnotations(parameter) + converter.convertAnnotations(field),
|
||||
parameterModifiers,
|
||||
default)
|
||||
.assignPrototypes(
|
||||
PrototypeInfo(parameter, CommentsAndSpacesInheritance.LINE_BREAKS),
|
||||
|
||||
@@ -30,6 +30,9 @@ import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessing
|
||||
import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessingExpressionConverter
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions.*
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
||||
import java.util.*
|
||||
|
||||
class Converter private constructor(
|
||||
@@ -58,6 +61,8 @@ class Converter private constructor(
|
||||
|
||||
public val referenceSearcher: CachingReferenceSearcher = CachingReferenceSearcher(services.referenceSearcher)
|
||||
|
||||
public val propertyDetectionCache = PropertyDetectionCache(this)
|
||||
|
||||
companion object {
|
||||
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, services: JavaToKotlinConverterServices,
|
||||
inConversionScope: (PsiElement) -> Boolean, usageProcessingsCollector: (UsageProcessing) -> Unit): Converter {
|
||||
@@ -103,8 +108,8 @@ class Converter private constructor(
|
||||
private fun convertTopElement(element: PsiElement): Element? = when (element) {
|
||||
is PsiJavaFile -> convertFile(element)
|
||||
is PsiClass -> convertClass(element)
|
||||
is PsiMethod -> convertMethod(element, null, null, null, false)
|
||||
is PsiField -> convertField(element, null)
|
||||
is PsiMethod -> convertMethod(element, null, null, null, ClassKind.FINAL_CLASS)
|
||||
is PsiField -> convertProperty(PropertyInfo.fromFieldWithNoAccessors(element, element.isVal(referenceSearcher)), ClassKind.FINAL_CLASS)
|
||||
is PsiStatement -> createDefaultCodeConverter().convertStatement(element)
|
||||
is PsiExpression -> createDefaultCodeConverter().convertExpression(element)
|
||||
is PsiImportList -> convertImportList(element)
|
||||
@@ -170,20 +175,20 @@ class Converter private constructor(
|
||||
|
||||
return when {
|
||||
psiClass.isInterface() -> {
|
||||
val classBody = ClassBodyConverter(psiClass, this, isOpenClass = false, isObject = false).convertBody()
|
||||
val classBody = ClassBodyConverter(psiClass, ClassKind.INTERFACE, this).convertBody()
|
||||
Interface(name, annotations, modifiers, typeParameters, extendsTypes, implementsTypes, classBody)
|
||||
}
|
||||
|
||||
psiClass.isEnum() -> {
|
||||
modifiers = modifiers.without(Modifier.ABSTRACT)
|
||||
val hasInheritors = psiClass.getFields().any { it is PsiEnumConstant && it.getInitializingClass() != null }
|
||||
val classBody = ClassBodyConverter(psiClass, this, isOpenClass = hasInheritors, isObject = false).convertBody()
|
||||
val classBody = ClassBodyConverter(psiClass, if (hasInheritors) ClassKind.OPEN_ENUM else ClassKind.FINAL_ENUM, this).convertBody()
|
||||
Enum(name, annotations, modifiers, typeParameters, implementsTypes, classBody)
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (shouldConvertIntoObject(psiClass)) {
|
||||
val classBody = ClassBodyConverter(psiClass, this, isOpenClass = false, isObject = true).convertBody()
|
||||
val classBody = ClassBodyConverter(psiClass, ClassKind.OBJECT, this).convertBody()
|
||||
Object(name, annotations, modifiers.without(Modifier.ABSTRACT), classBody)
|
||||
}
|
||||
else {
|
||||
@@ -195,7 +200,8 @@ class Converter private constructor(
|
||||
modifiers = modifiers.with(Modifier.OPEN)
|
||||
}
|
||||
|
||||
val classBody = ClassBodyConverter(psiClass, this, isOpenClass = modifiers.contains(Modifier.OPEN) || modifiers.contains(Modifier.ABSTRACT), isObject = false).convertBody()
|
||||
val isOpen = modifiers.contains(Modifier.OPEN) || modifiers.contains(Modifier.ABSTRACT)
|
||||
val classBody = ClassBodyConverter(psiClass, if (isOpen) ClassKind.OPEN_CLASS else ClassKind.FINAL_CLASS, this).convertBody()
|
||||
Class(name, annotations, modifiers, typeParameters, extendsTypes, classBody.baseClassParams, implementsTypes, classBody)
|
||||
}
|
||||
}
|
||||
@@ -276,9 +282,9 @@ class Converter private constructor(
|
||||
null
|
||||
|
||||
// to convert fields and nested types - they are not allowed in Kotlin but we convert them and let user refactor code
|
||||
var classBody = ClassBodyConverter(psiClass, this, isOpenClass = false, isObject = false).convertBody()
|
||||
var classBody = ClassBodyConverter(psiClass, ClassKind.ANNOTATION_CLASS, this).convertBody()
|
||||
classBody = ClassBody(constructorSignature, classBody.baseClassParams, classBody.members,
|
||||
classBody.companionObjectMembers, classBody.lBrace, classBody.rBrace, classBody.isEnumBody, classBody.isAnonymousClassBody)
|
||||
classBody.companionObjectMembers, classBody.lBrace, classBody.rBrace, classBody.classKind)
|
||||
|
||||
return Class(psiClass.declarationIdentifier(),
|
||||
convertAnnotations(psiClass),
|
||||
@@ -295,17 +301,19 @@ class Converter private constructor(
|
||||
convertModifiers(initializer, false)).assignPrototype(initializer)
|
||||
}
|
||||
|
||||
public fun convertField(field: PsiField, correction: FieldCorrectionInfo?): Member {
|
||||
val annotations = convertAnnotations(field)
|
||||
public fun convertProperty(propertyInfo: PropertyInfo, classKind: ClassKind): Member {
|
||||
val field = propertyInfo.field
|
||||
val getMethod = propertyInfo.getMethod
|
||||
val setMethod = propertyInfo.setMethod
|
||||
|
||||
var modifiers = convertModifiers(field, false)
|
||||
//TODO: annotations from getter/setter?
|
||||
val annotations = field?.let { convertAnnotations(it) } ?: Annotations.Empty
|
||||
|
||||
if (correction != null) {
|
||||
modifiers = modifiers.without(modifiers.accessModifier()).with(correction.access)
|
||||
}
|
||||
val modifiers = convertModifiers(propertyInfo, classKind)
|
||||
|
||||
val name = correction?.identifier ?: field.declarationIdentifier()
|
||||
val name = propertyInfo.identifier
|
||||
if (field is PsiEnumConstant) {
|
||||
assert(getMethod == null && setMethod == null)
|
||||
val argumentList = field.getArgumentList()
|
||||
val params = deferredElement { codeConverter ->
|
||||
ExpressionList(codeConverter.convertExpressions(argumentList?.getExpressions() ?: arrayOf<PsiExpression>())).assignPrototype(argumentList)
|
||||
@@ -315,55 +323,113 @@ class Converter private constructor(
|
||||
.assignPrototype(field, CommentsAndSpacesInheritance.LINE_BREAKS)
|
||||
}
|
||||
else {
|
||||
val isVal = field.isVal(referenceSearcher)
|
||||
val typeToDeclare = variableTypeToDeclare(field,
|
||||
settings.specifyFieldTypeByDefault,
|
||||
isVal && modifiers.isPrivate)
|
||||
val propertyType = typeToDeclare ?: typeConverter.convertVariableType(field)
|
||||
val setterParameter = setMethod?.parameterList?.parameters?.single()
|
||||
val nullability = combinedNullability(field, getMethod, setterParameter)
|
||||
val mutability = combinedMutability(field, getMethod, setterParameter)
|
||||
|
||||
addUsageProcessing(FieldToPropertyProcessing(field, correction?.name ?: field.getName()!!, propertyType.isNullable))
|
||||
val propertyType = typeConverter.convertType(propertyInfo.psiType, nullability, mutability)
|
||||
|
||||
return Property(name,
|
||||
annotations,
|
||||
modifiers,
|
||||
propertyType,
|
||||
deferredElement { codeConverter -> codeConverter.convertExpression(field.getInitializer(), field.getType()) },
|
||||
isVal,
|
||||
typeToDeclare != null,
|
||||
shouldGenerateDefaultInitializer(referenceSearcher, field),
|
||||
if (correction != null) correction.setterAccess else modifiers.accessModifier()
|
||||
).assignPrototype(field)
|
||||
val shouldDeclareType = settings.specifyFieldTypeByDefault
|
||||
|| field == null
|
||||
|| shouldDeclareVariableType(field, propertyType, propertyInfo.isVal && modifiers.isPrivate)
|
||||
|
||||
//TODO: usage processings for converting method's to property
|
||||
if (field != null) {
|
||||
addUsageProcessing(FieldToPropertyProcessing(field, propertyInfo.name, propertyType.isNullable))
|
||||
}
|
||||
|
||||
//TODO: doc-comments
|
||||
|
||||
val getter = getMethod
|
||||
?.check { propertyInfo.needExplicitGetter } //TODO: what if annotations are not empty?
|
||||
?.let {
|
||||
val method = convertMethod(it, null, null, null, classKind)!!
|
||||
PropertyAccessor(AccessorKind.GETTER, method.annotations, Modifiers.Empty, method.parameterList, method.body)
|
||||
.assignPrototype(it, CommentsAndSpacesInheritance.NO_SPACES)
|
||||
}
|
||||
|
||||
var setter: PropertyAccessor? = null
|
||||
if (propertyInfo.needExplicitSetter) {
|
||||
val method = setMethod?.let { convertMethod(it, null, null, null, classKind)!! }
|
||||
val accessorModifiers = Modifiers(propertyInfo.specialSetterAccess.singletonOrEmptyList()).assignNoPrototype()
|
||||
setter = PropertyAccessor(
|
||||
AccessorKind.SETTER,
|
||||
method?.annotations ?: Annotations.Empty,
|
||||
accessorModifiers,
|
||||
method?.parameterList?.check { propertyInfo.needSetterBody },
|
||||
method?.body?.check { propertyInfo.needSetterBody }).assignPrototype(setMethod, CommentsAndSpacesInheritance.NO_SPACES)
|
||||
}
|
||||
|
||||
val needInitializer = field != null && shouldGenerateDefaultInitializer(referenceSearcher, field)
|
||||
val property = Property(name,
|
||||
annotations,
|
||||
modifiers,
|
||||
propertyInfo.isVal,
|
||||
propertyType,
|
||||
shouldDeclareType,
|
||||
deferredElement { codeConverter -> field?.let { codeConverter.convertExpression(it.initializer, it.type) } ?: Expression.Empty },
|
||||
needInitializer,
|
||||
getter,
|
||||
setter,
|
||||
classKind == ClassKind.INTERFACE
|
||||
)
|
||||
return property.assignPrototype(field as PsiElement? ?: getMethod ?: setMethod)
|
||||
}
|
||||
}
|
||||
|
||||
public fun variableTypeToDeclare(variable: PsiVariable, specifyAlways: Boolean, canChangeType: Boolean): Type? {
|
||||
fun convertType() = typeConverter.convertVariableType(variable)
|
||||
private fun combinedNullability(vararg psiElements: PsiElement?): Nullability {
|
||||
val values = psiElements.filterNotNull().map {
|
||||
when (it) {
|
||||
is PsiVariable -> typeConverter.variableNullability(it)
|
||||
is PsiMethod -> typeConverter.methodNullability(it)
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
return when {
|
||||
values.contains(Nullability.Nullable) -> Nullability.Nullable
|
||||
values.contains(Nullability.Default) -> Nullability.Default
|
||||
else -> Nullability.NotNull
|
||||
}
|
||||
}
|
||||
|
||||
if (specifyAlways) return convertType()
|
||||
private fun combinedMutability(vararg psiElements: PsiElement?): Mutability {
|
||||
val values = psiElements.filterNotNull().map {
|
||||
when (it) {
|
||||
is PsiVariable -> typeConverter.variableMutability(it)
|
||||
is PsiMethod -> typeConverter.methodMutability(it)
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
return when {
|
||||
values.contains(Mutability.Mutable) -> Mutability.Mutable
|
||||
values.contains(Mutability.Default) -> Mutability.Default
|
||||
else -> Mutability.NonMutable
|
||||
}
|
||||
}
|
||||
|
||||
public fun shouldDeclareVariableType(variable: PsiVariable, type: Type, canChangeType: Boolean): Boolean {
|
||||
val initializer = variable.getInitializer()
|
||||
if (initializer == null || initializer.isNullLiteral()) return convertType()
|
||||
if (initializer == null || initializer.isNullLiteral()) return true
|
||||
|
||||
if (canChangeType) return null
|
||||
if (canChangeType) return false
|
||||
|
||||
val convertedType = convertType()
|
||||
var initializerType = createDefaultCodeConverter().convertedExpressionType(initializer, variable.getType())
|
||||
if (initializerType is ErrorType) return null // do not add explicit type when initializer is not resolved, let user add it if really needed
|
||||
return if (convertedType == initializerType) null else convertedType
|
||||
if (initializerType is ErrorType) return false // do not add explicit type when initializer is not resolved, let user add it if really needed
|
||||
return type != initializerType
|
||||
}
|
||||
|
||||
public fun convertMethod(
|
||||
method: PsiMethod,
|
||||
membersToRemove: MutableSet<PsiMember>?,
|
||||
fieldsToDrop: MutableSet<PsiField>?,
|
||||
constructorConverter: ConstructorConverter?,
|
||||
overloadReducer: OverloadReducer?,
|
||||
isInOpenClass: Boolean
|
||||
classKind: ClassKind
|
||||
): FunctionLike? {
|
||||
val returnType = typeConverter.convertMethodReturnType(method)
|
||||
|
||||
val annotations = convertAnnotations(method) + convertThrows(method)
|
||||
|
||||
var modifiers = convertModifiers(method, isInOpenClass)
|
||||
var modifiers = convertModifiers(method, classKind.isOpen())
|
||||
|
||||
val statementsToInsert = ArrayList<Statement>()
|
||||
for (parameter in method.getParameterList().getParameters()) {
|
||||
@@ -387,7 +453,7 @@ class Converter private constructor(
|
||||
}
|
||||
|
||||
val function = if (method.isConstructor() && constructorConverter != null) {
|
||||
constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove!!, postProcessBody)
|
||||
constructorConverter.convertConstructor(method, annotations, modifiers, fieldsToDrop!!, postProcessBody)
|
||||
}
|
||||
else {
|
||||
val containingClass = method.getContainingClass()
|
||||
@@ -407,7 +473,7 @@ class Converter private constructor(
|
||||
val body = codeConverter.withMethodReturnType(method.getReturnType()).convertBlock(method.getBody())
|
||||
postProcessBody(body)
|
||||
}
|
||||
Function(method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false)
|
||||
Function(method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, classKind == ClassKind.INTERFACE)
|
||||
}
|
||||
|
||||
if (function == null) return null
|
||||
@@ -419,7 +485,7 @@ class Converter private constructor(
|
||||
newLineAfter = false).assignNoPrototype())).assignNoPrototype()
|
||||
}
|
||||
|
||||
if (function.parameterList.parameters.any { it is FunctionParameter && it.defaultValue != null } && !function.modifiers.isPrivate) {
|
||||
if (function.parameterList!!.parameters.any { it is FunctionParameter && it.defaultValue != null } && !function.modifiers.isPrivate) {
|
||||
function.annotations += Annotations(
|
||||
listOf(Annotation(Identifier("JvmOverloads").assignNoPrototype(),
|
||||
listOf(),
|
||||
@@ -575,8 +641,44 @@ class Converter private constructor(
|
||||
return without(Modifier.INTERNAL).with(Modifier.PUBLIC)
|
||||
}
|
||||
|
||||
public fun convertModifiers(propertyInfo: PropertyInfo, classKind: ClassKind): Modifiers {
|
||||
val fieldModifiers = propertyInfo.field?.let { convertModifiers(it, false) } ?: Modifiers.Empty
|
||||
val getterModifiers = propertyInfo.getMethod?.let { convertModifiers(it, classKind.isOpen()) } ?: Modifiers.Empty
|
||||
val setterModifiers = propertyInfo.setMethod?.let { convertModifiers(it, classKind.isOpen()) } ?: Modifiers.Empty
|
||||
|
||||
val modifiers = ArrayList<Modifier>()
|
||||
|
||||
//TODO: what if one is abstract and another is not?
|
||||
if (getterModifiers.contains(Modifier.ABSTRACT) || setterModifiers.contains(Modifier.ABSTRACT)) {
|
||||
modifiers.add(Modifier.ABSTRACT)
|
||||
}
|
||||
|
||||
if (getterModifiers.contains(Modifier.OPEN) || setterModifiers.contains(Modifier.OPEN)) {
|
||||
modifiers.add(Modifier.OPEN)
|
||||
}
|
||||
|
||||
if (propertyInfo.isOverride) {
|
||||
modifiers.add(Modifier.OVERRIDE)
|
||||
}
|
||||
|
||||
if (propertyInfo.getMethod != null) {
|
||||
modifiers.addIfNotNull(getterModifiers.accessModifier())
|
||||
}
|
||||
else if (propertyInfo.setMethod != null) {
|
||||
modifiers.addIfNotNull(getterModifiers.accessModifier())
|
||||
}
|
||||
else {
|
||||
modifiers.addIfNotNull(fieldModifiers.accessModifier())
|
||||
}
|
||||
|
||||
val prototypes = listOf<PsiElement?>(propertyInfo.field, propertyInfo.getMethod, propertyInfo.setMethod)
|
||||
.filterNotNull()
|
||||
.map { PrototypeInfo(it, CommentsAndSpacesInheritance.NO_SPACES) }
|
||||
return Modifiers(modifiers).assignPrototypes(*prototypes.toTypedArray())
|
||||
}
|
||||
|
||||
public fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
|
||||
return AnonymousClassBody(ClassBodyConverter(anonymousClass, this, isOpenClass = false, isObject = false, isAnonymousObject = true).convertBody(),
|
||||
return AnonymousClassBody(ClassBodyConverter(anonymousClass, ClassKind.ANONYMOUS_OBJECT, this).convertBody(),
|
||||
anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.j2k.ast
|
||||
|
||||
import com.intellij.psi.PsiExpression
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.j2k.ClassKind
|
||||
import org.jetbrains.kotlin.j2k.CodeBuilder
|
||||
import org.jetbrains.kotlin.j2k.OverloadReducer
|
||||
import org.jetbrains.kotlin.j2k.append
|
||||
|
||||
abstract class Member(var annotations: Annotations, val modifiers: Modifiers) : Element()
|
||||
@@ -31,16 +29,15 @@ class ClassBody (
|
||||
val companionObjectMembers: List<Member>,
|
||||
val lBrace: LBrace,
|
||||
val rBrace: RBrace,
|
||||
val isEnumBody: Boolean,
|
||||
val isAnonymousClassBody: Boolean) {
|
||||
|
||||
val classKind: ClassKind
|
||||
) {
|
||||
fun appendTo(builder: CodeBuilder) {
|
||||
val membersFiltered = members.filter { !it.isEmpty }
|
||||
if (!isAnonymousClassBody && membersFiltered.isEmpty() && companionObjectMembers.isEmpty()) return
|
||||
if (classKind != ClassKind.ANONYMOUS_OBJECT && membersFiltered.isEmpty() && companionObjectMembers.isEmpty()) return
|
||||
|
||||
builder append " " append lBrace append "\n"
|
||||
|
||||
if (!isEnumBody) {
|
||||
if (!classKind.isEnum()) {
|
||||
builder.append(membersFiltered, "\n")
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -16,15 +16,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.j2k.ast
|
||||
|
||||
import org.jetbrains.kotlin.j2k.*
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.jetbrains.kotlin.j2k.CodeBuilder
|
||||
import org.jetbrains.kotlin.j2k.Converter
|
||||
|
||||
abstract class Constructor(
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
parameterList: ParameterList,
|
||||
body: DeferredElement<Block>
|
||||
) : FunctionLike(annotations, modifiers, parameterList, body)
|
||||
) : FunctionLike(annotations, modifiers, parameterList, body) {
|
||||
override val parameterList: ParameterList
|
||||
get() = super.parameterList!!
|
||||
}
|
||||
|
||||
class PrimaryConstructor(
|
||||
annotations: Annotations,
|
||||
|
||||
@@ -21,9 +21,12 @@ import org.jetbrains.kotlin.j2k.CodeBuilder
|
||||
abstract class FunctionLike(
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
val parameterList: ParameterList,
|
||||
open val parameterList: ParameterList?,
|
||||
val body: DeferredElement<Block>?
|
||||
) : Member(annotations, modifiers)
|
||||
) : Member(annotations, modifiers) {
|
||||
|
||||
protected open fun presentationModifiers(): Modifiers = modifiers
|
||||
}
|
||||
|
||||
class Function(
|
||||
val name: Identifier,
|
||||
@@ -33,10 +36,13 @@ class Function(
|
||||
val typeParameterList: TypeParameterList,
|
||||
parameterList: ParameterList,
|
||||
body: DeferredElement<Block>?,
|
||||
val isInInterface: Boolean
|
||||
private val isInInterface: Boolean
|
||||
) : FunctionLike(annotations, modifiers, parameterList, body) {
|
||||
|
||||
private fun presentationModifiers(): Modifiers {
|
||||
override val parameterList: ParameterList
|
||||
get() = super.parameterList!!
|
||||
|
||||
protected override fun presentationModifiers(): Modifiers {
|
||||
var modifiers = this.modifiers
|
||||
if (isInInterface) {
|
||||
modifiers = modifiers.without(Modifier.ABSTRACT)
|
||||
|
||||
@@ -16,23 +16,40 @@
|
||||
|
||||
package org.jetbrains.kotlin.j2k.ast
|
||||
|
||||
import org.jetbrains.kotlin.j2k.*
|
||||
import org.jetbrains.kotlin.j2k.AccessorKind
|
||||
import org.jetbrains.kotlin.j2k.CodeBuilder
|
||||
import org.jetbrains.kotlin.j2k.getDefaultInitializer
|
||||
|
||||
class Property(
|
||||
val identifier: Identifier,
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
val type: Type,
|
||||
private val initializer: DeferredElement<Expression>,
|
||||
val isVal: Boolean,
|
||||
val type: Type,
|
||||
val explicitType: Boolean,
|
||||
private val defaultInitializer: Boolean,
|
||||
val setterAccess: Modifier?
|
||||
private val initializer: DeferredElement<Expression>,
|
||||
private val needInitializer: Boolean,
|
||||
private val getter: PropertyAccessor?,
|
||||
private val setter: PropertyAccessor?,
|
||||
private val isInInterface: Boolean
|
||||
) : Member(annotations, modifiers) {
|
||||
|
||||
private fun presentationModifiers(): Modifiers {
|
||||
var modifiers = this.modifiers
|
||||
if (isInInterface) {
|
||||
modifiers = modifiers.without(Modifier.ABSTRACT)
|
||||
}
|
||||
|
||||
if (modifiers.contains(Modifier.OVERRIDE)) {
|
||||
modifiers = modifiers.filter { it != Modifier.OPEN }
|
||||
}
|
||||
|
||||
return modifiers
|
||||
}
|
||||
|
||||
override fun generateCode(builder: CodeBuilder) {
|
||||
builder.append(annotations)
|
||||
.appendWithSpaceAfter(modifiers)
|
||||
.appendWithSpaceAfter(presentationModifiers())
|
||||
.append(if (isVal) "val " else "var ")
|
||||
.append(identifier)
|
||||
|
||||
@@ -41,19 +58,47 @@ class Property(
|
||||
}
|
||||
|
||||
var initializerToUse: Element = initializer
|
||||
if (initializerToUse.isEmpty && defaultInitializer) {
|
||||
if (initializerToUse.isEmpty && needInitializer) {
|
||||
initializerToUse = getDefaultInitializer(this) ?: Element.Empty
|
||||
}
|
||||
if (!initializerToUse.isEmpty) {
|
||||
builder append " = " append initializerToUse
|
||||
}
|
||||
|
||||
if (!isVal && setterAccess != modifiers.accessModifier()) {
|
||||
builder.append("\n")
|
||||
if (setterAccess != null) {
|
||||
builder.appendWithSpaceAfter(Modifiers(listOf(setterAccess)).assignNoPrototype())
|
||||
}
|
||||
builder.append("set")
|
||||
if (getter != null) {
|
||||
builder append "\n" append getter
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
builder append "\n" append setter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyAccessor(
|
||||
private val kind: AccessorKind,
|
||||
annotations: Annotations,
|
||||
modifiers: Modifiers,
|
||||
parameterList: ParameterList?,
|
||||
body: DeferredElement<Block>?
|
||||
) : FunctionLike(annotations, modifiers, parameterList, body) {
|
||||
|
||||
override fun generateCode(builder: CodeBuilder) {
|
||||
builder.append(annotations)
|
||||
|
||||
builder.appendWithSpaceAfter(presentationModifiers())
|
||||
|
||||
when (kind) {
|
||||
AccessorKind.GETTER -> builder.append("get")
|
||||
AccessorKind.SETTER -> builder.append("set")
|
||||
}
|
||||
|
||||
if (parameterList != null) {
|
||||
builder append "(" append parameterList append ")"
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
builder append " " append body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.j2k
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.MethodSignatureUtil
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightMethod
|
||||
import org.jetbrains.kotlin.j2k.ast.Identifier
|
||||
import org.jetbrains.kotlin.j2k.ast.Modifier
|
||||
import org.jetbrains.kotlin.j2k.ast.assignNoPrototype
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.JetProperty
|
||||
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
||||
import java.util.*
|
||||
|
||||
class PropertyInfo(
|
||||
val name: String,
|
||||
val isVal: Boolean,
|
||||
val psiType: PsiType,
|
||||
val field: PsiField?,
|
||||
val getMethod: PsiMethod?,
|
||||
val setMethod: PsiMethod?,
|
||||
val needGetterBody: Boolean,
|
||||
val needSetterBody: Boolean,
|
||||
val specialSetterAccess: Modifier?,
|
||||
val isOverride: Boolean
|
||||
) {
|
||||
init {
|
||||
assert(field != null || getMethod != null || setMethod != null)
|
||||
if (needGetterBody) {
|
||||
assert(getMethod != null && getMethod.body != null)
|
||||
}
|
||||
if (needSetterBody) {
|
||||
assert(setMethod != null && setMethod.body != null)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: take from field
|
||||
val identifier = Identifier(name).assignNoPrototype()
|
||||
|
||||
val needExplicitGetter: Boolean get() = needGetterBody
|
||||
val needExplicitSetter: Boolean get() = needSetterBody || specialSetterAccess != null
|
||||
|
||||
companion object {
|
||||
fun fromFieldWithNoAccessors(field: PsiField, isVal: Boolean)
|
||||
= PropertyInfo(field.name ?: "", isVal, field.type, field, null, null, false, false, null, false)
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyDetectionCache(private val converter: Converter) {
|
||||
private val cache = HashMap<PsiClass, Map<PsiMember, PropertyInfo>>()
|
||||
|
||||
fun get(psiClass: PsiClass): Map<PsiMember, PropertyInfo> {
|
||||
cache[psiClass]?.let { return it }
|
||||
|
||||
assert(converter.inConversionScope(psiClass))
|
||||
|
||||
val detected = PropertyDetector(psiClass, converter).detectProperties()
|
||||
cache[psiClass] = detected
|
||||
return detected
|
||||
}
|
||||
}
|
||||
|
||||
private class PropertyDetector(
|
||||
private val psiClass: PsiClass,
|
||||
private val converter: Converter
|
||||
) {
|
||||
public fun detectProperties(): Map<PsiMember, PropertyInfo> {
|
||||
val propertyNameToGetterInfo = LinkedHashMap<String, AccessorInfo>()
|
||||
val propertyNameToSetterInfo = LinkedHashMap<String, AccessorInfo>()
|
||||
val propertyNamesWithConflict = HashSet<String>()
|
||||
val prohibitedPropertyNames = psiClass.fields.map { it.name }.toMutableSet() //TODO: fields from base
|
||||
for (method in psiClass.getMethods()) {
|
||||
val info = getAccessorInfo(method) ?: continue
|
||||
|
||||
val map = if (info.kind == AccessorKind.GETTER) propertyNameToGetterInfo else propertyNameToSetterInfo
|
||||
|
||||
val prevInfo = map[info.propertyName]
|
||||
if (prevInfo != null) {
|
||||
propertyNamesWithConflict.add(info.propertyName)
|
||||
continue
|
||||
}
|
||||
|
||||
map[info.propertyName] = info
|
||||
info.field?.let { prohibitedPropertyNames.remove(it.name) }
|
||||
}
|
||||
|
||||
val memberToPropertyInfo = HashMap<PsiMember, PropertyInfo>()
|
||||
|
||||
val propertyNames = propertyNameToGetterInfo.keySet() + propertyNameToSetterInfo.keySet()
|
||||
for (propertyName in propertyNames) {
|
||||
// TODO: use "field" expression if the conflicting field is used only inside get/set-methods
|
||||
if (propertyName in prohibitedPropertyNames) continue // cannot create such property - will conflict with existing field
|
||||
//TODO: what about overrides in this case?
|
||||
|
||||
val getterInfo = propertyNameToGetterInfo[propertyName]
|
||||
var setterInfo = propertyNameToSetterInfo[propertyName]
|
||||
|
||||
// no property without getter except for overrides
|
||||
if (getterInfo == null && setterInfo!!.method.hierarchicalMethodSignature.superSignatures.isEmpty()) continue
|
||||
|
||||
if (setterInfo != null && getterInfo != null && setterInfo.method.parameterList.parameters.single().type != getterInfo.method.returnType) {
|
||||
setterInfo = null
|
||||
}
|
||||
|
||||
var field = getterInfo?.field ?: setterInfo?.field
|
||||
|
||||
if (field != null && memberToPropertyInfo.containsKey(field)) { // already used in another property
|
||||
field = null
|
||||
}
|
||||
|
||||
var specialSetterAccess: Modifier? = null
|
||||
val getterAccess = if (getterInfo != null) converter.convertModifiers(getterInfo.method, false).accessModifier() else Modifier.PUBLIC //TODO
|
||||
val setterAccess = if (setterInfo != null)
|
||||
converter.convertModifiers(setterInfo.method, false).accessModifier()
|
||||
else if (field != null && !field.isVal(converter.referenceSearcher))
|
||||
converter.convertModifiers(field, false).accessModifier()
|
||||
else
|
||||
getterAccess
|
||||
if (setterAccess != getterAccess) {
|
||||
specialSetterAccess = setterAccess
|
||||
}
|
||||
|
||||
//TODO: no body for getter OR setter
|
||||
|
||||
val isVal = if (setterInfo != null)
|
||||
false
|
||||
else if (getterInfo!!.superProperty != null && !getterInfo.superProperty!!.isVal)
|
||||
false
|
||||
else
|
||||
field == null || field.isVal(converter.referenceSearcher)
|
||||
|
||||
val type = field?.type ?: getterInfo?.method?.returnType ?: setterInfo!!.method.parameterList.parameters.single()?.type!!
|
||||
|
||||
val propertyInfo = PropertyInfo(propertyName,
|
||||
isVal,
|
||||
type,
|
||||
field,
|
||||
getterInfo?.method,
|
||||
setterInfo?.method,
|
||||
getterInfo != null && getterInfo.method.body != null && (field == null || getterInfo.field != field),
|
||||
setterInfo != null && setterInfo.method.body != null && (field == null || setterInfo.field != field),
|
||||
specialSetterAccess,
|
||||
getterInfo?.superProperty != null || setterInfo?.superProperty != null)
|
||||
|
||||
if (field != null) {
|
||||
memberToPropertyInfo[field] = propertyInfo
|
||||
}
|
||||
if (getterInfo != null) {
|
||||
memberToPropertyInfo[getterInfo.method] = propertyInfo
|
||||
}
|
||||
if (setterInfo != null) {
|
||||
memberToPropertyInfo[setterInfo.method] = propertyInfo
|
||||
}
|
||||
}
|
||||
|
||||
dropPropertiesWithConflictingAccessors(memberToPropertyInfo)
|
||||
|
||||
val mappedFields = memberToPropertyInfo.values()
|
||||
.map { it.field }
|
||||
.filterNotNull()
|
||||
.toSet()
|
||||
|
||||
// map all other fields
|
||||
for (field in psiClass.fields) {
|
||||
if (field !in mappedFields) {
|
||||
val propertyInfo = PropertyInfo.fromFieldWithNoAccessors(field, field.isVal(converter.referenceSearcher))
|
||||
memberToPropertyInfo[field] = propertyInfo
|
||||
}
|
||||
}
|
||||
|
||||
return memberToPropertyInfo
|
||||
}
|
||||
|
||||
private fun dropPropertiesWithConflictingAccessors(memberToPropertyInfo: MutableMap<PsiMember, PropertyInfo>) {
|
||||
val propertyInfos = memberToPropertyInfo.values().distinct()
|
||||
|
||||
val mappedMethods = propertyInfos.map { it.getMethod }.filterNotNull().toSet() + propertyInfos.map { it.setMethod }.filterNotNull().toSet()
|
||||
|
||||
//TODO: bases
|
||||
val prohibitedSignatures = psiClass.methods
|
||||
.filter { it !in mappedMethods }
|
||||
.map { it.getSignature(PsiSubstitutor.EMPTY) }
|
||||
.toSet()
|
||||
|
||||
fun dropProperty(propertyInfo: PropertyInfo) {
|
||||
propertyInfo.field?.let { memberToPropertyInfo.remove(it) }
|
||||
propertyInfo.getMethod?.let { memberToPropertyInfo.remove(it) }
|
||||
propertyInfo.setMethod?.let { memberToPropertyInfo.remove(it) }
|
||||
}
|
||||
|
||||
for (propertyInfo in propertyInfos) {
|
||||
if (propertyInfo.isOverride) continue // cannot drop override
|
||||
|
||||
//TODO: test this case
|
||||
val getterName = JvmAbi.getterName(propertyInfo.name)
|
||||
val getterSignature = MethodSignatureUtil.createMethodSignature(getterName, emptyArray(), emptyArray(), PsiSubstitutor.EMPTY)
|
||||
if (getterSignature in prohibitedSignatures) {
|
||||
dropProperty(propertyInfo)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!propertyInfo.isVal) {
|
||||
val setterName = JvmAbi.setterName(propertyInfo.name)
|
||||
val setterSignature = MethodSignatureUtil.createMethodSignature(setterName, arrayOf(propertyInfo.psiType), emptyArray(), PsiSubstitutor.EMPTY)
|
||||
if (setterSignature in prohibitedSignatures) {
|
||||
dropProperty(propertyInfo)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessorInfo(
|
||||
val method: PsiMethod,
|
||||
val field: PsiField?,
|
||||
val kind: AccessorKind,
|
||||
val propertyName: String,
|
||||
val superProperty: SuperPropertyInfo?
|
||||
)
|
||||
|
||||
private class SuperPropertyInfo(
|
||||
val isVal: Boolean
|
||||
//TODO: add visibility
|
||||
)
|
||||
|
||||
private fun getAccessorInfo(method: PsiMethod): AccessorInfo? {
|
||||
propertyNameByGetMethod(method)?.let { propertyName ->
|
||||
val field = fieldFromGetterBody(method)
|
||||
return createAccessorInfo(method, field, AccessorKind.GETTER, propertyName)
|
||||
}
|
||||
|
||||
propertyNameBySetMethod(method)?.let { propertyName ->
|
||||
val field = fieldFromSetterBody(method)
|
||||
return createAccessorInfo(method, field, AccessorKind.SETTER, propertyName)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createAccessorInfo(getOrSetMethod: PsiMethod, field: PsiField?, kind: AccessorKind, propertyName: String): AccessorInfo? {
|
||||
//TODO: deepest method
|
||||
//TODO: multiple
|
||||
for (superSignature in getOrSetMethod.hierarchicalMethodSignature.superSignatures) {
|
||||
val method = superSignature.method
|
||||
val containingClass = method.containingClass!!
|
||||
val superPropertyInfo: SuperPropertyInfo? = if (converter.inConversionScope(containingClass)) {
|
||||
val propertyInfo = converter.propertyDetectionCache[containingClass][method]
|
||||
if (propertyInfo != null) SuperPropertyInfo(propertyInfo.isVal) else null
|
||||
}
|
||||
else if (method is KotlinLightMethod) {
|
||||
val origin = method.getOrigin()
|
||||
if (origin is JetProperty) SuperPropertyInfo(!origin.isVar) else null
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
return superPropertyInfo?.let { AccessorInfo(getOrSetMethod, field, kind, propertyName, it) }
|
||||
}
|
||||
|
||||
return AccessorInfo(getOrSetMethod, field, kind, propertyName, null)
|
||||
}
|
||||
|
||||
private fun propertyNameByGetMethod(method: PsiMethod): String? {
|
||||
if (method.isConstructor) return null
|
||||
if (method.parameterList.parametersCount != 0) return null
|
||||
|
||||
val name = method.name
|
||||
if (!Name.isValidIdentifier(name)) return null
|
||||
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(Name.identifier(name))?.identifier ?: return null
|
||||
|
||||
val returnType = method.returnType ?: return null
|
||||
if (returnType.canonicalText == "void") return null
|
||||
if (method.typeParameters.isNotEmpty()) return null
|
||||
|
||||
return propertyName
|
||||
}
|
||||
|
||||
private fun propertyNameBySetMethod(method: PsiMethod): String? {
|
||||
if (method.isConstructor) return null
|
||||
if (method.parameterList.parametersCount != 1) return null
|
||||
|
||||
val name = method.name
|
||||
if (!Name.isValidIdentifier(name)) return null
|
||||
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameBySetMethodName(Name.identifier(name), false/*TODO!!*/)?.identifier ?: return null
|
||||
|
||||
if (method.returnType?.canonicalText != "void") return null
|
||||
if (method.typeParameters.isNotEmpty()) return null
|
||||
|
||||
return propertyName
|
||||
}
|
||||
|
||||
private fun fieldFromGetterBody(getter: PsiMethod): PsiField? {
|
||||
val body = getter.getBody() ?: return null
|
||||
val returnStatement = (body.getStatements().singleOrNull() as? PsiReturnStatement) ?: return null
|
||||
val isStatic = getter.hasModifierProperty(PsiModifier.STATIC)
|
||||
val field = fieldByExpression(returnStatement.getReturnValue(), isStatic) ?: return null
|
||||
if (field.getType() != getter.getReturnType()) return null
|
||||
if (converter.typeConverter.variableMutability(field) != converter.typeConverter.methodMutability(getter)) return null
|
||||
return field
|
||||
}
|
||||
|
||||
private fun fieldFromSetterBody(setter: PsiMethod): PsiField? {
|
||||
val body = setter.getBody() ?: return null
|
||||
val statement = (body.getStatements().singleOrNull() as? PsiExpressionStatement) ?: return null
|
||||
val assignment = statement.getExpression() as? PsiAssignmentExpression ?: return null
|
||||
if (assignment.getOperationTokenType() != JavaTokenType.EQ) return null
|
||||
val isStatic = setter.hasModifierProperty(PsiModifier.STATIC)
|
||||
val field = fieldByExpression(assignment.getLExpression(), isStatic) ?: return null
|
||||
val parameter = setter.getParameterList().getParameters().single()
|
||||
if ((assignment.getRExpression() as? PsiReferenceExpression)?.resolve() != parameter) return null
|
||||
if (field.getType() != parameter.getType()) return null
|
||||
return field
|
||||
}
|
||||
|
||||
private fun fieldByExpression(expression: PsiExpression?, static: Boolean): PsiField? {
|
||||
val refExpr = expression as? PsiReferenceExpression ?: return null
|
||||
if (static) {
|
||||
if (!refExpr.isQualifierEmptyOrClass(psiClass)) return null
|
||||
}
|
||||
else {
|
||||
if (!refExpr.isQualifierEmptyOrThis()) return null
|
||||
}
|
||||
val field = refExpr.resolve() as? PsiField ?: return null
|
||||
if (field.getContainingClass() != psiClass || field.hasModifierProperty(PsiModifier.STATIC) != static) return null
|
||||
return field
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -90,4 +90,17 @@ public class MethodReferenceHelperClass {
|
||||
public void memberFun0(JFunction0 f) {}
|
||||
public <T> void memberFun1(JFunction1<T> f) {}
|
||||
public <T, K> void memberFun2(JFunction2<T, K> f) {}
|
||||
}
|
||||
|
||||
public class JavaClassWithProperties {
|
||||
public int getValue1() { return 1; }
|
||||
|
||||
public int getValue2() { return 1; }
|
||||
public void setValue2(int value) { }
|
||||
|
||||
public int getValue3() { return 1; }
|
||||
public void setValue3(int value) { }
|
||||
|
||||
public int getValue4() { return 1; }
|
||||
public void setValue4(int value) { }
|
||||
}
|
||||
Vendored
+22
-1
@@ -46,4 +46,25 @@ public object KotlinObject {
|
||||
public var property2: Int
|
||||
get() = 1
|
||||
set(value) {}
|
||||
}
|
||||
}
|
||||
|
||||
public open class KotlinClassWithProperties {
|
||||
public open var someVar1: String = ""
|
||||
public open var someVar2: String = ""
|
||||
public open var someVar3: String = ""
|
||||
public open var someVar4: String
|
||||
get() = ""
|
||||
set(value) {}
|
||||
public open val someVal: String = ""
|
||||
|
||||
public open fun getSomething1() { return 1; }
|
||||
|
||||
public open fun getSomething2() { return 1; }
|
||||
public open fun setSomething2(value: Int) { }
|
||||
|
||||
public open fun getSomething3() { return 1; }
|
||||
public open fun setSomething3(value: Int) { }
|
||||
|
||||
public open fun getSomething4() { return 1; }
|
||||
public open fun setSomething4(value: Int) { }
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ internal object Library {
|
||||
fun call() {
|
||||
}
|
||||
|
||||
fun getString(): String {
|
||||
return ""
|
||||
}
|
||||
val string: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
internal class User {
|
||||
fun main() {
|
||||
Library.call()
|
||||
Library.getString().isEmpty()
|
||||
Library.string.isEmpty()
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -4,18 +4,19 @@ internal class Library {
|
||||
fun call() {
|
||||
}
|
||||
|
||||
fun getString(): String? {
|
||||
return ""
|
||||
}
|
||||
val string: String?
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
internal class User {
|
||||
fun main() {
|
||||
val lib: Library = Library()
|
||||
lib.call()
|
||||
lib.getString()!!.isEmpty()
|
||||
lib.string!!.isEmpty()
|
||||
|
||||
Library().call()
|
||||
Library().getString()!!.isEmpty()
|
||||
Library().string!!.isEmpty()
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -2,18 +2,19 @@ internal class Library {
|
||||
fun call() {
|
||||
}
|
||||
|
||||
fun getString(): String {
|
||||
return ""
|
||||
}
|
||||
val string: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
internal class User {
|
||||
fun main() {
|
||||
val lib = Library()
|
||||
lib.call()
|
||||
lib.getString().isEmpty()
|
||||
lib.string.isEmpty()
|
||||
|
||||
Library().call()
|
||||
Library().getString().isEmpty()
|
||||
Library().string.isEmpty()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,5 @@
|
||||
internal interface I {
|
||||
fun getX(): Int
|
||||
fun setX(x: Int)
|
||||
var x: Int
|
||||
}
|
||||
|
||||
internal class A(private var x: Int) : I {
|
||||
|
||||
override fun getX(): Int {
|
||||
return x
|
||||
}
|
||||
|
||||
override fun setX(x: Int) {
|
||||
this.x = x
|
||||
}
|
||||
}
|
||||
internal class A(override var x: Int) : I
|
||||
+3
-5
@@ -1,8 +1,6 @@
|
||||
class AAA {
|
||||
var x = 42
|
||||
private set
|
||||
|
||||
fun setX(x: Int) {
|
||||
this.x += x
|
||||
}
|
||||
set(x: Int) {
|
||||
this.x += x
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
internal class A {
|
||||
private val s: String? = null
|
||||
|
||||
fun getValue(): Any {
|
||||
return s
|
||||
}
|
||||
val value: Any
|
||||
get() {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
interface I {
|
||||
int getSomething1();
|
||||
|
||||
int getSomething2();
|
||||
void setSomething2(int value);
|
||||
|
||||
void setSomething3(int value);
|
||||
|
||||
int getSomething4();
|
||||
void setSomething4(String value);
|
||||
|
||||
int getSomething5();
|
||||
int setSomething5(int value);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
internal interface I {
|
||||
val something1: Int
|
||||
|
||||
var something2: Int
|
||||
|
||||
fun setSomething3(value: Int)
|
||||
|
||||
val something4: Int
|
||||
fun setSomething4(value: String)
|
||||
|
||||
val something5: Int
|
||||
fun setSomething5(value: Int): Int
|
||||
}
|
||||
+4
-5
@@ -1,11 +1,10 @@
|
||||
object AAA {
|
||||
var x = 42
|
||||
var y = 0
|
||||
val z = 0
|
||||
|
||||
fun setZ(z: Int) {
|
||||
Other.z = z
|
||||
}
|
||||
var z = 0
|
||||
set(z: Int) {
|
||||
Other.z = z
|
||||
}
|
||||
}
|
||||
|
||||
internal object Other {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
interface I {
|
||||
int getSomething1();
|
||||
|
||||
int getSomething2();
|
||||
|
||||
int getSomething3();
|
||||
void setSomething3(int value);
|
||||
|
||||
int getSomething4();
|
||||
void setSomething4(int value);
|
||||
|
||||
int getSomething5();
|
||||
void setSomething5(int value);
|
||||
|
||||
void setSomething6(int value);
|
||||
}
|
||||
|
||||
class B {
|
||||
public String getFromB1() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getFromB2() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setFromB2(String value) {
|
||||
}
|
||||
|
||||
public String getFromB3() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setFromB3(String value) {
|
||||
}
|
||||
|
||||
public String getFromB4() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setFromB4(String value) {
|
||||
}
|
||||
|
||||
public void setFromB5(String value) {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class C extends B implements I {
|
||||
private final int mySomething1;
|
||||
private int mySomething6;
|
||||
|
||||
C(int something1) {
|
||||
mySomething1 = something1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSomething1() {
|
||||
return mySomething1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSomething2() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSomething3() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSomething3(int value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSomething4() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSomething5(int value) {
|
||||
|
||||
}
|
||||
|
||||
public int getSomething6() {
|
||||
return mySomething6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSomething6(int value) {
|
||||
mySomething6 = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromB1() {
|
||||
return super.getFromB1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromB2() {
|
||||
return super.getFromB2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFromB2(String value) {
|
||||
super.setFromB2(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromB3() {
|
||||
return super.getFromB3();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFromB4(String value) {
|
||||
super.setFromB4(value);
|
||||
}
|
||||
|
||||
public String getFromB5() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFromB5(String value) {
|
||||
super.setFromB5(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// ERROR: Property must be initialized
|
||||
// ERROR: Property must be initialized
|
||||
// ERROR: Property must be initialized
|
||||
// ERROR: Property must be initialized
|
||||
internal interface I {
|
||||
val something1: Int
|
||||
|
||||
val something2: Int
|
||||
|
||||
var something3: Int
|
||||
|
||||
var something4: Int
|
||||
|
||||
var something5: Int
|
||||
|
||||
fun setSomething6(value: Int)
|
||||
}
|
||||
|
||||
internal open class B {
|
||||
open val fromB1: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
|
||||
open var fromB2: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
set(value: String) {
|
||||
}
|
||||
|
||||
open var fromB3: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
set(value: String) {
|
||||
}
|
||||
|
||||
open var fromB4: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
set(value: String) {
|
||||
}
|
||||
|
||||
open fun setFromB5(value: String) {
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class C(override val something1: Int) : B(), I {
|
||||
private var mySomething6: Int = 0
|
||||
|
||||
override val something2: Int
|
||||
get() {
|
||||
return 0
|
||||
}
|
||||
|
||||
override var something3: Int
|
||||
get() {
|
||||
return 0
|
||||
}
|
||||
set(value: Int) {
|
||||
}
|
||||
|
||||
override var something4: Int
|
||||
get() {
|
||||
return 0
|
||||
}
|
||||
|
||||
override var something5: Int
|
||||
set(value: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun getSomething6(): Int {
|
||||
return mySomething6
|
||||
}
|
||||
|
||||
override fun setSomething6(value: Int) {
|
||||
mySomething6 = value
|
||||
}
|
||||
|
||||
override val fromB1: String
|
||||
get() {
|
||||
return super.fromB1
|
||||
}
|
||||
|
||||
override var fromB2: String
|
||||
get() {
|
||||
return super.fromB2
|
||||
}
|
||||
set(value: String) {
|
||||
super.fromB2 = value
|
||||
}
|
||||
|
||||
override var fromB3: String
|
||||
get() {
|
||||
return super.fromB3
|
||||
}
|
||||
|
||||
override var fromB4: String
|
||||
set(value: String) {
|
||||
super.fromB4 = value
|
||||
}
|
||||
|
||||
val fromB5: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
|
||||
override fun setFromB5(value: String) {
|
||||
super.setFromB5(value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import kotlinApi.KotlinClassWithProperties;
|
||||
import javaApi.JavaClassWithProperties;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.Override;
|
||||
import java.lang.String;
|
||||
|
||||
class A extends KotlinClassWithProperties {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSomeVar1() {
|
||||
return super.getSomeVar1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSomeVar1(@NotNull String s) {
|
||||
super.setSomeVar1(s);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSomeVar2() {
|
||||
return super.getSomeVar2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSomeVar3(@NotNull String s) {
|
||||
super.setSomeVar3(s);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSomeVar4() {
|
||||
return super.getSomeVar4();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSomeVal() {
|
||||
return super.getSomeVal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSomething1() {
|
||||
super.getSomething1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSomething2() {
|
||||
super.getSomething2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSomething2(int value) {
|
||||
super.setSomething2(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSomething3() {
|
||||
super.getSomething3();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSomething4(int value) {
|
||||
super.setSomething4(value);
|
||||
}
|
||||
}
|
||||
|
||||
class B extends JavaClassWithProperties {
|
||||
@Override
|
||||
public int getValue1() {
|
||||
return super.getValue1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValue2() {
|
||||
return super.getValue2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue2(int value) {
|
||||
super.setValue2(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValue3() {
|
||||
return super.getValue3();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue4(int value) {
|
||||
super.setValue4(value);
|
||||
}
|
||||
}
|
||||
|
||||
class C extends A {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSomeVar1() {
|
||||
return super.getSomeVar1();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// ERROR: Property must be initialized
|
||||
// ERROR: Property must be initialized
|
||||
// ERROR: Property must be initialized
|
||||
// ERROR: Property must be initialized
|
||||
import kotlinApi.KotlinClassWithProperties
|
||||
import javaApi.JavaClassWithProperties
|
||||
|
||||
internal open class A : KotlinClassWithProperties() {
|
||||
override var someVar1: String
|
||||
get() {
|
||||
return super.someVar1
|
||||
}
|
||||
set(s: String) {
|
||||
super.someVar1 = s
|
||||
}
|
||||
|
||||
override var someVar2: String
|
||||
get() {
|
||||
return super.someVar2
|
||||
}
|
||||
|
||||
override var someVar3: String
|
||||
set(s: String) {
|
||||
super.someVar3 = s
|
||||
}
|
||||
|
||||
override var someVar4: String
|
||||
get() {
|
||||
return super.someVar4
|
||||
}
|
||||
|
||||
override val someVal: String
|
||||
get() {
|
||||
return super.someVal
|
||||
}
|
||||
|
||||
override fun getSomething1() {
|
||||
super.getSomething1()
|
||||
}
|
||||
|
||||
override fun getSomething2() {
|
||||
super.getSomething2()
|
||||
}
|
||||
|
||||
override fun setSomething2(value: Int) {
|
||||
super.setSomething2(value)
|
||||
}
|
||||
|
||||
override fun getSomething3() {
|
||||
super.getSomething3()
|
||||
}
|
||||
|
||||
override fun setSomething4(value: Int) {
|
||||
super.setSomething4(value)
|
||||
}
|
||||
}
|
||||
|
||||
internal class B : JavaClassWithProperties() {
|
||||
override fun getValue1(): Int {
|
||||
return super.getValue1()
|
||||
}
|
||||
|
||||
override fun getValue2(): Int {
|
||||
return super.getValue2()
|
||||
}
|
||||
|
||||
override fun setValue2(value: Int) {
|
||||
super.setValue2(value)
|
||||
}
|
||||
|
||||
override fun getValue3(): Int {
|
||||
return super.getValue3()
|
||||
}
|
||||
|
||||
override fun setValue4(value: Int) {
|
||||
super.setValue4(value)
|
||||
}
|
||||
}
|
||||
|
||||
internal class C : A() {
|
||||
override var someVar1: String
|
||||
get() {
|
||||
return super.someVar1
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
internal class A {
|
||||
private var i: Int? = getByte().toInt()
|
||||
private var i: Int? = byte.toInt()
|
||||
|
||||
fun foo() {
|
||||
i = 10
|
||||
@@ -7,8 +7,9 @@ internal class A {
|
||||
|
||||
companion object {
|
||||
|
||||
fun getByte(): Byte {
|
||||
return 0
|
||||
}
|
||||
val byte: Byte
|
||||
get() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
internal abstract fun getNoofGears(): Int
|
||||
internal abstract val noofGears: Int
|
||||
@@ -1,2 +1,3 @@
|
||||
fun getT(): T {
|
||||
}
|
||||
val t: T
|
||||
get() {
|
||||
}
|
||||
+4
-3
@@ -1,3 +1,4 @@
|
||||
fun getString(): String {
|
||||
return ""
|
||||
}
|
||||
val string: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
fun isTrue(): Boolean {
|
||||
return true
|
||||
}
|
||||
val isTrue: Boolean
|
||||
get() {
|
||||
return true
|
||||
}
|
||||
+4
-3
@@ -1,3 +1,4 @@
|
||||
fun getString(): String {
|
||||
return ""
|
||||
}
|
||||
val string: String
|
||||
get() {
|
||||
return ""
|
||||
}
|
||||
+5
-4
@@ -1,6 +1,7 @@
|
||||
internal class Test {
|
||||
fun getInt(): Int {
|
||||
val b = 10
|
||||
return b.toInt()
|
||||
}
|
||||
val int: Int
|
||||
get() {
|
||||
val b = 10
|
||||
return b.toInt()
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
internal interface I {
|
||||
fun getString(): String?
|
||||
val string: String?
|
||||
}
|
||||
|
||||
internal class C {
|
||||
fun foo(i: I) {
|
||||
if (i.getString() == null) {
|
||||
if (i.string == null) {
|
||||
println("null")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
internal interface I {
|
||||
fun getString(): String?
|
||||
val string: String?
|
||||
}
|
||||
|
||||
internal class C {
|
||||
fun foo(i: I) {
|
||||
val result = i.getString()
|
||||
val result = i.string
|
||||
if (result != null) {
|
||||
print(result)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
internal interface I {
|
||||
fun getString(): String?
|
||||
val string: String?
|
||||
}
|
||||
|
||||
internal class C {
|
||||
fun foo(i: I) {
|
||||
val result = i.getString()
|
||||
val result = i.string
|
||||
if (result != null) {
|
||||
print(result)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
internal interface I {
|
||||
fun getString(): String
|
||||
val string: String
|
||||
}
|
||||
|
||||
internal class C {
|
||||
fun foo(i: I, b: Boolean) {
|
||||
var result: String? = i.getString()
|
||||
var result: String? = i.string
|
||||
if (b) result = null
|
||||
if (result != null) {
|
||||
print(result)
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
internal interface I {
|
||||
fun getInt(): Int
|
||||
val int: Int
|
||||
}
|
||||
|
||||
internal class C {
|
||||
fun getObject(): Any? {
|
||||
foo(object : I {
|
||||
override fun getInt(): Int {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
return string
|
||||
}
|
||||
val `object`: Any?
|
||||
get() {
|
||||
foo(object : I {
|
||||
override val int: Int
|
||||
get() {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
return string
|
||||
}
|
||||
|
||||
fun foo(i: I) {
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
internal interface INode {
|
||||
fun getTag(): Tag
|
||||
val tag: Tag
|
||||
fun toKotlin(): String
|
||||
}
|
||||
+1
-1
@@ -6,6 +6,6 @@ class AAA {
|
||||
}
|
||||
|
||||
fun bar(b: B) {
|
||||
println(b.YY)
|
||||
println(b.yy)
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
internal class B {
|
||||
fun foo(a: AAA) {
|
||||
a.x = a.x + 1
|
||||
YY += "a"
|
||||
yy += "a"
|
||||
}
|
||||
|
||||
var YY = ""
|
||||
var yy = ""
|
||||
private set
|
||||
}
|
||||
@@ -1597,6 +1597,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InInterface.java")
|
||||
public void testInInterface() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InInterface.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InObject.java")
|
||||
public void testInObject() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InObject.java");
|
||||
@@ -1615,6 +1621,18 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Overrides.java")
|
||||
public void testOverrides() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/Overrides.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OverridesOfExternalCode.java")
|
||||
public void testOverridesOfExternalCode() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SetterTypeNotMatch.java")
|
||||
public void testSetterTypeNotMatch() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/SetterTypeNotMatch.java");
|
||||
|
||||
@@ -1597,6 +1597,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InInterface.java")
|
||||
public void testInInterface() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InInterface.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("InObject.java")
|
||||
public void testInObject() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/InObject.java");
|
||||
@@ -1615,6 +1621,18 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Overrides.java")
|
||||
public void testOverrides() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/Overrides.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("OverridesOfExternalCode.java")
|
||||
public void testOverridesOfExternalCode() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.java");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("SetterTypeNotMatch.java")
|
||||
public void testSetterTypeNotMatch() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/dropAccessors/SetterTypeNotMatch.java");
|
||||
|
||||
Reference in New Issue
Block a user