From 9692c210fd129047d4acf1813c6f6c61adb9afd3 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Mon, 30 May 2016 20:38:59 +0300 Subject: [PATCH] Rename: Copy default values if function to be renamed inherits default values from some base function which is excluded from rename #KT-9446 Fixed --- ChangeLog.md | 1 + .../rename/RenameKotlinFunctionProcessor.kt | 21 ++++-- .../idea/refactoring/rename/renameUtil.kt | 64 ++++++++++++++----- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index bbe2b74e48f..84d68111b0f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt index 0f920cf1832..b06d0e7bec0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt @@ -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, + allRenames: Map, result: MutableList ) { - 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?, listener: RefactoringElementListener?) { + override fun renameElement(element: PsiElement, newName: String?, usages: Array, listener: RefactoringElementListener?) { + val simpleUsages = ArrayList(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) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt index 5ace4951f0b..f128e1d2461 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameUtil.kt @@ -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) { - val usagesToAdd = ArrayList() - val usagesToRemove = ArrayList() - - 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, + result: MutableList +) { + 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): 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) + } + } +} \ No newline at end of file