diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/JKInMemoryFilesSearcher.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/JKInMemoryFilesSearcher.kt index a0a08d5652c..66779bdcd9c 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/JKInMemoryFilesSearcher.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/JKInMemoryFilesSearcher.kt @@ -12,7 +12,7 @@ import com.intellij.psi.search.searches.ReferencesSearch import org.jetbrains.kotlin.psi.KtElement internal abstract class JKInMemoryFilesSearcher { - abstract fun search(element: KtElement): Iterable + abstract fun search(element: KtElement, scope: PsiElement? = null): Iterable companion object { fun create(files: List) = when { @@ -23,8 +23,8 @@ internal abstract class JKInMemoryFilesSearcher { } internal class JKSingleFileInMemoryFilesSearcher(private val scopeElement: PsiElement) : JKInMemoryFilesSearcher() { - override fun search(element: KtElement): Iterable = - ReferencesSearch.search(element, LocalSearchScope(scopeElement)) + override fun search(element: KtElement, scope: PsiElement?): Iterable = + ReferencesSearch.search(element, LocalSearchScope(scope ?: scopeElement)) } @@ -32,7 +32,10 @@ internal class JKSingleFileInMemoryFilesSearcher(private val scopeElement: PsiEl // maybe the solution is to do searching manually // firstly by-text and then resolving internal class JKMultipleFilesInMemoryFilesSearcher(private val scopeElements: List) : JKInMemoryFilesSearcher() { - override fun search(element: KtElement): Iterable { + override fun search(element: KtElement, scope: PsiElement?): Iterable { + if (scope != null) { + return ReferencesSearch.search(element, LocalSearchScope(scope)) + } val result = mutableListOf() for (scopeElement in scopeElements) { result += ReferencesSearch.search(element, LocalSearchScope(scopeElement)) diff --git a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/ConvertGettersAndSettersToPropertyProcessing.kt b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/ConvertGettersAndSettersToPropertyProcessing.kt index 60c40f8c316..2d9f53d7923 100644 --- a/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/ConvertGettersAndSettersToPropertyProcessing.kt +++ b/nj2k/nj2k-services/src/org/jetbrains/kotlin/nj2k/postProcessing/processings/ConvertGettersAndSettersToPropertyProcessing.kt @@ -120,7 +120,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( val name = name?.asGetterName() ?: return null if (valueParameters.isNotEmpty()) return null if (typeParameters.isNotEmpty()) return null - val target = bodyExpression + val singleTimeUsedTarget = bodyExpression ?.statements() ?.singleOrNull() ?.safeAs() @@ -129,7 +129,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( ?.takeIf { it.type() == type() ?: return@takeIf false } - return RealGetter(this, target, name) + return RealGetter(this, singleTimeUsedTarget, name, singleTimeUsedTarget != null) } private fun KtNamedFunction.asSetter(): Setter? { @@ -138,7 +138,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( if (valueParameters.size != 1) return null val descriptor = resolveToDescriptorIfAny() ?: return null if (descriptor.returnType?.isUnit() != true) return null - val target = bodyExpression + val singleTimeUsedTarget = bodyExpression ?.statements() ?.singleOrNull() ?.let { expression -> @@ -151,7 +151,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( }?.takeIf { it.type() == valueParameters.single().type() } - return RealSetter(this, target, name) + return RealSetter(this, singleTimeUsedTarget, name, singleTimeUsedTarget != null) } private fun KtDeclaration.asPropertyAccessor(): PropertyInfo? { @@ -262,18 +262,17 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( else -> false } - private fun KtElement.usages() = - searcher.search(this) + private fun KtElement.usages(scope: PsiElement? = null) = + searcher.search(this, scope) private fun T.withReplacedExpressionInBody( from: KtElement, to: KtExpression, replaceOnlyWriteUsages: Boolean ): T = also { - from.usages() + from.usages(this) .asSequence() .map { it.element } - .filter { it.isInsideOf(listOf(this)) } .forEach { reference -> val parent = reference.parent val referenceExpression = when { @@ -343,6 +342,14 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( return calculatePropertyType(getterType, setterType) } + private inline fun A.withTargetSet(property: RealProperty?): A { + if (property == null) return this + if (target != null) return this + return if(property.property.usages(function).any()) + updateTarget(property.property) as A + else this + } + private fun KtClassOrObject.collectPropertiesData(collectingState: CollectingState): List { return declarations .asSequence() @@ -382,7 +389,13 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( realSetter?.function ) ?: return@mapNotNull null collectingState.propertyNameToSuperType[this to name] = type - PropertyData(realProperty, realGetter, realSetter, type) + + PropertyData( + realProperty, + realGetter?.withTargetSet(realProperty), + realSetter?.withTargetSet(realProperty), + type + ) } } @@ -441,7 +454,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( ) return@mapNotNull null } - if (realProperty != null && (realGetter != null && realGetter.target == null || realSetter != null && realSetter.target == null)) { + if (realProperty != null && (realGetter != null && !realGetter.isPure || realSetter != null && !realSetter.isPure)) { if (!realProperty.property.isPrivate()) return@mapNotNull null val hasUsages = realProperty.property.hasUsagesOutsideOf( @@ -627,6 +640,12 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing( commentSaver.restore(ktProperty) } + // If getter & setter do not have backing fields we should remove initializer + // As we already know that property is not directly used in the code + if (getter.target == null && setter?.target == null) { + ktProperty.initializer = null + } + val propertyVisibility = ktProperty.visibilityModifierTypeOrDefault() getterVisibility?.let { ktProperty.setVisibility(it) } if (ktSetter != null) { @@ -707,15 +726,19 @@ private interface RealAccessor : Accessor { get() = function.bodyExpression override val modifiersText: String get() = function.modifierList?.text.orEmpty() - override val isPure: Boolean - get() = target != null + + fun updateTarget(newTarget: KtProperty): RealAccessor } private data class RealGetter( override val function: KtNamedFunction, override val target: KtProperty?, - override val name: String -) : Getter(), RealAccessor + override val name: String, + override val isPure: Boolean +) : Getter(), RealAccessor { + override fun updateTarget(newTarget: KtProperty): RealGetter = + copy(target = newTarget) +} private data class FakeGetter( override val name: String, @@ -726,10 +749,14 @@ private data class FakeGetter( private data class RealSetter( override val function: KtNamedFunction, override val target: KtProperty?, - override val name: String + override val name: String, + override val isPure: Boolean ) : Setter(), RealAccessor { override val parameterName: String get() = (function.valueParameters.first().name ?: name).fixSetterParameterName() + + override fun updateTarget(newTarget: KtProperty): RealSetter = + copy(target = newTarget) } private data class FakeSetter( diff --git a/nj2k/testData/newJ2k/detectProperties/NoBackingField.java b/nj2k/testData/newJ2k/detectProperties/NoBackingField.java new file mode 100644 index 00000000000..ce18faa5e41 --- /dev/null +++ b/nj2k/testData/newJ2k/detectProperties/NoBackingField.java @@ -0,0 +1,7 @@ +public class Host { + private String name; + + public String getName() { + return "Name"; + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/detectProperties/NoBackingField.kt b/nj2k/testData/newJ2k/detectProperties/NoBackingField.kt new file mode 100644 index 00000000000..2343fef6560 --- /dev/null +++ b/nj2k/testData/newJ2k/detectProperties/NoBackingField.kt @@ -0,0 +1,4 @@ +class Host { + val name: String? + get() = "Name" +} diff --git a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java index 3840558051b..cefebcfe5f3 100644 --- a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java +++ b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java @@ -1585,6 +1585,11 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew runTest("nj2k/testData/newJ2k/detectProperties/kt-32253.java"); } + @TestMetadata("NoBackingField.java") + public void testNoBackingField() throws Exception { + runTest("nj2k/testData/newJ2k/detectProperties/NoBackingField.java"); + } + @TestMetadata("Overrides.java") public void testOverrides() throws Exception { runTest("nj2k/testData/newJ2k/detectProperties/Overrides.java");