Initial implementation of renaming extension property usages on getter or setter rename

This commit is contained in:
Valentin Kipyatkov
2015-07-09 19:44:54 +03:00
parent 13f0d3ca23
commit 63614c5892
14 changed files with 117 additions and 27 deletions
@@ -151,13 +151,19 @@ class SyntheticExtensionsScope(storageManager: StorageManager) : JetScope by Jet
return Name.identifier("set" + propertyName.getIdentifier().capitalize())
}
private fun fromGetMethodName(methodName: Name): Name? {
if (methodName.isSpecial()) return null
val identifier = methodName.getIdentifier()
if (!identifier.startsWith("get")) return null
val name = identifier.removePrefix("get").decapitalize()
if (!Name.isValidIdentifier(name)) return null
return Name.identifier(name)
companion object {
public fun fromGetMethodName(methodName: Name): Name? = fromAccessorMethodName(methodName, "get")
public fun fromSetMethodName(methodName: Name): Name? = fromAccessorMethodName(methodName, "set")
private fun fromAccessorMethodName(methodName: Name, prefix: String): Name? {
if (methodName.isSpecial()) return null
val identifier = methodName.getIdentifier()
if (!identifier.startsWith(prefix)) return null
val name = identifier.removePrefix(prefix).decapitalize()
if (!Name.isValidIdentifier(name)) return null
return Name.identifier(name)
}
}
private class MyPropertyDescriptor(
@@ -38,7 +38,9 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.synthetic.SyntheticExtensionPropertyDescriptor
import org.jetbrains.kotlin.synthetic.SyntheticExtensionsScope
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.constant
@@ -135,6 +137,22 @@ public class JetSimpleNameReference(
}
}
@suppress("NAME_SHADOWING")
var newElementName = newElementName!!
val bindingContext = expression.analyze(BodyResolveMode.PARTIAL)
if (bindingContext[BindingContext.REFERENCE_TARGET, expression] is SyntheticExtensionPropertyDescriptor) {
if (Name.isValidIdentifier(newElementName)) {
val newNameAsName = Name.identifier(newElementName)
val newName = when (access()) {
Access.READ -> SyntheticExtensionsScope.fromGetMethodName(newNameAsName)
Access.WRITE -> SyntheticExtensionsScope.fromSetMethodName(newNameAsName)
Access.READ_WRITE -> SyntheticExtensionsScope.fromGetMethodName(newNameAsName) ?: SyntheticExtensionsScope.fromSetMethodName(newNameAsName)
} ?: return expression //TODO: handle the case when get/set becomes ordinary method
newElementName = newName.getIdentifier()
}
}
val psiFactory = JetPsiFactory(expression)
val element = when (expression.getReferencedNameElementType()) {
JetTokens.FIELD_IDENTIFIER -> psiFactory.createFieldIdentifier(newElementName)
@@ -156,23 +174,21 @@ public class JetSimpleNameReference(
var nameElement = expression.getReferencedNameElement()
val elementType = nameElement.getNode()?.getElementType()
val opExpression =
PsiTreeUtil.getParentOfType<JetExpression>(expression, javaClass<JetUnaryExpression>(), javaClass<JetBinaryExpression>())
val opExpression = PsiTreeUtil.getParentOfType<JetExpression>(expression, javaClass<JetUnaryExpression>(), javaClass<JetBinaryExpression>())
if (elementType is JetToken && OperatorConventions.getNameForOperationSymbol(elementType) != null && opExpression != null) {
val oldDescriptor = expression.analyze()[BindingContext.REFERENCE_TARGET, expression]
val oldDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, expression]
val newExpression = OperatorToFunctionIntention.convert(opExpression)
newExpression.accept(
object: JetTreeVisitorVoid() {
override fun visitCallExpression(expression: JetCallExpression) {
val callee = expression.getCalleeExpression() as? JetSimpleNameExpression
if (callee != null && callee.analyze()[BindingContext.REFERENCE_TARGET, callee] == oldDescriptor) {
nameElement = callee.getReferencedNameElement()
}
else {
super.visitCallExpression(expression)
}
}
newExpression.accept(object : JetTreeVisitorVoid() {
override fun visitCallExpression(expression: JetCallExpression) {
val callee = expression.getCalleeExpression() as? JetSimpleNameExpression
if (callee != null && bindingContext[BindingContext.REFERENCE_TARGET, callee] == oldDescriptor) {
nameElement = callee.getReferencedNameElement()
}
else {
super.visitCallExpression(expression)
}
}
}
)
}
@@ -0,0 +1,6 @@
import testing.JavaClass
fun usages(javaClass: JavaClass) {
javaClass.something = javaClass.somethingNew + 1
javaClass.somethingNew++
}
@@ -0,0 +1,6 @@
package testing;
public class JavaClass {
public int getSomethingNew() { return 1; }
public void setSomething(int value) {}
}
@@ -0,0 +1,6 @@
import testing.JavaClass
fun usages(javaClass: JavaClass) {
javaClass.something = javaClass.something + 1
javaClass.something++
}
@@ -0,0 +1,6 @@
package testing;
public class JavaClass {
public int getSomething() { return 1; }
public void setSomething(int value) {}
}
@@ -0,0 +1,6 @@
{
"type": "JAVA_METHOD",
"classId": "testing/JavaClass",
"methodSignature": "int getSomething()",
"newName": "getSomethingNew"
}
@@ -0,0 +1,6 @@
import testing.JavaClass
fun usages(javaClass: JavaClass) {
javaClass.somethingNew = javaClass.something + 1
javaClass.somethingNew++
}
@@ -0,0 +1,6 @@
package testing;
public class JavaClass {
public int getSomething() { return 1; }
public void setSomethingNew(int value) {}
}
@@ -0,0 +1,6 @@
import testing.JavaClass
fun usages(javaClass: JavaClass) {
javaClass.somethingNew = javaClass.something + 1
javaClass.somethingNew++
}
@@ -0,0 +1,6 @@
package testing;
public class JavaClass {
public int getSomething() { return 1; }
public void setSomething(int value) {}
}
@@ -0,0 +1,6 @@
{
"type": "JAVA_METHOD",
"classId": "testing/JavaClass",
"methodSignature": "void setSomething(int value)",
"newName": "setSomethingNew"
}
@@ -299,11 +299,7 @@ public abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
private fun String.toClassId(): ClassId {
val lastSlash = lastIndexOf("/")
if (lastSlash == -1) {
throw IllegalArgumentException("Class id should contain slash: $this")
}
val relativeClassName = FqName(substring(lastSlash + 1))
val packageFqName = FqName(substring(0, lastSlash).replace('/', '.'))
val relativeClassName = FqName(substringAfterLast('/'))
val packageFqName = FqName(substringBeforeLast('/', "").replace('/', '.'))
return ClassId(packageFqName, relativeClassName, false)
}
@@ -370,4 +370,16 @@ public class RenameTestGenerated extends AbstractRenameTest {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameUnaryMinus/unaryMinus.test");
doTest(fileName);
}
@TestMetadata("syntheticPropertyUsages1/renameGetMethod.test")
public void testSyntheticPropertyUsages1_RenameGetMethod() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/rename/syntheticPropertyUsages1/renameGetMethod.test");
doTest(fileName);
}
@TestMetadata("syntheticPropertyUsages2/renameSetMethod.test")
public void testSyntheticPropertyUsages2_RenameSetMethod() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/rename/syntheticPropertyUsages2/renameSetMethod.test");
doTest(fileName);
}
}