Rename: Copy default values if function to be renamed inherits default values from some base function which is excluded from rename

#KT-9446 Fixed
This commit is contained in:
Alexey Sedunov
2016-05-30 20:38:59 +03:00
parent 188531017a
commit 9692c210fd
3 changed files with 64 additions and 22 deletions
+1
View File
@@ -192,6 +192,7 @@
- [`KT-9157`](https://youtrack.jetbrains.com/issue/KT-9157) Fixed in-place rename of Kotlin expression referring Java declaration
- [`KT-9241`](https://youtrack.jetbrains.com/issue/KT-9241) Do not replace Java references to synthetic component functions when renaming constructor parameter
- [`KT-9444`](https://youtrack.jetbrains.com/issue/KT-9444) Rename dialog: Allow typing any identifier without backquotes
- [`KT-9446`](https://youtrack.jetbrains.com/issue/KT-9446) Warn about calls with default arguments if function to be renamed inherits default values from some base function which is excluded from rename
- [`KT-10713`](https://youtrack.jetbrains.com/issue/KT-10713) Skip read-only declarations when renaming parameters
#### Java to Kotlin converter
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import java.util.*
class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
private val javaMethodProcessorInstance = RenameJavaMethodProcessor()
@@ -59,12 +60,12 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
}
override fun findCollisions(
element: PsiElement?,
element: PsiElement,
newName: String?,
allRenames: Map<out PsiElement?, String>,
allRenames: Map<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
checkConflictsAndReplaceUsageInfos(result)
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
}
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
@@ -88,10 +89,20 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
}
}
override fun renameElement(element: PsiElement?, newName: String?, usages: Array<out UsageInfo>?, listener: RefactoringElementListener?) {
override fun renameElement(element: PsiElement, newName: String?, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
for (usage in usages) {
if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) {
usage.apply()
continue
}
simpleUsages += usage
}
super.renameElement(element, newName, usages, listener)
(element?.unwrapped as? KtNamedDeclaration)?.let { dropOverrideKeywordIfNecessary(it) }
(element.unwrapped as? KtNamedDeclaration)?.let { dropOverrideKeywordIfNecessary(it) }
}
private fun wrapPsiMethod(element: PsiElement?): PsiMethod? = when (element) {
@@ -17,31 +17,47 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.ResolvableCollisionUsageInfo
import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.dropDefaultValue
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
import org.jetbrains.kotlin.idea.references.AbstractKtReference
import java.util.*
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.resolve.source.getPsi
fun checkConflictsAndReplaceUsageInfos(result: MutableList<UsageInfo>) {
val usagesToAdd = ArrayList<UsageInfo>()
val usagesToRemove = ArrayList<UsageInfo>()
for (usageInfo in result) {
val ref = usageInfo.reference
if (usageInfo !is MoveRenameUsageInfo || ref !is AbstractKtReference<*> || ref.canRename()) continue
val refElement = usageInfo.element
val referencedElement = usageInfo.referencedElement
if (refElement != null && referencedElement != null) {
usagesToAdd.add(UnresolvableConventionViolationUsageInfo(refElement, referencedElement))
usagesToRemove.add(usageInfo)
}
fun checkConflictsAndReplaceUsageInfos(
element: PsiElement,
allRenames: Map<out PsiElement?, String>,
result: MutableList<UsageInfo>
) {
element.getOverriddenFunctionWithDefaultValues(allRenames)?.let { baseFunction ->
result += LostDefaultValuesInOverridingFunctionUsageInfo(element.unwrapped as KtNamedFunction, baseFunction)
}
result.removeAll(usagesToRemove)
result.addAll(usagesToAdd)
val usageIterator = result.listIterator()
while (usageIterator.hasNext()) {
val usageInfo = usageIterator.next() as? MoveRenameUsageInfo ?: continue
val ref = usageInfo.reference as? AbstractKtReference<*> ?: continue
if (!ref.canRename()) {
val refElement = usageInfo.element ?: continue
val referencedElement = usageInfo.referencedElement ?: continue
usageIterator.set(UnresolvableConventionViolationUsageInfo(refElement, referencedElement))
}
}
}
private fun PsiElement.getOverriddenFunctionWithDefaultValues(allRenames: Map<out PsiElement?, String>): KtNamedFunction? {
val elementsToRename = allRenames.keys.mapNotNull { it?.unwrapped }
val function = unwrapped as? KtNamedFunction ?: return null
val descriptor = function.resolveToDescriptor() as FunctionDescriptor
return descriptor.overriddenDescriptors
.mapNotNull { it.source.getPsi() as? KtNamedFunction }
.firstOrNull { it !in elementsToRename && it.valueParameters.any { it.hasDefaultValue() }}
}
class UnresolvableConventionViolationUsageInfo(
@@ -50,3 +66,17 @@ class UnresolvableConventionViolationUsageInfo(
) : UnresolvableCollisionUsageInfo(element, referencedElement) {
override fun getDescription(): String = KotlinRefactoringBundle.message("naming.convention.will.be.violated.after.rename")
}
class LostDefaultValuesInOverridingFunctionUsageInfo(
function: KtNamedFunction,
private val baseFunction: KtNamedFunction
) : ResolvableCollisionUsageInfo(function, function) {
fun apply() {
val function = element as? KtNamedFunction ?: return
for ((subParam, superParam) in (function.valueParameters zip baseFunction.valueParameters)) {
val defaultValue = superParam.defaultValue ?: continue
subParam.dropDefaultValue()
subParam.addRange(superParam.equalsToken, defaultValue)
}
}
}