New J2K: remove initializer from property without backing field
#KT-33637 fixed
This commit is contained in:
+7
-4
@@ -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))
|
||||
|
||||
+42
-15
@@ -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"
|
||||
}
|
||||
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user