181: Misc: Refine nullability to match changes in the platform
This commit is contained in:
committed by
Nikolay Krasko
parent
8ce65dc715
commit
7988fcabcc
+141
@@ -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<PsiElement, String>) {
|
||||
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<PsiReference> {
|
||||
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<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as ClassifierDescriptor
|
||||
|
||||
val collisions = SmartList<UsageInfo>()
|
||||
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<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val simpleUsages = ArrayList<UsageInfo>(usages.size)
|
||||
val ambiguousImportUsages = com.intellij.util.SmartList<UsageInfo>()
|
||||
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() }
|
||||
}
|
||||
}
|
||||
+65
@@ -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<PsiElement, String>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+245
@@ -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<PsiReference> {
|
||||
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<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
val declaration = element.unwrapped as? KtNamedFunction ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor()
|
||||
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
|
||||
result += SmartList<UsageInfo>().also { collisions ->
|
||||
checkRedeclarations(descriptor, newName, collisions)
|
||||
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
|
||||
checkNewNameUsagesRetargeting(declaration, newName, collisions)
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionWithSupersWrapper(
|
||||
val originalDeclaration: KtNamedFunction,
|
||||
val supers: List<PsiElement>
|
||||
) : KtLightElement<KtNamedFunction, KtNamedFunction>, 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<PsiElement>) {
|
||||
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<PsiElement, String>, 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<UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val simpleUsages = ArrayList<UsageInfo>(usages.size)
|
||||
val ambiguousImportUsages = SmartList<UsageInfo>()
|
||||
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()")
|
||||
}
|
||||
}
|
||||
+61
@@ -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<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
|
||||
|
||||
val collisions = SmartList<UsageInfo>()
|
||||
checkRedeclarations(descriptor, newName, collisions)
|
||||
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
|
||||
checkNewNameUsagesRetargeting(declaration, newName, collisions)
|
||||
result += collisions
|
||||
}
|
||||
|
||||
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
|
||||
super.renameElement(element, newName, usages, listener)
|
||||
|
||||
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
|
||||
}
|
||||
}
|
||||
+478
@@ -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<String?, String?> {
|
||||
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<PsiReference> {
|
||||
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<UsageInfo>
|
||||
) {
|
||||
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<ClassDescriptor> { DescriptorUtils.getSuperclassDescriptors(it) },
|
||||
object : DFS.AbstractNodeHandler<ClassDescriptor, Unit>() {
|
||||
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<PsiClass> { DirectClassInheritorsSearch.search(it) },
|
||||
object : DFS.AbstractNodeHandler<PsiClass, Unit>() {
|
||||
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<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor() as VariableDescriptor
|
||||
|
||||
val collisions = SmartList<UsageInfo>()
|
||||
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<PsiElement>) {
|
||||
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<PsiElement, String>, 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<UsageInfo>, 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<UsageInfo>(),
|
||||
null)
|
||||
|
||||
super.renameElement(element, JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(),
|
||||
refKindUsages[UsageKind.GETTER_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||
null)
|
||||
|
||||
super.renameElement(element, newName,
|
||||
refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||
null)
|
||||
|
||||
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
|
||||
|
||||
dropOverrideKeywordIfNecessary(element)
|
||||
|
||||
listener?.elementRenamed(element)
|
||||
}
|
||||
|
||||
private fun addRenameElements(psiMethod: PsiMethod?,
|
||||
oldName: String?,
|
||||
newName: String?,
|
||||
allRenames: MutableMap<PsiElement, String>,
|
||||
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
|
||||
}
|
||||
}
|
||||
+120
@@ -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<PsiReference> {
|
||||
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<PsiElement, String>, 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<UsageInfo>? 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<KtImportDirective>()?.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
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -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<out PsiElement, String>,
|
||||
result: MutableList<UsageInfo>
|
||||
) {
|
||||
val declaration = element as? KtTypeParameter ?: return
|
||||
val descriptor = declaration.unsafeResolveToDescriptor()
|
||||
checkRedeclarations(descriptor, newName, result)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user