diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt index 7374697b9f8..8808068c7ed 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ClassBodyConverter.kt @@ -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() - private val fieldCorrections = HashMap() + private val classKind: ClassKind, + private val converter: Converter +) { + private val fieldsToDrop = HashSet() 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, - 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 + ): 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() - val fieldToSetterInfo = HashMap() - val fieldsWithConflict = HashSet() - 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 - } } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt index 7e65fab0399..b9a2ba008cd 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt @@ -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) } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ConstructorConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ConstructorConverter.kt index 0a98fbadf5d..17a24848131 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ConstructorConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ConstructorConverter.kt @@ -24,8 +24,9 @@ import java.util.* class ConstructorConverter( private val psiClass: PsiClass, private val converter: Converter, - private val fieldCorrections: Map, - 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, + fieldsToDrop: MutableSet, 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, + fieldsToDrop: MutableSet, postProcessBody: (Block) -> Block): PrimaryConstructor { val params = primaryConstructor!!.getParameterList().getParameters() val parameterToField = HashMap>() @@ -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), diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt index 9317dd8c6d5..2adb642be4a 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt @@ -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())).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?, + fieldsToDrop: MutableSet?, 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() 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() + + //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(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) } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/ClassBody.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/ClassBody.kt index 97ee5abba27..03222ac6030 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/ClassBody.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/ClassBody.kt @@ -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, 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 { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Constructors.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Constructors.kt index 55bfc95cf58..e04dd0b5255 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Constructors.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Constructors.kt @@ -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 -) : FunctionLike(annotations, modifiers, parameterList, body) +) : FunctionLike(annotations, modifiers, parameterList, body) { + override val parameterList: ParameterList + get() = super.parameterList!! +} class PrimaryConstructor( annotations: Annotations, diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Function.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Function.kt index cbbbe444b73..9917ad4ed79 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Function.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Function.kt @@ -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? -) : 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?, - 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) diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Property.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Property.kt index 70c300d3ae2..61364513fc6 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Property.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Property.kt @@ -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, val isVal: Boolean, + val type: Type, val explicitType: Boolean, - private val defaultInitializer: Boolean, - val setterAccess: Modifier? + private val initializer: DeferredElement, + 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? +) : 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 } } } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt b/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt new file mode 100644 index 00000000000..8a3fca9233a --- /dev/null +++ b/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt @@ -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>() + + fun get(psiClass: PsiClass): Map { + 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 { + val propertyNameToGetterInfo = LinkedHashMap() + val propertyNameToSetterInfo = LinkedHashMap() + val propertyNamesWithConflict = HashSet() + 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() + + 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) { + 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 + } +} \ No newline at end of file diff --git a/j2k/testData/JavaApi.java b/j2k/testData/JavaApi.java index 3b7e1691c53..f971e229b8e 100644 --- a/j2k/testData/JavaApi.java +++ b/j2k/testData/JavaApi.java @@ -90,4 +90,17 @@ public class MethodReferenceHelperClass { public void memberFun0(JFunction0 f) {} public void memberFun1(JFunction1 f) {} public void memberFun2(JFunction2 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) { } } \ No newline at end of file diff --git a/j2k/testData/KotlinApi.kt b/j2k/testData/KotlinApi.kt index ca753774907..79615235cbf 100644 --- a/j2k/testData/KotlinApi.kt +++ b/j2k/testData/KotlinApi.kt @@ -46,4 +46,25 @@ public object KotlinObject { public var property2: Int get() = 1 set(value) {} -} \ No newline at end of file +} + +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) { } +} diff --git a/j2k/testData/fileOrElement/callChainExpression/libraryMethodCall.kt b/j2k/testData/fileOrElement/callChainExpression/libraryMethodCall.kt index acbc341fb8f..7a22d376b62 100644 --- a/j2k/testData/fileOrElement/callChainExpression/libraryMethodCall.kt +++ b/j2k/testData/fileOrElement/callChainExpression/libraryMethodCall.kt @@ -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() } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance-settings.kt b/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance-settings.kt index 0362fc28e3a..3e1a523f2b2 100644 --- a/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance-settings.kt +++ b/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance-settings.kt @@ -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() } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance.kt b/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance.kt index f4e98f83358..2030c23e08e 100644 --- a/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance.kt +++ b/j2k/testData/fileOrElement/callChainExpression/libraryMethodCallFromInstance.kt @@ -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() } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/dropAccessors/AccessorsImplementInterface.kt b/j2k/testData/fileOrElement/dropAccessors/AccessorsImplementInterface.kt index e9637970db8..c591a398136 100644 --- a/j2k/testData/fileOrElement/dropAccessors/AccessorsImplementInterface.kt +++ b/j2k/testData/fileOrElement/dropAccessors/AccessorsImplementInterface.kt @@ -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 \ No newline at end of file diff --git a/j2k/testData/fileOrElement/dropAccessors/FalseSetter.kt b/j2k/testData/fileOrElement/dropAccessors/FalseSetter.kt index 4d7aaa28c11..f0c9a2c1713 100644 --- a/j2k/testData/fileOrElement/dropAccessors/FalseSetter.kt +++ b/j2k/testData/fileOrElement/dropAccessors/FalseSetter.kt @@ -1,8 +1,6 @@ class AAA { var x = 42 - private set - - fun setX(x: Int) { - this.x += x - } + set(x: Int) { + this.x += x + } } diff --git a/j2k/testData/fileOrElement/dropAccessors/GetterTypeNotMatch.kt b/j2k/testData/fileOrElement/dropAccessors/GetterTypeNotMatch.kt index a7cfbc7390a..9dc7fee2233 100644 --- a/j2k/testData/fileOrElement/dropAccessors/GetterTypeNotMatch.kt +++ b/j2k/testData/fileOrElement/dropAccessors/GetterTypeNotMatch.kt @@ -2,7 +2,8 @@ internal class A { private val s: String? = null - fun getValue(): Any { - return s - } + val value: Any + get() { + return s + } } diff --git a/j2k/testData/fileOrElement/dropAccessors/InInterface.java b/j2k/testData/fileOrElement/dropAccessors/InInterface.java new file mode 100644 index 00000000000..a193f8a4464 --- /dev/null +++ b/j2k/testData/fileOrElement/dropAccessors/InInterface.java @@ -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); +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/dropAccessors/InInterface.kt b/j2k/testData/fileOrElement/dropAccessors/InInterface.kt new file mode 100644 index 00000000000..69ae5cab59f --- /dev/null +++ b/j2k/testData/fileOrElement/dropAccessors/InInterface.kt @@ -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 +} diff --git a/j2k/testData/fileOrElement/dropAccessors/InObject.kt b/j2k/testData/fileOrElement/dropAccessors/InObject.kt index ed43f97de13..c7abb9ff612 100644 --- a/j2k/testData/fileOrElement/dropAccessors/InObject.kt +++ b/j2k/testData/fileOrElement/dropAccessors/InObject.kt @@ -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 { diff --git a/j2k/testData/fileOrElement/dropAccessors/Overrides.java b/j2k/testData/fileOrElement/dropAccessors/Overrides.java new file mode 100644 index 00000000000..5d447629074 --- /dev/null +++ b/j2k/testData/fileOrElement/dropAccessors/Overrides.java @@ -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); + } +} diff --git a/j2k/testData/fileOrElement/dropAccessors/Overrides.kt b/j2k/testData/fileOrElement/dropAccessors/Overrides.kt new file mode 100644 index 00000000000..e7c6a9443a5 --- /dev/null +++ b/j2k/testData/fileOrElement/dropAccessors/Overrides.kt @@ -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) + } +} diff --git a/j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.java b/j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.java new file mode 100644 index 00000000000..9b58ee9a702 --- /dev/null +++ b/j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.java @@ -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(); + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.kt b/j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.kt new file mode 100644 index 00000000000..5fc60c63faf --- /dev/null +++ b/j2k/testData/fileOrElement/dropAccessors/OverridesOfExternalCode.kt @@ -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 + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/field/conversion.kt b/j2k/testData/fileOrElement/field/conversion.kt index 5c506b49cdb..db8229e4308 100644 --- a/j2k/testData/fileOrElement/field/conversion.kt +++ b/j2k/testData/fileOrElement/field/conversion.kt @@ -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 + } } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/function/abstractMethod.kt b/j2k/testData/fileOrElement/function/abstractMethod.kt index 12c044ea709..a511833eda8 100644 --- a/j2k/testData/fileOrElement/function/abstractMethod.kt +++ b/j2k/testData/fileOrElement/function/abstractMethod.kt @@ -1 +1 @@ -internal abstract fun getNoofGears(): Int \ No newline at end of file +internal abstract val noofGears: Int \ No newline at end of file diff --git a/j2k/testData/fileOrElement/function/classGenericParam.kt b/j2k/testData/fileOrElement/function/classGenericParam.kt index 9286b7acb4b..c4291e5a69a 100644 --- a/j2k/testData/fileOrElement/function/classGenericParam.kt +++ b/j2k/testData/fileOrElement/function/classGenericParam.kt @@ -1,2 +1,3 @@ -fun getT(): T { -} \ No newline at end of file +val t: T + get() { + } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/function/final.kt b/j2k/testData/fileOrElement/function/final.kt index 836f70823d6..d1c25ac9d65 100644 --- a/j2k/testData/fileOrElement/function/final.kt +++ b/j2k/testData/fileOrElement/function/final.kt @@ -1,3 +1,4 @@ -fun getString(): String { - return "" -} \ No newline at end of file +val string: String + get() { + return "" + } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/function/methodWithReturnStatement.kt b/j2k/testData/fileOrElement/function/methodWithReturnStatement.kt index f2cd6c4849c..b707a1331a3 100644 --- a/j2k/testData/fileOrElement/function/methodWithReturnStatement.kt +++ b/j2k/testData/fileOrElement/function/methodWithReturnStatement.kt @@ -1,3 +1,4 @@ -fun isTrue(): Boolean { - return true -} \ No newline at end of file +val isTrue: Boolean + get() { + return true + } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/function/open.kt b/j2k/testData/fileOrElement/function/open.kt index 836f70823d6..d1c25ac9d65 100644 --- a/j2k/testData/fileOrElement/function/open.kt +++ b/j2k/testData/fileOrElement/function/open.kt @@ -1,3 +1,4 @@ -fun getString(): String { - return "" -} \ No newline at end of file +val string: String + get() { + return "" + } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/issues/kt-794.kt b/j2k/testData/fileOrElement/issues/kt-794.kt index 5fbc2727308..18300f9bafa 100644 --- a/j2k/testData/fileOrElement/issues/kt-794.kt +++ b/j2k/testData/fileOrElement/issues/kt-794.kt @@ -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() + } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull.kt b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull.kt index b8c5d1e4dd3..c06f185323d 100644 --- a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull.kt +++ b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull.kt @@ -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") } } diff --git a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull2.kt b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull2.kt index 81b6eb8a822..b9bc29b02ec 100644 --- a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull2.kt +++ b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull2.kt @@ -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) } diff --git a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull3.kt b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull3.kt index 81b6eb8a822..b9bc29b02ec 100644 --- a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull3.kt +++ b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull3.kt @@ -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) } diff --git a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull4.kt b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull4.kt index 212e7b610b9..addf0dd3c14 100644 --- a/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull4.kt +++ b/j2k/testData/fileOrElement/nullability/MethodResultComparedWithNull4.kt @@ -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) diff --git a/j2k/testData/fileOrElement/returnStatement/currentMethodBug.kt b/j2k/testData/fileOrElement/returnStatement/currentMethodBug.kt index 5bcb73c8fcb..22bf7e09a9b 100644 --- a/j2k/testData/fileOrElement/returnStatement/currentMethodBug.kt +++ b/j2k/testData/fileOrElement/returnStatement/currentMethodBug.kt @@ -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) { } diff --git a/j2k/testData/fileOrElement/trait/interfaceWithMethodDeclaration.kt b/j2k/testData/fileOrElement/trait/interfaceWithMethodDeclaration.kt index 9d42e881704..0354a94ead9 100644 --- a/j2k/testData/fileOrElement/trait/interfaceWithMethodDeclaration.kt +++ b/j2k/testData/fileOrElement/trait/interfaceWithMethodDeclaration.kt @@ -1,4 +1,4 @@ internal interface INode { - fun getTag(): Tag + val tag: Tag fun toKotlin(): String } \ No newline at end of file diff --git a/j2k/testData/multiFile/GetterAndSetterUsages/1.kt b/j2k/testData/multiFile/GetterAndSetterUsages/1.kt index c9b4a91a22a..558b0507e05 100644 --- a/j2k/testData/multiFile/GetterAndSetterUsages/1.kt +++ b/j2k/testData/multiFile/GetterAndSetterUsages/1.kt @@ -6,6 +6,6 @@ class AAA { } fun bar(b: B) { - println(b.YY) + println(b.yy) } } \ No newline at end of file diff --git a/j2k/testData/multiFile/GetterAndSetterUsages/2.kt b/j2k/testData/multiFile/GetterAndSetterUsages/2.kt index 615d1838d59..6c49aeed79d 100644 --- a/j2k/testData/multiFile/GetterAndSetterUsages/2.kt +++ b/j2k/testData/multiFile/GetterAndSetterUsages/2.kt @@ -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 } \ No newline at end of file diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index e7314e47038..b61e644fbfd 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -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"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 69e77e87aa8..6965d974518 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.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");