diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt index ed933c4ab47..99f07fd3302 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt @@ -348,7 +348,7 @@ class Converter private constructor( getter = PropertyAccessor(AccessorKind.GETTER, method.annotations, Modifiers.Empty, method.parameterList, method.body) getter.assignPrototype(getMethod, CommentsAndSpacesInheritance.NO_SPACES) } - else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE)) { + else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) { val superExpression = SuperExpression(Identifier.Empty).assignNoPrototype() val superAccess = QualifiedExpression(superExpression, propertyInfo.identifier).assignNoPrototype() val returnStatement = ReturnStatement(superAccess).assignNoPrototype() @@ -357,7 +357,10 @@ class Converter private constructor( getter = PropertyAccessor(AccessorKind.GETTER, Annotations.Empty, Modifiers.Empty, parameterList, deferredElement { body }) getter.assignNoPrototype() } - //TODO: what else? + else { + //TODO: what else? + getter = PropertyAccessor(AccessorKind.GETTER, Annotations.Empty, Modifiers.Empty, null, null).assignNoPrototype() + } } var setter: PropertyAccessor? = null @@ -378,7 +381,7 @@ class Converter private constructor( setter = PropertyAccessor(AccessorKind.SETTER, method.annotations, accessorModifiers, parameterList, method.body) setter.assignPrototype(setMethod, CommentsAndSpacesInheritance.NO_SPACES) } - else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE)) { + else if (propertyInfo.modifiers.contains(Modifier.OVERRIDE) && !(propertyInfo.superInfo?.isAbstract() ?: false)) { val superExpression = SuperExpression(Identifier.Empty).assignNoPrototype() val superAccess = QualifiedExpression(superExpression, propertyInfo.identifier).assignNoPrototype() val valueIdentifier = Identifier("value", false).assignNoPrototype() diff --git a/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt b/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt index 63e5cb0c14f..3aa2cba3997 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/propertyDetection.kt @@ -20,6 +20,7 @@ import com.intellij.psi.* import com.intellij.psi.util.MethodSignatureUtil import org.jetbrains.kotlin.asJava.KtLightMethod import org.jetbrains.kotlin.j2k.ast.* +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtProperty @@ -38,7 +39,8 @@ class PropertyInfo( val isGetMethodBodyFieldAccess: Boolean, val isSetMethodBodyFieldAccess: Boolean, val modifiers: Modifiers, - val specialSetterAccess: Modifier? + val specialSetterAccess: Modifier?, + val superInfo: SuperInfo? ) { init { assert(field != null || getMethod != null || setMethod != null) @@ -73,7 +75,7 @@ class PropertyInfo( fun fromFieldWithNoAccessors(field: PsiField, converter: Converter): PropertyInfo { val isVar = field.isVar(converter.referenceSearcher) val modifiers = converter.convertModifiers(field, false) - return PropertyInfo(field.declarationIdentifier(), isVar, field.type, field, null, null, false, false, modifiers, null) + return PropertyInfo(field.declarationIdentifier(), isVar, field.type, field, null, null, false, false, modifiers, null, null) } } } @@ -92,6 +94,21 @@ class PropertyDetectionCache(private val converter: Converter) { } } +sealed class SuperInfo { + class Property( + val isVar: Boolean, + val name: String, + val hasAbstractModifier: Boolean + //TODO: add visibility + ) : SuperInfo() + + object Function : SuperInfo() + + fun isAbstract(): Boolean { + return if (this is Property) hasAbstractModifier else false + } +} + private class PropertyDetector( private val psiClass: PsiClass, private val converter: Converter @@ -151,7 +168,8 @@ private class PropertyDetector( val type = getterInfo?.method?.returnType ?: setterInfo!!.method.parameterList.parameters.single()?.type!! - val isOverride = getterInfo?.superProperty != null || setterInfo?.superProperty != null + val superProperty = getterInfo?.superProperty ?: setterInfo?.superProperty + val isOverride = superProperty != null val modifiers = convertModifiers(field, getterInfo?.method, setterInfo?.method, isOverride) @@ -173,7 +191,8 @@ private class PropertyDetector( field != null && getterInfo?.field == field, field != null && setterInfo?.field == field, modifiers, - specialSetterAccess) + specialSetterAccess, + superProperty) if (field != null) { memberToPropertyInfo[field] = propertyInfo @@ -329,17 +348,6 @@ private class PropertyDetector( val superProperty: SuperInfo.Property? ) - private sealed class SuperInfo { - class Property( - val isVar: Boolean, - val name: String - //TODO: add visibility - ) : SuperInfo() - - object Function : SuperInfo() - - } - private fun getGetterInfo(method: PsiMethod, superProperty: SuperInfo.Property?): AccessorInfo? { val propertyName = propertyNameByGetMethod(method) ?: return null val field = fieldFromGetterBody(method) @@ -364,11 +372,11 @@ private class PropertyDetector( val containingClass = superMethod.containingClass!! if (converter.inConversionScope(containingClass)) { val propertyInfo = converter.propertyDetectionCache[containingClass][superMethod] - return if (propertyInfo != null) SuperInfo.Property(propertyInfo.isVar, propertyInfo.name) else SuperInfo.Function + return if (propertyInfo != null) SuperInfo.Property(propertyInfo.isVar, propertyInfo.name, propertyInfo.modifiers.contains(Modifier.ABSTRACT)) else SuperInfo.Function } else if (superMethod is KtLightMethod) { val origin = superMethod.kotlinOrigin - return if (origin is KtProperty) SuperInfo.Property(origin.isVar, origin.name ?: "") else SuperInfo.Function + return if (origin is KtProperty) SuperInfo.Property(origin.isVar, origin.name ?: "", origin.hasModifier(KtTokens.ABSTRACT_KEYWORD)) else SuperInfo.Function } else { return SuperInfo.Function diff --git a/j2k/testData/KotlinApi.kt b/j2k/testData/KotlinApi.kt index 2297aa8f518..cfa7c84b178 100644 --- a/j2k/testData/KotlinApi.kt +++ b/j2k/testData/KotlinApi.kt @@ -70,3 +70,7 @@ public open class KotlinClassWithProperties { public open fun getSomething4() { return 1; } public open fun setSomething4(value: Int) { } } + +public abstract class KotlinClassAbstractProperty { + abstract val isVisible: Boolean +} diff --git a/j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.java b/j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.java new file mode 100644 index 00000000000..cc048448880 --- /dev/null +++ b/j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.java @@ -0,0 +1,16 @@ +package test; + +import kotlinApi.*; + +public class KotlinClassAbstractPropertyImpl extends KotlinClassAbstractProperty { + private boolean myIsVisible; + + @Override + public boolean isVisible() { + return myIsVisible; + } + + private void test() { + myIsVisible = true; + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.kt b/j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.kt new file mode 100644 index 00000000000..5dcc4eb16b2 --- /dev/null +++ b/j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.kt @@ -0,0 +1,12 @@ +package test + +import kotlinApi.* + +class KotlinClassAbstractPropertyImpl : KotlinClassAbstractProperty() { + override var isVisible: Boolean = false + private set + + private fun test() { + isVisible = true + } +} diff --git a/j2k/testData/fileOrElement/detectProperties/IsPrefix.kt b/j2k/testData/fileOrElement/detectProperties/IsPrefix.kt index fced0168d5d..eeb8cb7c277 100644 --- a/j2k/testData/fileOrElement/detectProperties/IsPrefix.kt +++ b/j2k/testData/fileOrElement/detectProperties/IsPrefix.kt @@ -1,6 +1,6 @@ -// ERROR: Abstract member cannot be accessed directly -// ERROR: Abstract member cannot be accessed directly -// ERROR: Abstract member cannot be accessed directly +// ERROR: Property must be initialized +// ERROR: Property must be initialized +// ERROR: Property must be initialized internal interface I { val isSomething1: Boolean @@ -23,17 +23,15 @@ internal abstract class C : I { override var isSomething4: Boolean get() = false - set(value: Boolean) { - super.isSomething4 = value - } + set override var isSomething5: Boolean - get() = super.isSomething5 + get set(value) { } override var something6: Boolean - get() = super.something6 + get set(value) { } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/detectProperties/Overrides.kt b/j2k/testData/fileOrElement/detectProperties/Overrides.kt index 7cff81cc377..e324840fc30 100644 --- a/j2k/testData/fileOrElement/detectProperties/Overrides.kt +++ b/j2k/testData/fileOrElement/detectProperties/Overrides.kt @@ -1,5 +1,5 @@ -// ERROR: Abstract member cannot be accessed directly -// ERROR: Abstract member cannot be accessed directly +// ERROR: Property must be initialized +// ERROR: Property must be initialized internal interface I { val something1: Int @@ -56,12 +56,10 @@ internal abstract class C(override val something1: Int) : B(), I { override var something4: Int get() = 0 - set(value: Int) { - super.something4 = value - } + set override var something5: Int - get() = super.something5 + get set(value) { } diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index 0e5cc53a351..a544efd0f2c 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -1368,6 +1368,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class DetectProperties extends AbstractJavaToKotlinConverterForWebDemoTest { + @TestMetadata("AbstractPropertyPrivateSetter.java") + public void testAbstractPropertyPrivateSetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.java"); + doTest(fileName); + } + @TestMetadata("AccessInGetterWithThis.java") public void testAccessInGetterWithThis() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/detectProperties/AccessInGetterWithThis.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 74359756b27..eac4836952d 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -1368,6 +1368,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class DetectProperties extends AbstractJavaToKotlinConverterSingleFileTest { + @TestMetadata("AbstractPropertyPrivateSetter.java") + public void testAbstractPropertyPrivateSetter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/detectProperties/AbstractPropertyPrivateSetter.java"); + doTest(fileName); + } + @TestMetadata("AccessInGetterWithThis.java") public void testAccessInGetterWithThis() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/detectProperties/AccessInGetterWithThis.java");