diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt.181 new file mode 100644 index 00000000000..5eb65ea3d24 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassifierProcessor.kt.181 @@ -0,0 +1,141 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenamePsiElementProcessor +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade +import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.utils.SmartList +import java.util.* + +class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement): Boolean { + return element is KtClassOrObject || element is KtLightClass || element is KtConstructor<*> || element is KtTypeAlias + } + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_CLASS = enabled + } + + override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS + + override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_CLASS = enabled + } + + override fun substituteElementToRename(element: PsiElement, editor: Editor?) = getClassOrObject(element) + + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap) { + super.prepareRenaming(element, newName, allRenames) + + val classOrObject = getClassOrObject(element) as? KtClassOrObject ?: return + + val file = classOrObject.containingKtFile + + val virtualFile = file.virtualFile + if (virtualFile != null) { + val nameWithoutExtensions = virtualFile.nameWithoutExtension + if (nameWithoutExtensions == classOrObject.name) { + val newFileName = newName + "." + virtualFile.extension + allRenames.put(file, newFileName) + RenamePsiElementProcessor.forElement(file).prepareRenaming(file, newFileName, allRenames) + } + } + } + + override fun findReferences(element: PsiElement): Collection { + if (element is KtObjectDeclaration && element.isCompanion()) { + return super.findReferences(element).filter { !it.isCompanionObjectClassReference() } + } + return super.findReferences(element) + } + + private fun PsiReference.isCompanionObjectClassReference(): Boolean { + if (this !is KtSimpleNameReference) { + return false + } + val bindingContext = element.analyze(BodyResolveMode.PARTIAL) + return bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element] != null + } + + override fun findCollisions( + element: PsiElement, + newName: String, + allRenames: MutableMap, + result: MutableList + ) { + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return + val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor + + val collisions = SmartList() + checkRedeclarations(descriptor, newName, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + result += collisions + } + + private fun getClassOrObject(element: PsiElement?): PsiElement? = when (element) { + is KtLightClass -> + when (element) { + is KtLightClassForSourceDeclaration -> element.kotlinOrigin + is KtLightClassForFacade -> element + else -> throw AssertionError("Should not be suggested to rename element of type " + element::class.java + " " + element) + } + + is KtConstructor<*> -> + element.getContainingClassOrObject() + + is KtClassOrObject, is KtTypeAlias -> element + + else -> null + } + + override fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { + val simpleUsages = ArrayList(usages.size) + val ambiguousImportUsages = com.intellij.util.SmartList() + for (usage in usages) { + if (usage.isAmbiguousImportUsage()) { + ambiguousImportUsages += usage + } + else { + simpleUsages += usage + } + } + element.ambiguousImportUsages = ambiguousImportUsages + + super.renameElement(element, newName, simpleUsages.toTypedArray(), listener) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt.181 new file mode 100644 index 00000000000..d45e55e829f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFileProcessor.kt.181 @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.openapi.fileTypes.FileTypeManager +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiElement +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.search.SearchScope +import com.intellij.refactoring.rename.RenamePsiFileProcessor +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils +import org.jetbrains.kotlin.psi.KtFile + +class RenameKotlinFileProcessor : RenamePsiFileProcessor() { + class FileRenamingPsiClassWrapper( + private val psiClass: KtLightClass, + private val file: KtFile + ) : KtLightClass by psiClass { + override fun isValid() = file.isValid + } + + override fun canProcessElement(element: PsiElement) = element is KtFile && ProjectRootsUtil.isInProjectSource(element) + + override fun prepareRenaming(element: PsiElement, + newName: String, + allRenames: MutableMap, + scope: SearchScope) { + val jetFile = element as? KtFile ?: return + if (FileTypeManager.getInstance().getFileTypeByFileName(newName) != KotlinFileType.INSTANCE) { + return + } + + val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return + + val fileInfo = JvmFileClassUtil.getFileClassInfoNoResolve(jetFile) + if (!fileInfo.withJvmName) { + val facadeFqName = fileInfo.facadeClassFqName + val project = jetFile.project + val facadeClass = JavaPsiFacade.getInstance(project) + .findClass(facadeFqName.asString(), GlobalSearchScope.moduleScope(module)) as? KtLightClass + if (facadeClass != null) { + allRenames[FileRenamingPsiClassWrapper(facadeClass, jetFile)] = PackagePartClassUtils.getFilePartShortName(newName) + } + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt.181 new file mode 100644 index 00000000000..e5622404417 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt.181 @@ -0,0 +1,245 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Pass +import com.intellij.psi.* +import com.intellij.psi.search.SearchScope +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenameDialog +import com.intellij.refactoring.rename.RenameJavaMethodProcessor +import com.intellij.refactoring.rename.RenameProcessor +import com.intellij.refactoring.rename.RenameUtil +import com.intellij.refactoring.util.RefactoringUtil +import com.intellij.usageView.UsageInfo +import com.intellij.util.SmartList +import org.jetbrains.kotlin.asJava.LightClassUtil +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.refactoring.Pass +import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods +import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup +import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary +import org.jetbrains.kotlin.idea.references.KtReference +import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsKotlinAware +import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsNoWrapping +import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingMethod +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.idea.util.liftToExpected +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.DescriptorUtils +import java.lang.IllegalStateException +import java.util.* + +class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { + private val javaMethodProcessorInstance = RenameJavaMethodProcessor() + + override fun canProcessElement(element: PsiElement): Boolean { + return element is KtNamedFunction || (element is KtLightMethod && element.kotlinOrigin is KtNamedFunction) || element is FunctionWithSupersWrapper + } + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD = enabled + } + + override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD + + override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD = enabled + } + + private fun getJvmName(element: PsiElement): String? { + val descriptor = (element.unwrapped as? KtFunction)?.unsafeResolveToDescriptor() as? FunctionDescriptor ?: return null + return DescriptorUtils.getJvmName(descriptor) + } + + override fun findReferences(element: PsiElement): Collection { + val allReferences = super.findReferences(element) + return when { + getJvmName(element) == null -> allReferences + element is KtElement -> allReferences.filter { it is KtReference } + element is KtLightElement<*, *> -> allReferences.filterNot { it is KtReference } + else -> emptyList() + } + } + + override fun findCollisions( + element: PsiElement, + newName: String, + allRenames: Map, + result: MutableList + ) { + val declaration = element.unwrapped as? KtNamedFunction ?: return + val descriptor = declaration.unsafeResolveToDescriptor() + checkConflictsAndReplaceUsageInfos(element, allRenames, result) + result += SmartList().also { collisions -> + checkRedeclarations(descriptor, newName, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + } + } + + class FunctionWithSupersWrapper( + val originalDeclaration: KtNamedFunction, + val supers: List + ) : KtLightElement, PsiNamedElement by originalDeclaration { + override val kotlinOrigin: KtNamedFunction? + get() = originalDeclaration + override val clsDelegate: KtNamedFunction + get() = originalDeclaration + } + + private fun substituteForExpectOrActual(element: PsiElement?) = (element?.namedUnwrappedElement as? KtNamedDeclaration)?.liftToExpected() + + override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? { + substituteForExpectOrActual(element)?.let { return it } + + val wrappedMethod = wrapPsiMethod(element) ?: return element + + val deepestSuperMethods = findDeepestSuperMethodsKotlinAware(wrappedMethod) + val substitutedJavaElement = when { + deepestSuperMethods.isEmpty() -> return element + wrappedMethod.isConstructor || deepestSuperMethods.size == 1 || element !is KtNamedFunction -> { + javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor) + } + else -> { + val chosenElements = checkSuperMethods(element, null, "rename") + if (chosenElements.size > 1) FunctionWithSupersWrapper(element, chosenElements) else wrappedMethod + } + } + + if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) { + return substitutedJavaElement.kotlinOrigin as? KtNamedFunction + } + + return substitutedJavaElement + } + + override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass) { + fun preprocessAndPass(substitutedJavaElement: PsiElement) { + val elementToProcess = if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) { + substitutedJavaElement.kotlinOrigin as? KtNamedFunction + } + else { + substitutedJavaElement + } + renameCallback.pass(elementToProcess) + } + + substituteForExpectOrActual(element)?.let { return preprocessAndPass(it) } + + val wrappedMethod = wrapPsiMethod(element) + val deepestSuperMethods = if (wrappedMethod != null) { + findDeepestSuperMethodsKotlinAware(wrappedMethod) + } else { + findDeepestSuperMethodsNoWrapping(element) + } + when { + deepestSuperMethods.isEmpty() -> preprocessAndPass(element) + wrappedMethod != null && (wrappedMethod.isConstructor || element !is KtNamedFunction) -> { + javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor, Pass(::preprocessAndPass)) + } + else -> { + val declaration = element.unwrapped as? KtNamedFunction ?: return + checkSuperMethodsWithPopup(declaration, deepestSuperMethods.toList(), "Rename", editor) { + preprocessAndPass(if (it.size > 1) FunctionWithSupersWrapper(declaration, it) else wrappedMethod ?: element) + } + } + } + } + + override fun createRenameDialog(project: Project, element: PsiElement, nameSuggestionContext: PsiElement?, editor: Editor?): RenameDialog { + val elementForDialog = (element as? FunctionWithSupersWrapper)?.originalDeclaration ?: element + return object : RenameDialog(project, elementForDialog, nameSuggestionContext, editor) { + override fun createRenameProcessor(newName: String) = RenameProcessor(getProject(), element, newName, isSearchInComments, isSearchInNonJavaFiles) + } + } + + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { + super.prepareRenaming(element, newName, allRenames, scope) + + if (element is KtLightMethod && getJvmName(element) == null) { + (element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName } + } + if (element is FunctionWithSupersWrapper) { + allRenames.remove(element) + } + for (declaration in ((element as? FunctionWithSupersWrapper)?.supers ?: listOf(element))) { + val psiMethod = wrapPsiMethod(declaration) ?: continue + allRenames[declaration] = newName + if (psiMethod.containingClass != null) { + psiMethod.forEachOverridingMethod { it -> + val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it + + if (overrider is SyntheticElement) return@forEachOverridingMethod true + + val overriderName = overrider.name + val baseName = psiMethod.name + val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newName) + if (newOverriderName != null) { + RenameProcessor.assertNonCompileElement(overrider) + allRenames.put(overrider, newOverriderName) + } + return@forEachOverridingMethod true + } + javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope) + } + } + } + + override fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { + val simpleUsages = ArrayList(usages.size) + val ambiguousImportUsages = SmartList() + for (usage in usages) { + if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) { + usage.apply() + continue + } + + if (usage.isAmbiguousImportUsage()) { + ambiguousImportUsages += usage + } + else { + simpleUsages += usage + } + } + element.ambiguousImportUsages = ambiguousImportUsages + + RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + + (element.unwrapped as? KtNamedDeclaration)?.let(::dropOverrideKeywordIfNecessary) + } + + private fun wrapPsiMethod(element: PsiElement?): PsiMethod? = when (element) { + is PsiMethod -> element + is KtNamedFunction, is KtSecondaryConstructor -> runReadAction { + LightClassUtil.getLightClassMethod(element as KtFunction) + } + else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt.181 new file mode 100644 index 00000000000..69f24550b5f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt.181 @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.psi.PsiElement +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.utils.SmartList + +class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement) = element is KtParameter && element.ownerFunction is KtFunction + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_VARIABLE = enabled + } + + override fun findCollisions( + element: PsiElement, + newName: String, + allRenames: MutableMap, + result: MutableList + ) { + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return + val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor + + val collisions = SmartList() + checkRedeclarations(descriptor, newName, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + result += collisions + } + + override fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { + super.renameElement(element, newName, usages, listener) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt.181 new file mode 100644 index 00000000000..b30984cbeee --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt.181 @@ -0,0 +1,478 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.navigation.NavigationItem +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.Pass +import com.intellij.psi.* +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.DirectClassInheritorsSearch +import com.intellij.psi.search.searches.OverridingMethodsSearch +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenameProcessor +import com.intellij.refactoring.util.MoveRenameUsageInfo +import com.intellij.refactoring.util.RefactoringUtil +import com.intellij.usageView.UsageInfo +import com.intellij.usageView.UsageViewUtil +import org.jetbrains.kotlin.asJava.* +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations +import org.jetbrains.kotlin.idea.core.isEnumCompanionPropertyWithEntryConflict +import org.jetbrains.kotlin.idea.core.unquote +import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup +import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary +import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference +import org.jetbrains.kotlin.idea.references.KtReference +import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName +import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.util.findCallableMemberBySignature +import org.jetbrains.kotlin.utils.DFS +import org.jetbrains.kotlin.utils.SmartList + +class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement): Boolean { + val namedUnwrappedElement = element.namedUnwrappedElement + return namedUnwrappedElement is KtProperty || (namedUnwrappedElement is KtParameter && namedUnwrappedElement.hasValOrVar()) + } + + override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD + + override fun setToSearchInComments(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled + } + + override fun isToSearchForTextOccurrences(element: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD + + override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) { + JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_FIELD = enabled + } + + private fun getJvmNames(element: PsiElement): Pair { + val descriptor = (element.unwrapped as? KtDeclaration)?.unsafeResolveToDescriptor() as? PropertyDescriptor ?: return null to null + val getterName = descriptor.getter?.let { DescriptorUtils.getJvmName(it) } + val setterName = descriptor.setter?.let { DescriptorUtils.getJvmName(it) } + return getterName to setterName + } + + override fun findReferences(element: PsiElement): Collection { + val allReferences = super.findReferences(element).filterNot { it is KtDestructuringDeclarationReference } + val (getterJvmName, setterJvmName) = getJvmNames(element) + return when { + getterJvmName == null && setterJvmName == null -> allReferences + element is KtElement -> allReferences.filter { + it is KtReference + || (getterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != setterJvmName) + || (setterJvmName == null && (it.resolve() as? PsiNamedElement)?.name != getterJvmName) + } + element is KtLightDeclaration<*, *> -> { + val name = element.name + if (name == getterJvmName || name == setterJvmName) allReferences.filterNot { it is KtReference } else allReferences + } + else -> emptyList() + } + } + + private fun checkAccidentalOverrides( + declaration: KtNamedDeclaration, + newName: String, + descriptor: VariableDescriptor, + result: MutableList + ) { + fun reportAccidentalOverride(candidate: PsiNamedElement) { + val what = UsageViewUtil.getType(declaration).capitalize() + val withWhat = candidate.renderDescription() + val where = candidate.representativeContainer()?.renderDescription() ?: return + val message = "$what after rename will clash with existing $withWhat in $where" + result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message) + } + + if (descriptor !is PropertyDescriptor) return + val initialClass = declaration.containingClassOrObject ?: return + val initialClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return + + val prototype = object : PropertyDescriptor by descriptor { + override fun getName() = Name.guessByFirstCharacter(newName) + } + + DFS.dfs( + listOf(initialClassDescriptor), + DFS.Neighbors { DescriptorUtils.getSuperclassDescriptors(it) }, + object : DFS.AbstractNodeHandler() { + override fun beforeChildren(current: ClassDescriptor): Boolean { + if (current == initialClassDescriptor) return true + (current.findCallableMemberBySignature(prototype))?.let { candidateDescriptor -> + val candidate = candidateDescriptor.source.getPsi() as? PsiNamedElement ?: return false + reportAccidentalOverride(candidate) + return false + } + return true + } + + override fun result() { + + } + } + ) + + if (!declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) { + val initialPsiClass = initialClass.toLightClass() ?: return + val prototypes = declaration.toLightMethods().mapNotNull { + it as KtLightMethod + val methodName = accessorNameByPropertyName(newName, it) ?: return@mapNotNull null + object : KtLightMethod by it { + override fun getName() = methodName + } + } + DFS.dfs( + listOf(initialPsiClass), + DFS.Neighbors { DirectClassInheritorsSearch.search(it) }, + object : DFS.AbstractNodeHandler() { + override fun beforeChildren(current: PsiClass): Boolean { + if (current == initialPsiClass) return true + + if (current is KtLightClass) { + val property = current.kotlinOrigin?.findPropertyByName(newName) ?: return true + reportAccidentalOverride(property) + return false + } + + for (psiMethod in prototypes) { + current.findMethodBySignature(psiMethod, false)?.let { + val candidate = it.unwrapped as? PsiNamedElement ?: return true + reportAccidentalOverride(candidate) + return false + } + } + + return true + } + + override fun result() { + + } + } + ) + } + } + + override fun findCollisions( + element: PsiElement, + newName: String, + allRenames: MutableMap, + result: MutableList + ) { + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return + val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor + + val collisions = SmartList() + checkRedeclarations(descriptor, newName, collisions) + checkAccidentalOverrides(declaration, newName, descriptor, collisions) + checkOriginalUsagesRetargeting(declaration, newName, result, collisions) + checkNewNameUsagesRetargeting(declaration, newName, collisions) + result += collisions + } + + private fun chooseCallableToRename(callableDeclaration: KtCallableDeclaration): KtCallableDeclaration? { + val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration) + if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) { + return callableDeclaration + } + + if (ApplicationManager.getApplication()!!.isUnitTestMode) return deepestSuperDeclaration + + val containsText: String? = + deepestSuperDeclaration.fqName?.parent()?.asString() ?: + (deepestSuperDeclaration.parent as? KtClassOrObject)?.name + + val result = Messages.showYesNoCancelDialog( + deepestSuperDeclaration.project, + if (containsText != null) "Do you want to rename base property from \n$containsText" else "Do you want to rename base property", + "Rename warning", + Messages.getQuestionIcon()) + + return when (result) { + Messages.YES -> deepestSuperDeclaration + Messages.NO -> callableDeclaration + else -> /* Cancel rename */ null + } + } + + override fun substituteElementToRename(element: PsiElement, editor: Editor?): PsiElement? { + val namedUnwrappedElement = element.namedUnwrappedElement ?: return null + + val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration + ?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + + val declarationToRename = chooseCallableToRename(callableDeclaration) ?: return null + + val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement) + if (element is KtLightMethod) { + val name = element.name + if (element.name != getterJvmName && element.name != setterJvmName) return declarationToRename + return declarationToRename.toLightMethods().firstOrNull { it.name == name } + } + + return declarationToRename + } + + override fun substituteElementToRename(element: PsiElement, editor: Editor, renameCallback: Pass) { + val namedUnwrappedElement = element.namedUnwrappedElement ?: return + + val callableDeclaration = namedUnwrappedElement as? KtCallableDeclaration + ?: throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + + fun preprocessAndPass(substitutedJavaElement: PsiElement) { + val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement) + val elementToProcess = if (element is KtLightMethod) { + val name = element.name + if (element.name != getterJvmName && element.name != setterJvmName) { + substitutedJavaElement + } + else { + substitutedJavaElement.toLightMethods().firstOrNull { it.name == name } + } + } + else substitutedJavaElement + renameCallback.pass(elementToProcess) + } + + val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration) + if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) { + return preprocessAndPass(callableDeclaration) + } + + val superPsiMethods = listOfNotNull(deepestSuperDeclaration.getRepresentativeLightMethod()) + checkSuperMethodsWithPopup(callableDeclaration, superPsiMethods, "Rename", editor) { + preprocessAndPass(if (it.size > 1) deepestSuperDeclaration else callableDeclaration) + } + } + + class PropertyMethodWrapper(private val propertyMethod: PsiMethod) : PsiNamedElement by propertyMethod, NavigationItem by propertyMethod { + override fun getName() = propertyMethod.name + override fun setName(name: String) = this + override fun copy() = this + } + + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { + super.prepareRenaming(element, newName, allRenames, scope) + + val namedUnwrappedElement = element.namedUnwrappedElement + val propertyMethods = when(namedUnwrappedElement) { + is KtProperty -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) } + is KtParameter -> runReadAction { LightClassUtil.getLightClassPropertyMethods(namedUnwrappedElement) } + else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + } + + val newPropertyName = if (element is KtLightMethod) propertyNameByAccessor(newName, element) else newName + + val (getterJvmName, setterJvmName) = getJvmNames(namedUnwrappedElement) + + val getter = propertyMethods.getter as? KtLightMethod + val setter = propertyMethods.setter as? KtLightMethod + if (newPropertyName != null + && getter != null && setter != null + && (element == getter || element == setter) + && propertyNameByAccessor(getter.name, getter) == propertyNameByAccessor(setter.name, setter)) { + val accessorToRename = if (element == getter) setter else getter + val newAccessorName = if (element == getter) JvmAbi.setterName(newPropertyName) else JvmAbi.getterName(newPropertyName) + if (ApplicationManager.getApplication().isUnitTestMode + || Messages.showYesNoDialog("Do you want to rename ${accessorToRename.name}() as well?", + "Rename", + Messages.getQuestionIcon()) == Messages.YES) { + allRenames[accessorToRename] = newAccessorName + } + } + + for (propertyMethod in propertyMethods) { + if (element is KtDeclaration && newPropertyName != null) { + val wrapper = PropertyMethodWrapper(propertyMethod) + when { + JvmAbi.isGetterName(propertyMethod.name) && getterJvmName == null -> + allRenames[wrapper] = JvmAbi.getterName(newPropertyName) + JvmAbi.isSetterName(propertyMethod.name) && setterJvmName == null -> + allRenames[wrapper] = JvmAbi.setterName(newPropertyName) + } + } + addRenameElements(propertyMethod, (element as PsiNamedElement).name, newPropertyName, allRenames, scope) + } + } + + private enum class UsageKind { + SIMPLE_PROPERTY_USAGE, + GETTER_USAGE, + SETTER_USAGE + } + + override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { + val newNameUnquoted = newName.unquote() + if (element is KtLightMethod) { + if (element.modifierList.findAnnotation(DescriptorUtils.JVM_NAME.asString()) != null) { + return super.renameElement(element, newName, usages, listener) + } + + val origin = element.kotlinOrigin + val newPropertyName = propertyNameByAccessor(newNameUnquoted, element) + // Kotlin references to Kotlin property should not use accessor name + if (newPropertyName != null && (origin is KtProperty || origin is KtParameter)) { + val (ktUsages, otherUsages) = usages.partition { it.reference is KtSimpleNameReference } + super.renameElement(element, newName, otherUsages.toTypedArray(), listener) + renameElement(origin, newPropertyName.quoteIfNeeded(), ktUsages.toTypedArray(), listener) + return + } + } + + if (element !is KtProperty && element !is KtParameter) { + super.renameElement(element, newName, usages, listener) + return + } + + val name = (element as KtNamedDeclaration).name!! + val oldGetterName = JvmAbi.getterName(name) + val oldSetterName = JvmAbi.setterName(name) + + if (isEnumCompanionPropertyWithEntryConflict(element, newNameUnquoted)) { + for ((i, usage) in usages.withIndex()) { + if (usage !is MoveRenameUsageInfo) continue + val ref = usage.reference ?: continue + // TODO: Enum value can't be accessed from Java in case of conflict with companion member + if (ref is KtReference) { + val newRef = (ref.bindToElement(element) as? KtSimpleNameExpression)?.mainReference ?: continue + usages[i] = MoveRenameUsageInfo(newRef, usage.referencedElement) + } + } + } + + val adjustedUsages = if (element is KtParameter) usages.filterNot { + val refTarget = it.reference?.resolve() + refTarget is KtLightMethod && DataClassDescriptorResolver.isComponentLike(Name.guessByFirstCharacter(refTarget.name)) + } else usages.toList() + + val refKindUsages = adjustedUsages.groupBy { usage: UsageInfo -> + val refElement = usage.reference?.resolve() + if (refElement is PsiMethod) { + when (refElement.name) { + oldGetterName -> UsageKind.GETTER_USAGE + oldSetterName -> UsageKind.SETTER_USAGE + else -> UsageKind.SIMPLE_PROPERTY_USAGE + } + } + else { + UsageKind.SIMPLE_PROPERTY_USAGE + } + } + + super.renameElement(element, JvmAbi.setterName(newNameUnquoted).quoteIfNeeded(), + refKindUsages[UsageKind.SETTER_USAGE]?.toTypedArray() ?: arrayOf(), + null) + + super.renameElement(element, JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(), + refKindUsages[UsageKind.GETTER_USAGE]?.toTypedArray() ?: arrayOf(), + null) + + super.renameElement(element, newName, + refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf(), + null) + + usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() } + + dropOverrideKeywordIfNecessary(element) + + listener?.elementRenamed(element) + } + + private fun addRenameElements(psiMethod: PsiMethod?, + oldName: String?, + newName: String?, + allRenames: MutableMap, + scope: SearchScope) { + if (psiMethod == null) return + + OverridingMethodsSearch.search(psiMethod, scope, true).forEach { overrider -> + val overriderElement = overrider.namedUnwrappedElement + + if (overriderElement != null && overriderElement !is SyntheticElement) { + RenameProcessor.assertNonCompileElement(overriderElement) + + val overriderName = overriderElement.name + + if (overriderElement is PsiMethod) { + if (newName != null && Name.isValidIdentifier(newName)) { + val isGetter = overriderElement.parameterList.parametersCount == 0 + allRenames[overriderElement] = if (isGetter) JvmAbi.getterName(newName) else JvmAbi.setterName(newName) + } + } + else { + val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, newName) + if (newOverriderName != null) { + allRenames[overriderElement] = newOverriderName + } + } + } + } + } + + private fun findDeepestOverriddenDeclaration(declaration: KtCallableDeclaration): KtCallableDeclaration? { + if (declaration.modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true) { + val bindingContext = declaration.analyze() + var descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] + if (descriptor is ValueParameterDescriptor) { + descriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor] + ?: return declaration + } + + if (descriptor != null) { + assert(descriptor is PropertyDescriptor) { "Property descriptor is expected" } + + val supers = (descriptor as PropertyDescriptor).getDeepestSuperDeclarations() + + // Take one of supers for now - API doesn't support substitute to several elements (IDEA-48796) + val deepest = supers.first() + if (deepest != descriptor) { + val superPsiElement = DescriptorToSourceUtils.descriptorToDeclaration(deepest) + return superPsiElement as? KtCallableDeclaration + } + } + } + + return null + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt.181 new file mode 100644 index 00000000000..1d5ee60760e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt.181 @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.openapi.util.Key +import com.intellij.psi.* +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.MethodReferencesSearch +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.psi.util.PsiUtilCore +import com.intellij.refactoring.listeners.RefactoringElementListener +import com.intellij.refactoring.rename.RenamePsiElementProcessor +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions +import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters +import org.jetbrains.kotlin.idea.search.projectScope +import org.jetbrains.kotlin.idea.util.actualsForExpected +import org.jetbrains.kotlin.idea.util.liftToExpected +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isIdentifier +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded +import org.jetbrains.kotlin.resolve.ImportPath + +abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() { + override fun canProcessElement(element: PsiElement): Boolean = element is KtNamedDeclaration + + override fun findReferences(element: PsiElement): Collection { + val searchParameters = KotlinReferencesSearchParameters( + element, + element.project.projectScope(), + kotlinOptions = KotlinReferencesSearchOptions(searchForComponentConventions = false) + ) + val references = ReferencesSearch.search(searchParameters).toMutableList() + if (element is KtNamedFunction + || (element is KtProperty && !element.isLocal) + || (element is KtParameter && element.hasValOrVar())) { + element.toLightMethods().flatMapTo(references) { MethodReferencesSearch.search(it) } + } + return references + } + + override fun getQualifiedNameAfterRename(element: PsiElement, newName: String, nonJava: Boolean): String? { + if (!nonJava) return newName + + val qualifiedName = when (element) { + is KtNamedDeclaration -> element.fqName?.asString() ?: element.name + is PsiClass -> element.qualifiedName ?: element.name + else -> return null + } + return PsiUtilCore.getQualifiedNameAfterRename(qualifiedName, newName) + } + + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { + val safeNewName = newName.quoteIfNeeded() + + if (!newName.isIdentifier()) { + allRenames[element] = safeNewName + } + + val declaration = element.namedUnwrappedElement as? KtNamedDeclaration + if (declaration != null) { + declaration.liftToExpected()?.let { expectDeclaration -> + allRenames[expectDeclaration] = safeNewName + expectDeclaration.actualsForExpected().forEach { allRenames[it] = safeNewName } + } + } + } + + protected var PsiElement.ambiguousImportUsages: List? by UserDataProperty(Key.create("AMBIGUOUS_IMPORT_USAGES")) + + protected fun UsageInfo.isAmbiguousImportUsage(): Boolean { + val ref = reference as? PsiPolyVariantReference ?: return false + val refElement = ref.element + return refElement.parents.any { (it is KtImportDirective && !it.isAllUnder) || (it is PsiImportStaticStatement && !it.isOnDemand) } + && ref.multiResolve(false).mapNotNullTo(HashSet()) { it.element?.unwrapped }.size > 1 + } + + override fun getPostRenameCallback(element: PsiElement, newName: String, elementListener: RefactoringElementListener): Runnable? { + return Runnable { + element.ambiguousImportUsages?.forEach { + val ref = it.reference as? PsiPolyVariantReference ?: return@forEach + if (ref.multiResolve(false).isEmpty()) { + ref.handleElementRename(newName) + } + else { + ref.element?.getStrictParentOfType()?.let { importDirective -> + val fqName = importDirective.importedFqName!! + val newFqName = fqName.parent().child(Name.identifier(newName)) + val importList = importDirective.parent as KtImportList + if (importList.imports.none { it.importedFqName == newFqName }) { + val newImportDirective = KtPsiFactory(element).createImportDirective(ImportPath(newFqName, false)) + importDirective.parent.addAfter(newImportDirective, importDirective) + } + } + } + } + element.ambiguousImportUsages = null + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt.181 b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt.181 new file mode 100644 index 00000000000..86ef29c83a1 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt.181 @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.psi.PsiElement +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor +import org.jetbrains.kotlin.psi.KtTypeParameter + +class RenameKotlinTypeParameterProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement) = element is KtTypeParameter + + override fun findCollisions( + element: PsiElement, + newName: String, + allRenames: MutableMap, + result: MutableList + ) { + val declaration = element as? KtTypeParameter ?: return + val descriptor = declaration.unsafeResolveToDescriptor() + checkRedeclarations(descriptor, newName, result) + } +} \ No newline at end of file