New J2K: remove initializer from property without backing field

#KT-33637 fixed
This commit is contained in:
Ilya Kirillov
2019-12-02 12:59:44 +03:00
parent 1756857237
commit 14a8d3726d
5 changed files with 65 additions and 19 deletions
@@ -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<PsiReference>
abstract fun search(element: KtElement, scope: PsiElement? = null): Iterable<PsiReference>
companion object {
fun create(files: List<PsiElement>) = when {
@@ -23,8 +23,8 @@ internal abstract class JKInMemoryFilesSearcher {
}
internal class JKSingleFileInMemoryFilesSearcher(private val scopeElement: PsiElement) : JKInMemoryFilesSearcher() {
override fun search(element: KtElement): Iterable<PsiReference> =
ReferencesSearch.search(element, LocalSearchScope(scopeElement))
override fun search(element: KtElement, scope: PsiElement?): Iterable<PsiReference> =
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<PsiElement>) : JKInMemoryFilesSearcher() {
override fun search(element: KtElement): Iterable<PsiReference> {
override fun search(element: KtElement, scope: PsiElement?): Iterable<PsiReference> {
if (scope != null) {
return ReferencesSearch.search(element, LocalSearchScope(scope))
}
val result = mutableListOf<PsiReference>()
for (scopeElement in scopeElements) {
result += ReferencesSearch.search(element, LocalSearchScope(scopeElement))
@@ -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<KtReturnExpression>()
@@ -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 : KtExpression> 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 <reified A : RealAccessor> 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<PropertyData> {
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(
@@ -0,0 +1,7 @@
public class Host {
private String name;
public String getName() {
return "Name";
}
}
@@ -0,0 +1,4 @@
class Host {
val name: String?
get() = "Name"
}
@@ -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");