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
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
|
||||||
internal abstract class JKInMemoryFilesSearcher {
|
internal abstract class JKInMemoryFilesSearcher {
|
||||||
abstract fun search(element: KtElement): Iterable<PsiReference>
|
abstract fun search(element: KtElement, scope: PsiElement? = null): Iterable<PsiReference>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(files: List<PsiElement>) = when {
|
fun create(files: List<PsiElement>) = when {
|
||||||
@@ -23,8 +23,8 @@ internal abstract class JKInMemoryFilesSearcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class JKSingleFileInMemoryFilesSearcher(private val scopeElement: PsiElement) : JKInMemoryFilesSearcher() {
|
internal class JKSingleFileInMemoryFilesSearcher(private val scopeElement: PsiElement) : JKInMemoryFilesSearcher() {
|
||||||
override fun search(element: KtElement): Iterable<PsiReference> =
|
override fun search(element: KtElement, scope: PsiElement?): Iterable<PsiReference> =
|
||||||
ReferencesSearch.search(element, LocalSearchScope(scopeElement))
|
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
|
// maybe the solution is to do searching manually
|
||||||
// firstly by-text and then resolving
|
// firstly by-text and then resolving
|
||||||
internal class JKMultipleFilesInMemoryFilesSearcher(private val scopeElements: List<PsiElement>) : JKInMemoryFilesSearcher() {
|
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>()
|
val result = mutableListOf<PsiReference>()
|
||||||
for (scopeElement in scopeElements) {
|
for (scopeElement in scopeElements) {
|
||||||
result += ReferencesSearch.search(element, LocalSearchScope(scopeElement))
|
result += ReferencesSearch.search(element, LocalSearchScope(scopeElement))
|
||||||
|
|||||||
+42
-15
@@ -120,7 +120,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
val name = name?.asGetterName() ?: return null
|
val name = name?.asGetterName() ?: return null
|
||||||
if (valueParameters.isNotEmpty()) return null
|
if (valueParameters.isNotEmpty()) return null
|
||||||
if (typeParameters.isNotEmpty()) return null
|
if (typeParameters.isNotEmpty()) return null
|
||||||
val target = bodyExpression
|
val singleTimeUsedTarget = bodyExpression
|
||||||
?.statements()
|
?.statements()
|
||||||
?.singleOrNull()
|
?.singleOrNull()
|
||||||
?.safeAs<KtReturnExpression>()
|
?.safeAs<KtReturnExpression>()
|
||||||
@@ -129,7 +129,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
?.takeIf {
|
?.takeIf {
|
||||||
it.type() == type() ?: return@takeIf false
|
it.type() == type() ?: return@takeIf false
|
||||||
}
|
}
|
||||||
return RealGetter(this, target, name)
|
return RealGetter(this, singleTimeUsedTarget, name, singleTimeUsedTarget != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtNamedFunction.asSetter(): Setter? {
|
private fun KtNamedFunction.asSetter(): Setter? {
|
||||||
@@ -138,7 +138,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
if (valueParameters.size != 1) return null
|
if (valueParameters.size != 1) return null
|
||||||
val descriptor = resolveToDescriptorIfAny() ?: return null
|
val descriptor = resolveToDescriptorIfAny() ?: return null
|
||||||
if (descriptor.returnType?.isUnit() != true) return null
|
if (descriptor.returnType?.isUnit() != true) return null
|
||||||
val target = bodyExpression
|
val singleTimeUsedTarget = bodyExpression
|
||||||
?.statements()
|
?.statements()
|
||||||
?.singleOrNull()
|
?.singleOrNull()
|
||||||
?.let { expression ->
|
?.let { expression ->
|
||||||
@@ -151,7 +151,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
}?.takeIf {
|
}?.takeIf {
|
||||||
it.type() == valueParameters.single().type()
|
it.type() == valueParameters.single().type()
|
||||||
}
|
}
|
||||||
return RealSetter(this, target, name)
|
return RealSetter(this, singleTimeUsedTarget, name, singleTimeUsedTarget != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtDeclaration.asPropertyAccessor(): PropertyInfo? {
|
private fun KtDeclaration.asPropertyAccessor(): PropertyInfo? {
|
||||||
@@ -262,18 +262,17 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtElement.usages() =
|
private fun KtElement.usages(scope: PsiElement? = null) =
|
||||||
searcher.search(this)
|
searcher.search(this, scope)
|
||||||
|
|
||||||
private fun <T : KtExpression> T.withReplacedExpressionInBody(
|
private fun <T : KtExpression> T.withReplacedExpressionInBody(
|
||||||
from: KtElement,
|
from: KtElement,
|
||||||
to: KtExpression,
|
to: KtExpression,
|
||||||
replaceOnlyWriteUsages: Boolean
|
replaceOnlyWriteUsages: Boolean
|
||||||
): T = also {
|
): T = also {
|
||||||
from.usages()
|
from.usages(this)
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { it.element }
|
.map { it.element }
|
||||||
.filter { it.isInsideOf(listOf(this)) }
|
|
||||||
.forEach { reference ->
|
.forEach { reference ->
|
||||||
val parent = reference.parent
|
val parent = reference.parent
|
||||||
val referenceExpression = when {
|
val referenceExpression = when {
|
||||||
@@ -343,6 +342,14 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
return calculatePropertyType(getterType, setterType)
|
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> {
|
private fun KtClassOrObject.collectPropertiesData(collectingState: CollectingState): List<PropertyData> {
|
||||||
return declarations
|
return declarations
|
||||||
.asSequence()
|
.asSequence()
|
||||||
@@ -382,7 +389,13 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
realSetter?.function
|
realSetter?.function
|
||||||
) ?: return@mapNotNull null
|
) ?: return@mapNotNull null
|
||||||
collectingState.propertyNameToSuperType[this to name] = type
|
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
|
) 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
|
if (!realProperty.property.isPrivate()) return@mapNotNull null
|
||||||
val hasUsages =
|
val hasUsages =
|
||||||
realProperty.property.hasUsagesOutsideOf(
|
realProperty.property.hasUsagesOutsideOf(
|
||||||
@@ -627,6 +640,12 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
|
|||||||
commentSaver.restore(ktProperty)
|
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()
|
val propertyVisibility = ktProperty.visibilityModifierTypeOrDefault()
|
||||||
getterVisibility?.let { ktProperty.setVisibility(it) }
|
getterVisibility?.let { ktProperty.setVisibility(it) }
|
||||||
if (ktSetter != null) {
|
if (ktSetter != null) {
|
||||||
@@ -707,15 +726,19 @@ private interface RealAccessor : Accessor {
|
|||||||
get() = function.bodyExpression
|
get() = function.bodyExpression
|
||||||
override val modifiersText: String
|
override val modifiersText: String
|
||||||
get() = function.modifierList?.text.orEmpty()
|
get() = function.modifierList?.text.orEmpty()
|
||||||
override val isPure: Boolean
|
|
||||||
get() = target != null
|
fun updateTarget(newTarget: KtProperty): RealAccessor
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class RealGetter(
|
private data class RealGetter(
|
||||||
override val function: KtNamedFunction,
|
override val function: KtNamedFunction,
|
||||||
override val target: KtProperty?,
|
override val target: KtProperty?,
|
||||||
override val name: String
|
override val name: String,
|
||||||
) : Getter(), RealAccessor
|
override val isPure: Boolean
|
||||||
|
) : Getter(), RealAccessor {
|
||||||
|
override fun updateTarget(newTarget: KtProperty): RealGetter =
|
||||||
|
copy(target = newTarget)
|
||||||
|
}
|
||||||
|
|
||||||
private data class FakeGetter(
|
private data class FakeGetter(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
@@ -726,10 +749,14 @@ private data class FakeGetter(
|
|||||||
private data class RealSetter(
|
private data class RealSetter(
|
||||||
override val function: KtNamedFunction,
|
override val function: KtNamedFunction,
|
||||||
override val target: KtProperty?,
|
override val target: KtProperty?,
|
||||||
override val name: String
|
override val name: String,
|
||||||
|
override val isPure: Boolean
|
||||||
) : Setter(), RealAccessor {
|
) : Setter(), RealAccessor {
|
||||||
override val parameterName: String
|
override val parameterName: String
|
||||||
get() = (function.valueParameters.first().name ?: name).fixSetterParameterName()
|
get() = (function.valueParameters.first().name ?: name).fixSetterParameterName()
|
||||||
|
|
||||||
|
override fun updateTarget(newTarget: KtProperty): RealSetter =
|
||||||
|
copy(target = newTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class FakeSetter(
|
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");
|
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")
|
@TestMetadata("Overrides.java")
|
||||||
public void testOverrides() throws Exception {
|
public void testOverrides() throws Exception {
|
||||||
runTest("nj2k/testData/newJ2k/detectProperties/Overrides.java");
|
runTest("nj2k/testData/newJ2k/detectProperties/Overrides.java");
|
||||||
|
|||||||
Reference in New Issue
Block a user