New J2K: Fix convert getters and setters
This commit is contained in:
committed by
Ilya Kirillov
parent
0340c218b9
commit
8bd49c147c
+268
-183
@@ -8,16 +8,24 @@ package org.jetbrains.kotlin.nj2k.postProcessing
|
|||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.psi.util.parentOfType
|
import com.intellij.psi.util.parentOfType
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.setVisibility
|
import org.jetbrains.kotlin.idea.core.setVisibility
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
|
||||||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
||||||
import org.jetbrains.kotlin.nj2k.NewJ2kPostProcessing
|
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||||
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
import org.jetbrains.kotlin.lexer.KtKeywordToken
|
||||||
|
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
|
import org.jetbrains.kotlin.nj2k.NewJ2kPostProcessing
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList
|
import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
|
|
||||||
class ConvertGettersAndSetters : NewJ2kPostProcessing {
|
class ConvertGettersAndSetters : NewJ2kPostProcessing {
|
||||||
@@ -29,41 +37,67 @@ class ConvertGettersAndSetters : NewJ2kPostProcessing {
|
|||||||
var setter: Accessor? = null
|
var setter: Accessor? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class PropertyDataExtended(
|
||||||
|
val name: String,
|
||||||
|
val getter: Accessor,
|
||||||
|
val setter: Accessor?,
|
||||||
|
val needOverride: Boolean,
|
||||||
|
val target: KtProperty,
|
||||||
|
val visibility: KtModifierKeywordToken
|
||||||
|
)
|
||||||
|
|
||||||
data class Accessor(
|
data class Accessor(
|
||||||
val name: String,
|
val name: String,
|
||||||
val target: KtProperty?,
|
val target: KtProperty?,
|
||||||
val function: KtFunction
|
val ktFunction: KtFunction,
|
||||||
|
val isPure: Boolean,
|
||||||
|
val isFromCurrentDeclaration: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun Accessor.isTrivial() = target != null
|
|
||||||
|
|
||||||
private fun KtExpression.statements() =
|
private fun KtExpression.statements() =
|
||||||
if (this is KtBlockExpression) statements
|
if (this is KtBlockExpression) statements
|
||||||
else listOf(this)
|
else listOf(this)
|
||||||
|
|
||||||
private fun KtFunction.asGetter(): Accessor? {
|
|
||||||
|
private fun KtFunction.getGetterTarget(): KtProperty? =
|
||||||
|
collectDescendantsOfType<KtReturnExpression>()
|
||||||
|
.singleOrNull()
|
||||||
|
?.returnedExpression
|
||||||
|
?.unpackedReferenceToProperty()
|
||||||
|
?.takeIf {
|
||||||
|
it.type() == this.type()!!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun KtFunction.asGetter(klass: KtClassOrObject): Accessor? {
|
||||||
if (valueParameters.isNotEmpty()) return null
|
if (valueParameters.isNotEmpty()) return null
|
||||||
if (typeParameters.isNotEmpty()) return null
|
if (typeParameters.isNotEmpty()) return null
|
||||||
val name = getterName() ?: return null
|
val name = getterName() ?: return null
|
||||||
val target = bodyExpression
|
val target = getGetterTarget()
|
||||||
?.statements()
|
val isPure = target != null
|
||||||
?.singleOrNull()
|
&& bodyExpression?.statements()?.size == 1
|
||||||
?.let {
|
|
||||||
if (it is KtReturnExpression) it.returnedExpression
|
return Accessor(
|
||||||
else it
|
name,
|
||||||
}?.let {
|
target,
|
||||||
it.unpackedReferenceToProperty()
|
this,
|
||||||
}?.takeIf {
|
isPure,
|
||||||
it.type() == this.type()
|
isFromCurrentDeclaration = containingClassOrObject == klass
|
||||||
}
|
)
|
||||||
return Accessor(name, target, this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <reified D : MemberDescriptor, reified P : KtElement> KtClassOrObject.declarations(): List<P> =
|
||||||
|
(analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as ClassDescriptor)
|
||||||
|
.unsubstitutedMemberScope.getContributedDescriptors { true }
|
||||||
|
.filterIsInstance<D>()
|
||||||
|
.mapNotNull { it.findPsi() as? P }
|
||||||
|
|
||||||
|
|
||||||
private fun KtFunction.isProcedure() =
|
private fun KtFunction.isProcedure() =
|
||||||
bodyExpression is KtBlockExpression? && !hasDeclaredReturnType()
|
bodyExpression is KtBlockExpression? && !hasDeclaredReturnType()
|
||||||
|| getReturnTypeReference()?.typeElement!!.text == "Unit"
|
|| getReturnTypeReference()?.typeElement?.text == "Unit"
|
||||||
|
|
||||||
private fun KtFunction.asSetter(): Accessor? {
|
private fun KtFunction.asSetter(klass: KtClassOrObject): Accessor? {
|
||||||
if (typeParameters.isNotEmpty()) return null
|
if (typeParameters.isNotEmpty()) return null
|
||||||
if (valueParameters.size != 1) return null
|
if (valueParameters.size != 1) return null
|
||||||
if (!isProcedure()) return null
|
if (!isProcedure()) return null
|
||||||
@@ -74,15 +108,21 @@ class ConvertGettersAndSetters : NewJ2kPostProcessing {
|
|||||||
?.singleOrNull()
|
?.singleOrNull()
|
||||||
?.let {
|
?.let {
|
||||||
if (it is KtBinaryExpression) {
|
if (it is KtBinaryExpression) {
|
||||||
if (it.operationToken != KtTokens.EQEQ) return@let null
|
if (it.operationToken != KtTokens.EQ) return@let null
|
||||||
val right = it.right as? KtNameReferenceExpression ?: return@let null
|
val right = it.right as? KtNameReferenceExpression ?: return@let null
|
||||||
if (right.reference?.resolve() != valueParameters.single()) return@let null
|
if (right.resolve() != valueParameters.single()) return@let null
|
||||||
it.left?.unpackedReferenceToProperty()
|
it.left?.unpackedReferenceToProperty()
|
||||||
} else null
|
} else null
|
||||||
}?.takeIf {
|
}?.takeIf {
|
||||||
it.type() == valueParameters.single().type()
|
it.type() == valueParameters.single().type()
|
||||||
}
|
}
|
||||||
return Accessor(name, target, this)
|
return Accessor(
|
||||||
|
name,
|
||||||
|
target,
|
||||||
|
this,
|
||||||
|
isPure = target != null || bodyExpression?.statements()?.isEmpty() == true,
|
||||||
|
isFromCurrentDeclaration = containingClassOrObject == klass
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val keywords = KtTokens.KEYWORDS.types.map { (it as KtKeywordToken).value }.toSet()
|
private val keywords = KtTokens.KEYWORDS.types.map { (it as KtKeywordToken).value }.toSet()
|
||||||
@@ -107,108 +147,180 @@ class ConvertGettersAndSetters : NewJ2kPostProcessing {
|
|||||||
?.decapitalize()
|
?.decapitalize()
|
||||||
?.escaped()
|
?.escaped()
|
||||||
|
|
||||||
private fun generatePropertiesData(element: KtClassOrObject): List<PropertyData> {
|
private fun KtClassOrObject.collectGetterAndSettersPairs(): Sequence<PropertyData> {
|
||||||
val properties = mutableMapOf<String, PropertyData>()
|
val properties = mutableMapOf<String, PropertyData>()
|
||||||
|
val functions = declarations<SimpleFunctionDescriptor, KtFunction>()
|
||||||
|
|
||||||
for (declaration in element.declarations) {
|
for (declaration in functions) {
|
||||||
when (declaration) {
|
declaration.asGetter(this)
|
||||||
is KtFunction -> {
|
?.also { getter ->
|
||||||
declaration.asGetter()
|
properties.getOrPut(getter.name.removePrefix("is").decapitalize()) {
|
||||||
?.also { getter ->
|
PropertyData(getter.name)
|
||||||
properties.getOrPut(getter.name.removePrefix("is").decapitalize()) {
|
}.also { it.getter = getter }
|
||||||
PropertyData(getter.name)
|
}
|
||||||
}.also { it.getter = getter }
|
|
||||||
}
|
declaration.asSetter(this)
|
||||||
|
?.also { setter ->
|
||||||
declaration.asSetter()
|
properties.getOrPut(setter.name) {
|
||||||
?.also { setter ->
|
PropertyData(setter.name)
|
||||||
properties.getOrPut(setter.name) {
|
}.also { it.setter = setter }
|
||||||
PropertyData(setter.name)
|
|
||||||
}.also { it.setter = setter }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return properties.values.toList()
|
return properties.values.asSequence()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T : KtExpression> T.replaceBodyText(
|
private fun List<KtModifierListOwner>.maxVisibility(): KtModifierKeywordToken =
|
||||||
|
map { it.visibilityModifierTypeOrDefault() }
|
||||||
|
.maxBy {
|
||||||
|
when (it) {
|
||||||
|
KtTokens.PUBLIC_KEYWORD -> 4
|
||||||
|
KtTokens.INTERNAL_KEYWORD -> 3
|
||||||
|
KtTokens.PROTECTED_KEYWORD -> 2
|
||||||
|
KtTokens.PRIVATE_KEYWORD -> 1
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
} ?: KtTokens.DEFAULT_VISIBILITY_KEYWORD
|
||||||
|
|
||||||
|
private fun generatePropertiesData(klass: KtClassOrObject): List<PropertyDataExtended> {
|
||||||
|
val propertyByName = klass
|
||||||
|
.declarations<PropertyDescriptor, KtProperty>()
|
||||||
|
.map { it.name!! to it }
|
||||||
|
.toMap()
|
||||||
|
val factory = KtPsiFactory(klass)
|
||||||
|
|
||||||
|
|
||||||
|
return klass.collectGetterAndSettersPairs().mapNotNull { (name, getter, setter) ->
|
||||||
|
if (getter == null) return@mapNotNull null
|
||||||
|
|
||||||
|
val referencedProperty = propertyByName[name]
|
||||||
|
if (referencedProperty != null
|
||||||
|
&& getter.target != referencedProperty
|
||||||
|
&& referencedProperty.containingClassOrObject == klass
|
||||||
|
) return@mapNotNull null
|
||||||
|
|
||||||
|
val setterAndGetterHaveTheSameTypes =
|
||||||
|
setter != null
|
||||||
|
&& setter.ktFunction.valueParameters.single().type() == getter.ktFunction.type()
|
||||||
|
|
||||||
|
if (klass.isInterfaceClass()
|
||||||
|
&& setter != null
|
||||||
|
&& !setterAndGetterHaveTheSameTypes
|
||||||
|
) return@mapNotNull null
|
||||||
|
|
||||||
|
val needOverride =
|
||||||
|
getter.target?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true
|
||||||
|
|| getter.ktFunction.hasModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||||
|
|| setter?.ktFunction?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true
|
||||||
|
|
||||||
|
val realTarget =
|
||||||
|
getter.target?.takeIf {
|
||||||
|
!it.hasUsagesOutsideOf(klass.containingKtFile, listOfNotNull(getter.ktFunction, setter?.ktFunction))
|
||||||
|
|| getter.isPure && setter?.isPure != false
|
||||||
|
}
|
||||||
|
?.takeIf {
|
||||||
|
getter.isFromCurrentDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getter.target?.visibilityModifierType() == KtTokens.PUBLIC_KEYWORD
|
||||||
|
&& (!getter.isPure || setter?.isPure == false)
|
||||||
|
) return@mapNotNull null
|
||||||
|
|
||||||
|
if (realTarget == null
|
||||||
|
&& referencedProperty != null
|
||||||
|
&& referencedProperty.containingClassOrObject == klass
|
||||||
|
) return@mapNotNull null
|
||||||
|
|
||||||
|
val target = realTarget
|
||||||
|
// ?.takeIf {
|
||||||
|
// getter.isFromCurrentDeclaration
|
||||||
|
// }
|
||||||
|
?: factory.createProperty(
|
||||||
|
name,
|
||||||
|
getter.ktFunction.getReturnTypeReference()?.text,
|
||||||
|
isVar = true
|
||||||
|
)
|
||||||
|
|
||||||
|
val visibility =
|
||||||
|
listOfNotNull(setter?.ktFunction, getter.ktFunction, target).maxVisibility()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PropertyDataExtended(
|
||||||
|
name,
|
||||||
|
getter,
|
||||||
|
setter?.takeIf { setterAndGetterHaveTheSameTypes },
|
||||||
|
needOverride,
|
||||||
|
target,
|
||||||
|
visibility
|
||||||
|
)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtExpression.isReferenceToThis() =
|
||||||
|
when (this) {
|
||||||
|
is KtThisExpression -> instanceReference.resolve() == parentOfType<KtClassOrObject>()
|
||||||
|
is KtReferenceExpression -> resolve() == parentOfType<KtClassOrObject>()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : KtExpression> T.withReplacedExpressionInBody(
|
||||||
from: KtElement,
|
from: KtElement,
|
||||||
to: KtExpression,
|
to: KtExpression,
|
||||||
replaceOnlyWriteUsages: Boolean
|
replaceOnlyWriteUsages: Boolean
|
||||||
): T =
|
): T = also {
|
||||||
also {
|
from.usages()
|
||||||
from.usages()
|
.map { it.element }
|
||||||
.map { it.element }
|
.filter { it.isInsideOf(listOf(this)) }
|
||||||
.filter { it.isInsideOf(listOf(this)) }
|
.forEach { reference ->
|
||||||
.forEach { reference ->
|
val parent = reference.parent
|
||||||
val parent = reference.parent
|
val referenceExpression = when {
|
||||||
val referenceExpression = when {
|
parent is KtQualifiedExpression
|
||||||
parent is KtQualifiedExpression && parent.receiverExpression is KtThisExpression ->
|
&& parent.receiverExpression.isReferenceToThis() ->
|
||||||
parent
|
parent
|
||||||
else -> reference
|
else -> reference
|
||||||
}
|
|
||||||
if (!replaceOnlyWriteUsages
|
|
||||||
|| (referenceExpression.parent as? KtExpression)?.asAssignment()?.left == referenceExpression
|
|
||||||
) {
|
|
||||||
referenceExpression.replace(to)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if (!replaceOnlyWriteUsages
|
||||||
|
|| (referenceExpression.parent as? KtExpression)?.asAssignment()?.left == referenceExpression
|
||||||
|
) {
|
||||||
private fun KtPsiFactory.createGetter(expression: KtExpression?): KtPropertyAccessor {
|
referenceExpression.replace(to)
|
||||||
val property =
|
}
|
||||||
createProperty("val x get" + if (expression == null) "" else if (expression is KtBlockExpression) "() { return 1 }" else "() = 1")
|
}
|
||||||
val getter = property.getter!!
|
|
||||||
val bodyExpression = getter.bodyExpression
|
|
||||||
|
|
||||||
bodyExpression?.replace(expression!!)
|
|
||||||
return getter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun KtProperty.addGetter(getterFunction: Accessor, target: KtProperty): KtPropertyAccessor {
|
private fun KtProperty.addGetter(getterFunction: Accessor, target: KtProperty): KtPropertyAccessor {
|
||||||
val factory = KtPsiFactory(this)
|
val factory = KtPsiFactory(this)
|
||||||
val newBody =
|
val newBody =
|
||||||
if (!getterFunction.isTrivial()) {
|
if (getterFunction.isPure && getterFunction.name == getterFunction.target?.name) null
|
||||||
getterFunction.function.bodyExpression
|
else {
|
||||||
?.replaceBodyText(target, factory.createExpression("field"), replaceOnlyWriteUsages = false)
|
getterFunction.ktFunction.bodyExpression
|
||||||
} else null
|
?.withReplacedExpressionInBody(target, factory.createExpression("field"), replaceOnlyWriteUsages = false)
|
||||||
|
}
|
||||||
add(factory.createGetter(newBody))
|
add(factory.createGetter(newBody))
|
||||||
getterFunction.function.modifierList?.also {
|
getterFunction.ktFunction.modifierList?.also {
|
||||||
getter!!.addModifiers(it)
|
getter!!.addModifiers(it)
|
||||||
}
|
}
|
||||||
return getter!!
|
return getter!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun KtPropertyAccessor.addModifiers(newModifiers: KtModifierList) {
|
private fun KtPropertyAccessor.addModifiers(newModifiers: KtModifierList) {
|
||||||
setModifierList(newModifiers)
|
setModifierList(newModifiers)
|
||||||
removeModifier(KtTokens.OVERRIDE_KEYWORD)
|
removeModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||||
removeModifier(KtTokens.FINAL_KEYWORD)
|
removeModifier(KtTokens.FINAL_KEYWORD)
|
||||||
|
removeModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtPsiFactory.createSetter(body: KtExpression?, fieldName: String): KtPropertyAccessor {
|
|
||||||
val property = when (body) {
|
|
||||||
null -> createProperty("var x = 1\n get() = 1\n set")
|
|
||||||
is KtBlockExpression -> createProperty("var x get() = 1\nset($fieldName) {\n field = $fieldName\n }")
|
|
||||||
else -> createProperty("var x get() = 1\nset($fieldName) = TODO()")
|
|
||||||
}
|
|
||||||
val setter = property.setter!!
|
|
||||||
if (body != null) {
|
|
||||||
setter.bodyExpression?.replace(body)
|
|
||||||
}
|
|
||||||
return setter
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtProperty.addSetter(setterInfo: Accessor): KtPropertyAccessor {
|
private fun KtProperty.addSetter(setterInfo: Accessor): KtPropertyAccessor {
|
||||||
val factory = KtPsiFactory(this)
|
val factory = KtPsiFactory(this)
|
||||||
val newBody =
|
val newBody =
|
||||||
if (!setterInfo.isTrivial()) {
|
if (setterInfo.isPure && setterInfo.name == setterInfo.target?.name) null
|
||||||
setterInfo.function.bodyExpression?.replaceBodyText(this, factory.createExpression("field"), true)
|
else {
|
||||||
} else null
|
setterInfo.ktFunction.bodyExpression?.withReplacedExpressionInBody(this, factory.createExpression("field"), true)
|
||||||
add(factory.createSetter(newBody, setterInfo.function.valueParameters.single().name!!))
|
}
|
||||||
setterInfo.function.modifierList?.also {
|
add(factory.createSetter(newBody, setterInfo.ktFunction.valueParameters.single().name!!))
|
||||||
|
setterInfo.ktFunction.modifierList?.also {
|
||||||
setter!!.addModifiers(it)
|
setter!!.addModifiers(it)
|
||||||
}
|
}
|
||||||
return setter!!
|
return setter!!
|
||||||
@@ -221,124 +333,97 @@ class ConvertGettersAndSetters : NewJ2kPostProcessing {
|
|||||||
|
|
||||||
private fun KtElement.usages() =
|
private fun KtElement.usages() =
|
||||||
ReferencesSearch.search(this, LocalSearchScope(containingKtFile))
|
ReferencesSearch.search(this, LocalSearchScope(containingKtFile))
|
||||||
.findAll()
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun KtProperty.addGetterAndChangeAllUsages(getter: Accessor, name: String) =
|
||||||
|
addGetter(getter, this).also {
|
||||||
|
val factory = KtPsiFactory(this)
|
||||||
|
getter.ktFunction.forAllUsages { usage ->
|
||||||
|
usage.parentOfType<KtCallExpression>()!!.replace(factory.createExpression(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtProperty.addSetterAndChangeAllUsages(setter: Accessor) =
|
||||||
|
addSetter(setter).also {
|
||||||
|
val factory = KtPsiFactory(this)
|
||||||
|
setter.ktFunction.forAllUsages { usage ->
|
||||||
|
val callExpression = usage.parentOfType<KtCallExpression>()!!
|
||||||
|
val qualifier = callExpression.getQualifiedExpressionForSelector()
|
||||||
|
val newValue = callExpression.valueArguments.single()
|
||||||
|
if (qualifier != null) {
|
||||||
|
qualifier.replace(factory.createExpression("${qualifier.receiverExpression.text}.$name = ${newValue.text}"))
|
||||||
|
} else {
|
||||||
|
callExpression.replace(factory.createExpression("$name = ${newValue.text}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtProperty.addDefaultSetter() =
|
||||||
|
KtPsiFactory(this).createSetter(null, "value").let { setter ->
|
||||||
|
this.add(setter) as KtPropertyAccessor
|
||||||
|
}
|
||||||
|
|
||||||
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||||
if (element !is KtClassOrObject) return null
|
if (element !is KtClassOrObject) return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
val factory = KtPsiFactory(element)
|
val factory = KtPsiFactory(element)
|
||||||
for ((name, getterFunction, setterFunction) in generatePropertiesData(element)) {
|
for ((name,
|
||||||
if (getterFunction == null) continue
|
getterAccessor,
|
||||||
|
setterAccessor,
|
||||||
if (!getterFunction.isTrivial() &&
|
needOverride,
|
||||||
element.declarations.any { (it as? KtProperty)?.name == name }
|
target,
|
||||||
) {//TODO improve??
|
visibility
|
||||||
continue
|
) in generatePropertiesData(element)) {
|
||||||
}
|
|
||||||
|
|
||||||
val setterAndGetterHaveTheSameTypes =
|
|
||||||
setterFunction != null &&
|
|
||||||
setterFunction.function.valueParameters.single().type() == getterFunction.function.type()
|
|
||||||
|
|
||||||
if (element.isInterfaceClass() && setterFunction != null && !setterAndGetterHaveTheSameTypes) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val hasOverrideModifier =
|
|
||||||
getterFunction.target?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false
|
|
||||||
|| getterFunction.function.hasModifier(KtTokens.OVERRIDE_KEYWORD)
|
|
||||||
|| setterFunction?.function?.hasModifier(KtTokens.OVERRIDE_KEYWORD) ?: false
|
|
||||||
|
|
||||||
val target = getterFunction.target
|
|
||||||
?: factory.createProperty(
|
|
||||||
name,
|
|
||||||
getterFunction.function.getReturnTypeReference()?.text,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
if (target.name != name) {//TODO use refactoring
|
if (target.name != name) {//TODO use refactoring
|
||||||
target.forAllUsages { usage ->
|
target.forAllUsages { usage ->
|
||||||
val parent = (usage.parent as? KtExpression)
|
val parent = (usage.parent as? KtExpression)
|
||||||
?.asAssignment()
|
?.asAssignment()
|
||||||
?.takeIf { it.left == usage }
|
?.takeIf { it.left == usage }
|
||||||
|
|
||||||
val expression = if (parent != null) factory.createExpression("this.$name")
|
val expression =
|
||||||
else factory.createExpression(name)
|
if (parent != null) factory.createExpression("this.$name")
|
||||||
|
else factory.createExpression(name)
|
||||||
|
|
||||||
usage.replace(expression)
|
usage.replace(expression)
|
||||||
}
|
}
|
||||||
target.setName(name)
|
target.setName(name)
|
||||||
}
|
}
|
||||||
|
target.addGetterAndChangeAllUsages(getterAccessor, name).also { getter ->
|
||||||
val propertyGetter = getterFunction.let {
|
getter.setVisibility(visibility)
|
||||||
val getter = target.addGetter(it, target)
|
|
||||||
it.function.forAllUsages { usage ->
|
|
||||||
usage.parentOfType<KtCallExpression>()!!.replace(factory.createExpression(name))
|
|
||||||
}
|
|
||||||
getter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val propertySetter = setterFunction
|
val propertySetter =
|
||||||
?.takeIf { setterAndGetterHaveTheSameTypes }
|
setterAccessor
|
||||||
?.let {
|
?.takeIf { it.isFromCurrentDeclaration }
|
||||||
val setter = target.addSetter(it)
|
?.let { target.addSetterAndChangeAllUsages(it) }
|
||||||
it.function.forAllUsages { usage ->
|
?: if (target.isVar
|
||||||
val callExpression = usage.parentOfType<KtCallExpression>()!!
|
&& (target.visibilityModifierTypeOrDefault() == KtTokens.PRIVATE_KEYWORD
|
||||||
val qualifier = callExpression.getQualifiedExpressionForSelector()
|
|| target.visibilityModifierTypeOrDefault() == KtTokens.PROTECTED_KEYWORD)
|
||||||
val newValue = callExpression.valueArguments.single()
|
) target.addDefaultSetter().also { it.setVisibility(target.visibilityModifierTypeOrDefault()) }
|
||||||
if (qualifier != null) {
|
else null
|
||||||
qualifier
|
|
||||||
.replace(factory.createExpression("${qualifier.receiverExpression.text}.${target.name} = ${newValue.text}"))
|
|
||||||
} else {
|
|
||||||
callExpression.replace(factory.createExpression("${target.name} = ${newValue.text}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.function.delete()
|
|
||||||
setter
|
|
||||||
}
|
|
||||||
?: if (target.isVar
|
|
||||||
&& target.visibilityModifierTypeOrDefault() in listOf(KtTokens.PRIVATE_KEYWORD, KtTokens.PROTECTED_KEYWORD)
|
|
||||||
) {
|
|
||||||
factory.createSetter(null, "value").apply {
|
|
||||||
setVisibility(target.visibilityModifierTypeOrDefault())
|
|
||||||
target.add(this)
|
|
||||||
}
|
|
||||||
target.setter!!
|
|
||||||
} else null
|
|
||||||
|
|
||||||
|
|
||||||
|
target.setVisibility(visibility)
|
||||||
val isVar = propertySetter != null
|
val isVar = propertySetter != null
|
||||||
|
|
||||||
if (target.isVar != isVar) {
|
if (target.isVar != isVar) {
|
||||||
target.valOrVarKeyword
|
target.valOrVarKeyword.replace(if (isVar) factory.createVarKeyword() else factory.createValKeyword())
|
||||||
.replace(if (isVar) factory.createVarKeyword() else factory.createValKeyword())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.parent?.isPhysical != true) {
|
||||||
val visibility = listOfNotNull(
|
when {
|
||||||
propertySetter?.visibilityModifierTypeOrDefault(),
|
getterAccessor.isFromCurrentDeclaration ->
|
||||||
propertyGetter.visibilityModifierTypeOrDefault(),
|
element.addDeclarationAfter(target, getterAccessor.ktFunction)
|
||||||
target.visibilityModifierTypeOrDefault()
|
setterAccessor?.isFromCurrentDeclaration == true ->
|
||||||
).maxBy {
|
element.addDeclarationAfter(target, setterAccessor.ktFunction)
|
||||||
when (it) {
|
|
||||||
KtTokens.PUBLIC_KEYWORD -> 4
|
|
||||||
KtTokens.INTERNAL_KEYWORD -> 3
|
|
||||||
KtTokens.PROTECTED_KEYWORD -> 2
|
|
||||||
KtTokens.PRIVATE_KEYWORD -> 1
|
|
||||||
else -> 0
|
|
||||||
}
|
}
|
||||||
}!!
|
|
||||||
target.setVisibility(visibility)
|
|
||||||
|
|
||||||
if (getterFunction.target == null) {
|
|
||||||
element.addDeclarationAfter(target, getterFunction.function)
|
|
||||||
getterFunction.function.delete()
|
|
||||||
} else {
|
|
||||||
getterFunction.function.delete()
|
|
||||||
}
|
}
|
||||||
if (!target.hasModifier(KtTokens.OVERRIDE_KEYWORD) && hasOverrideModifier) {
|
if (getterAccessor.isFromCurrentDeclaration) {
|
||||||
|
getterAccessor.ktFunction.delete()
|
||||||
|
}
|
||||||
|
if (setterAccessor?.isFromCurrentDeclaration == true) {
|
||||||
|
setterAccessor.ktFunction.delete()
|
||||||
|
}
|
||||||
|
if (!target.hasModifier(KtTokens.OVERRIDE_KEYWORD) && needOverride) {
|
||||||
target.addModifier(KtTokens.OVERRIDE_KEYWORD)
|
target.addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,16 @@ import com.intellij.psi.search.searches.ReferencesSearch
|
|||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
|
|
||||||
fun KtExpression.asProperty(): KtProperty? =
|
fun KtExpression.asProperty(): KtProperty? =
|
||||||
(this as? KtNameReferenceExpression)
|
(this as? KtNameReferenceExpression)
|
||||||
?.references
|
?.mainReference
|
||||||
?.firstOrNull { it is KtSimpleNameReference }
|
|
||||||
?.resolve() as? KtProperty
|
?.resolve() as? KtProperty
|
||||||
|
|
||||||
fun KtExpression.unpackedReferenceToProperty(): KtProperty? =
|
fun KtExpression.unpackedReferenceToProperty(): KtProperty? =
|
||||||
@@ -42,6 +43,37 @@ fun KtElement.topLevelContainingClassOrObject(): KtClassOrObject? =
|
|||||||
}.lastOrNull()
|
}.lastOrNull()
|
||||||
|
|
||||||
fun KtReferenceExpression.resolve() =
|
fun KtReferenceExpression.resolve() =
|
||||||
references
|
mainReference.resolve()
|
||||||
.firstOrNull { it is KtSimpleNameReference }
|
|
||||||
?.resolve()
|
fun KtPsiFactory.createGetter(body: KtExpression?): KtPropertyAccessor {
|
||||||
|
val property =
|
||||||
|
createProperty("val x get" + if (body == null) "" else if (body is KtBlockExpression) "() { return 1 }" else "() = 1")
|
||||||
|
val getter = property.getter!!
|
||||||
|
val bodyExpression = getter.bodyExpression
|
||||||
|
|
||||||
|
bodyExpression?.replace(body!!)
|
||||||
|
return getter
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtPsiFactory.createSetter(body: KtExpression?, fieldName: String): KtPropertyAccessor {
|
||||||
|
val property = when (body) {
|
||||||
|
null -> createProperty("var x = 1\n get() = 1\n set")
|
||||||
|
is KtBlockExpression -> createProperty("var x get() = 1\nset($fieldName) {\n field = $fieldName\n }")
|
||||||
|
else -> createProperty("var x get() = 1\nset($fieldName) = TODO()")
|
||||||
|
}
|
||||||
|
val setter = property.setter!!
|
||||||
|
if (body != null) {
|
||||||
|
setter.bodyExpression?.replace(body)
|
||||||
|
}
|
||||||
|
return setter
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtClassOrObject.parentClassForCompanionOrThis(): KtClassOrObject =
|
||||||
|
if (safeAs<KtObjectDeclaration>()?.isCompanion() == true)
|
||||||
|
getStrictParentOfType() ?: this
|
||||||
|
else this
|
||||||
|
|
||||||
|
fun KtElement.hasUsagesOutsideOf(inElement: KtElement, outsideElements: List<KtElement>): Boolean =
|
||||||
|
ReferencesSearch.search(this, LocalSearchScope(inElement)).any { reference ->
|
||||||
|
outsideElements.none { it.isAncestor(reference.element) }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user