J2K: get/set-methods converted to properties (but lot of TODOs left)

This commit is contained in:
Valentin Kipyatkov
2015-09-23 00:54:48 +03:00
parent 3f6cadf9b7
commit 320102bbdb
41 changed files with 1266 additions and 298 deletions
@@ -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),
+148 -46
View File
@@ -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
}
}
+13
View File
@@ -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) { }
}
+22 -1
View File
@@ -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()
}
}
@@ -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()
}
}
@@ -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
View File
@@ -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);
}
+13
View File
@@ -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
View File
@@ -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 {
+127
View File
@@ -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);
}
}
+114
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
internal abstract fun getNoofGears(): Int
internal abstract val noofGears: Int
+3 -2
View File
@@ -1,2 +1,3 @@
fun getT(): T {
}
val t: T
get() {
}
+4 -3
View File
@@ -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
View File
@@ -1,3 +1,4 @@
fun getString(): String {
return ""
}
val string: String
get() {
return ""
}
+5 -4
View File
@@ -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
View File
@@ -6,6 +6,6 @@ class AAA {
}
fun bar(b: B) {
println(b.YY)
println(b.yy)
}
}
+2 -2
View File
@@ -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");