diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt index ec392b71faf..0442717dae0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt @@ -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( diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt index 7d7c3913480..16e3b194e05 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt @@ -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(expression, javaClass(), javaClass()) + val opExpression = PsiTreeUtil.getParentOfType(expression, javaClass(), javaClass()) 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) + } + } + } ) } diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages1/after/Usages.kt b/idea/testData/refactoring/rename/syntheticPropertyUsages1/after/Usages.kt new file mode 100644 index 00000000000..c9635f4e917 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages1/after/Usages.kt @@ -0,0 +1,6 @@ +import testing.JavaClass + +fun usages(javaClass: JavaClass) { + javaClass.something = javaClass.somethingNew + 1 + javaClass.somethingNew++ +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages1/after/testing/JavaClass.java b/idea/testData/refactoring/rename/syntheticPropertyUsages1/after/testing/JavaClass.java new file mode 100644 index 00000000000..daec9622c18 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages1/after/testing/JavaClass.java @@ -0,0 +1,6 @@ +package testing; + +public class JavaClass { + public int getSomethingNew() { return 1; } + public void setSomething(int value) {} +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages1/before/Usages.kt b/idea/testData/refactoring/rename/syntheticPropertyUsages1/before/Usages.kt new file mode 100644 index 00000000000..97a3bb2eda3 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages1/before/Usages.kt @@ -0,0 +1,6 @@ +import testing.JavaClass + +fun usages(javaClass: JavaClass) { + javaClass.something = javaClass.something + 1 + javaClass.something++ +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages1/before/testing/JavaClass.java b/idea/testData/refactoring/rename/syntheticPropertyUsages1/before/testing/JavaClass.java new file mode 100644 index 00000000000..87b737c9ce3 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages1/before/testing/JavaClass.java @@ -0,0 +1,6 @@ +package testing; + +public class JavaClass { + public int getSomething() { return 1; } + public void setSomething(int value) {} +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages1/renameGetMethod.test b/idea/testData/refactoring/rename/syntheticPropertyUsages1/renameGetMethod.test new file mode 100644 index 00000000000..bdf2e845685 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages1/renameGetMethod.test @@ -0,0 +1,6 @@ +{ + "type": "JAVA_METHOD", + "classId": "testing/JavaClass", + "methodSignature": "int getSomething()", + "newName": "getSomethingNew" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages2/after/Usages.kt b/idea/testData/refactoring/rename/syntheticPropertyUsages2/after/Usages.kt new file mode 100644 index 00000000000..d850719ed2e --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages2/after/Usages.kt @@ -0,0 +1,6 @@ +import testing.JavaClass + +fun usages(javaClass: JavaClass) { + javaClass.somethingNew = javaClass.something + 1 + javaClass.somethingNew++ +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages2/after/testing/JavaClass.java b/idea/testData/refactoring/rename/syntheticPropertyUsages2/after/testing/JavaClass.java new file mode 100644 index 00000000000..a592c8fe126 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages2/after/testing/JavaClass.java @@ -0,0 +1,6 @@ +package testing; + +public class JavaClass { + public int getSomething() { return 1; } + public void setSomethingNew(int value) {} +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages2/before/Usages.kt b/idea/testData/refactoring/rename/syntheticPropertyUsages2/before/Usages.kt new file mode 100644 index 00000000000..d850719ed2e --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages2/before/Usages.kt @@ -0,0 +1,6 @@ +import testing.JavaClass + +fun usages(javaClass: JavaClass) { + javaClass.somethingNew = javaClass.something + 1 + javaClass.somethingNew++ +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages2/before/testing/JavaClass.java b/idea/testData/refactoring/rename/syntheticPropertyUsages2/before/testing/JavaClass.java new file mode 100644 index 00000000000..87b737c9ce3 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages2/before/testing/JavaClass.java @@ -0,0 +1,6 @@ +package testing; + +public class JavaClass { + public int getSomething() { return 1; } + public void setSomething(int value) {} +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/syntheticPropertyUsages2/renameSetMethod.test b/idea/testData/refactoring/rename/syntheticPropertyUsages2/renameSetMethod.test new file mode 100644 index 00000000000..41fee1bdcf5 --- /dev/null +++ b/idea/testData/refactoring/rename/syntheticPropertyUsages2/renameSetMethod.test @@ -0,0 +1,6 @@ +{ + "type": "JAVA_METHOD", + "classId": "testing/JavaClass", + "methodSignature": "void setSomething(int value)", + "newName": "setSomethingNew" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt index 0622cd4efc3..1add86f238a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt @@ -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) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java index 536fce18bfd..22b18c54325 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -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); + } }