Cleanup 191 patchset logic (KTI-267)

This commit is contained in:
Yunir Salimzyanov
2020-06-04 19:42:45 +03:00
parent 820353ee0e
commit dce19b0ace
35 changed files with 1004 additions and 1230 deletions
+1 -1
View File
@@ -200,7 +200,7 @@ extra["intellijSeparateSdks"] = intellijSeparateSdks
extra["IntellijCoreDependencies"] =
listOf(
if (Platform[191].orHigher()) "asm-all-7.0.1" else "asm-all",
"asm-all-7.0.1",
"guava",
"jdom",
"jna",
-3
View File
@@ -30,9 +30,6 @@ dependencies {
}
testCompile(intellijDep()) { includeJars("util", "idea", "idea_rt", "groovy-all", rootProject = rootProject) }
Platform[191].orLower {
testCompile(intellijDep()) { includeJars("jps-builders") }
}
Platform[192].orHigher {
testCompile(intellijPluginDep("java")) { includeJars("jps-builders") }
}
@@ -17,10 +17,6 @@ dependencies {
includeJars("extensions", "idea_rt", "util", "asm-all", rootProject = rootProject)
}
Platform[191].orLower {
testCompileOnly(intellijDep()) { includeJars("java-api") }
}
Platform[192].orHigher {
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api") }
testRuntimeOnly(intellijPluginDep("java"))
@@ -9,7 +9,6 @@ package org.jetbrains.kotlin.idea.caches.trackers
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.pom.tree.TreeAspect
import com.intellij.psi.impl.PsiTreeChangeEventImpl
import org.jetbrains.kotlin.idea.KotlinLanguage
/**
@@ -21,61 +20,32 @@ class KotlinCodeBlockModificationListener(
treeAspect: TreeAspect
) : KotlinCodeBlockModificationListenerCompat(project) {
@Suppress("UnstableApiUsage")
private val isLanguageTrackerEnabled = modificationTrackerImpl.isEnableLanguageTrackerCompat
// FIX ME WHEN BUNCH 191 REMOVED
// When there're we no per-language trackers we had to increment global tracker first and process result afterward
private val customIncrement = if (isLanguageTrackerEnabled) 0 else 1
init {
init(
treeAspect,
incOCBCounter = { ktFile ->
if (isLanguageTrackerEnabled) {
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
perModuleOutOfCodeBlockTrackerUpdater.onKotlinPhysicalFileOutOfBlockChange(ktFile, true)
} else {
perModuleOutOfCodeBlockTrackerUpdater.onKotlinPhysicalFileOutOfBlockChange(ktFile, false)
// Increment counter and process changes in PsiModificationTracker.Listener
modificationTrackerImpl.incCounter()
}
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
perModuleOutOfCodeBlockTrackerUpdater.onKotlinPhysicalFileOutOfBlockChange(ktFile, true)
},
kotlinOutOfCodeBlockTrackerProducer = {
if (isLanguageTrackerEnabled) {
SimpleModificationTracker()
} else {
object : SimpleModificationTracker() {
override fun getModificationCount(): Long {
@Suppress("DEPRECATION")
return modificationTrackerImpl.outOfCodeBlockModificationCount
}
}
}
SimpleModificationTracker()
},
psiModificationTrackerListener = {
@Suppress("UnstableApiUsage")
if (isLanguageTrackerEnabled) {
val kotlinTrackerInternalIDECount =
modificationTrackerImpl.forLanguage(KotlinLanguage.INSTANCE).modificationCount
if (kotlinModificationTracker == kotlinTrackerInternalIDECount) {
// Some update that we are not sure is from Kotlin language, as Kotlin language tracker wasn't changed
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
} else {
kotlinModificationTracker = kotlinTrackerInternalIDECount
}
val kotlinTrackerInternalIDECount =
modificationTrackerImpl.forLanguage(KotlinLanguage.INSTANCE).modificationCount
if (kotlinModificationTracker == kotlinTrackerInternalIDECount) {
// Some update that we are not sure is from Kotlin language, as Kotlin language tracker wasn't changed
kotlinOutOfCodeBlockTrackerImpl.incModificationCount()
} else {
kotlinModificationTracker = kotlinTrackerInternalIDECount
}
perModuleOutOfCodeBlockTrackerUpdater.onPsiModificationTrackerUpdate(customIncrement)
perModuleOutOfCodeBlockTrackerUpdater.onPsiModificationTrackerUpdate()
}
)
}
override fun treeChanged(event: PsiTreeChangeEventImpl) {
assert(isLanguageTrackerEnabled)
super.treeChanged(event)
}
companion object {
fun getInstance(project: Project): KotlinCodeBlockModificationListener =
project.getComponent(KotlinCodeBlockModificationListener::class.java)
@@ -1,13 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.caches.trackers
import com.intellij.psi.impl.PsiModificationTrackerImpl
// FIX ME WHEN BUNCH 191 REMOVED
@Suppress("unused")
val PsiModificationTrackerImpl.isEnableLanguageTrackerCompat
get() = true
@@ -38,22 +38,6 @@ abstract class AbstractKotlinInspection : LocalInspectionTool() {
val problemDescriptor = manager.createProblemDescriptor(element, range, description, highlightType, isOnTheFly, *fixes)
registerProblem(problemDescriptor)
}
// FIX ME WHEN BUNCH 191 REMOVED
// a workaround for IDEA-211491
override fun getProblemElement(psiElement: PsiElement): PsiNamedElement? {
val parent = psiElement.parents().firstOrNull { parent ->
when (parent) {
is KtPropertyAccessor -> true
is KtNamedDeclaration -> parent !is KtValVarKeywordOwner || parent.valOrVarKeyword != null
else -> false
}
}
if (parent is KtPropertyAccessor) {
return parent.property
}
return super.getProblemElement(psiElement)
}
}
@Suppress("unused")
-3
View File
@@ -18,9 +18,6 @@ dependencies {
testCompileOnly(intellijDep())
testRuntime(intellijDep())
if (Platform[191].orLower()) {
testRuntimeOnly(intellijPluginDep("Groovy"))
}
Platform[192].orHigher {
testCompileOnly(intellijPluginDep("java"))
testRuntime(intellijPluginDep("java"))
@@ -935,8 +935,6 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
}
// FIX ME WHEN BUNCH 191 REMOVED Can't use property because there's no getter in 192 and thus it isn't property anymore
@Suppress("UsePropertyAccessSyntax")
sourceSet.setSources(sourcesWithTypes.toMap())
}
}
@@ -952,8 +950,6 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
sourceSet.targetCompatibility = ktSourceSetData.targetCompatibility
sourceSet.dependencies += ktSourceSet.dependencies.mapNotNull { mppModel.dependencyMap[it] }
// FIX ME WHEN BUNCH 191 REMOVED Can't use property because there's no getter in 192 and thus it isn't property anymore
@Suppress("UsePropertyAccessSyntax")
sourceSet.setSources(linkedMapOf(
ktSourceSet.sourceType to DefaultExternalSourceDirectorySet().also { dirSet ->
dirSet.srcDirs = ktSourceSet.sourceDirs
@@ -47,6 +47,7 @@ import org.jetbrains.plugins.gradle.execution.build.GradleProjectTaskRunner;
import org.jetbrains.plugins.gradle.service.project.GradleBuildSrcProjectsResolver;
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil;
import org.jetbrains.plugins.gradle.service.task.GradleTaskManager;
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
import org.jetbrains.plugins.gradle.settings.GradleSettings;
import org.jetbrains.plugins.gradle.util.GradleConstants;
@@ -58,7 +59,6 @@ import java.util.stream.Collectors;
import static com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration.PROGRESS_LISTENER_KEY;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.*;
import static com.intellij.openapi.util.text.StringUtil.*;
import static org.jetbrains.kotlin.idea.gradle.execution.KotlinMPPGradleProjectTaskRunnerUtilKt.isDelegatedBuild;
import static org.jetbrains.plugins.gradle.execution.GradleRunnerUtil.resolveProjectPath;
/**
@@ -180,7 +180,7 @@ class KotlinMPPGradleProjectTaskRunner extends ProjectTaskRunner
final ModuleBuildTask moduleBuildTask = (ModuleBuildTask) projectTask;
final Module module = moduleBuildTask.getModule();
if (! isDelegatedBuild(module)) {
if (module.getProject().getPresentableUrl() != null && GradleProjectSettings.isDelegatedBuildEnabled(module)) {
return false;
}
@@ -45,6 +45,7 @@ import org.jetbrains.plugins.gradle.execution.build.GradleProjectTaskRunner;
import org.jetbrains.plugins.gradle.service.project.GradleBuildSrcProjectsResolver;
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil;
import org.jetbrains.plugins.gradle.service.task.GradleTaskManager;
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
import org.jetbrains.plugins.gradle.settings.GradleSettings;
import org.jetbrains.plugins.gradle.util.GradleConstants;
@@ -56,7 +57,6 @@ import java.util.stream.Collectors;
import static com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration.PROGRESS_LISTENER_KEY;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.*;
import static com.intellij.openapi.util.text.StringUtil.*;
import static org.jetbrains.kotlin.idea.gradle.execution.KotlinMPPGradleProjectTaskRunnerUtilKt.isDelegatedBuild;
import static org.jetbrains.plugins.gradle.execution.GradleRunnerUtil.resolveProjectPath;
/**
@@ -177,7 +177,7 @@ class KotlinMPPGradleProjectTaskRunner extends ProjectTaskRunner
final ModuleBuildTask moduleBuildTask = (ModuleBuildTask) projectTask;
final Module module = moduleBuildTask.getModule();
if (! isDelegatedBuild(module)) {
if (module.getProject().getPresentableUrl() != null && GradleProjectSettings.isDelegatedBuildEnabled(module)) {
return false;
}
@@ -1,18 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.gradle.execution
import com.intellij.openapi.module.Module
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
// FIX ME WHEN BUNCH 191 REMOVED
fun isDelegatedBuild(module: Module): Boolean {
val projectUrl = module.project.presentableUrl
if (projectUrl == null || !GradleProjectSettings.isDelegatedBuildEnabled(module)) {
return false
}
return true
}
@@ -43,10 +43,6 @@ dependencies {
compileOnly(intellijPluginDep("maven"))
}
Platform[191].orLower {
compileOnly(intellijDep()) { includeJars("java-api", "java-impl") }
}
Platform[192].orHigher {
compileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
@@ -5,23 +5,18 @@
package org.jetbrains.kotlin.idea.test
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
import com.intellij.util.ThrowableRunnable
// FIX ME WHEN BUNCH 191 REMOVED
abstract class KotlinLightPlatformCodeInsightTestCase : LightPlatformCodeInsightTestCase() {
protected inline val project_: Project get() = project
protected inline val editor_: Editor get() = editor
override fun setUp() {
super.setUp()
enableKotlinOfficialCodeStyle(project_)
enableKotlinOfficialCodeStyle(project)
}
override fun tearDown() = runAll(
ThrowableRunnable { disableKotlinOfficialCodeStyle(project_) },
ThrowableRunnable { disableKotlinOfficialCodeStyle(project) },
ThrowableRunnable { super.tearDown() },
)
}
@@ -16,7 +16,6 @@ import com.intellij.psi.PsiFile
import com.intellij.testFramework.LightIdeaTestCase
// FIX ME WHEN BUNCH as36 REMOVED
// FIX ME WHEN BUNCH 191 REMOVED
@Suppress("DEPRECATION")
@Deprecated("Use KotlinLightCodeInsightFixtureTestCase instead")
abstract class KotlinLightCodeInsightTestCase : com.intellij.testFramework.LightCodeInsightTestCase() {
@@ -32,7 +31,6 @@ abstract class KotlinLightCodeInsightTestCase : com.intellij.testFramework.Light
}
// FIX ME WHEN BUNCH as36 REMOVED
// FIX ME WHEN BUNCH 191 REMOVED
abstract class KotlinLightIdeaTestCase : LightIdeaTestCase() {
protected inline val project_: Project get() = project
protected inline val module_: Module get() = module
@@ -16,7 +16,6 @@ import com.intellij.psi.PsiFile
import com.intellij.testFramework.LightIdeaTestCase
// FIX ME WHEN BUNCH as36 REMOVED
// FIX ME WHEN BUNCH 191 REMOVED
@Suppress("DEPRECATION")
@Deprecated("Use KotlinLightCodeInsightFixtureTestCase instead")
abstract class KotlinLightCodeInsightTestCase : com.intellij.testFramework.LightCodeInsightTestCase() {
@@ -32,7 +31,6 @@ abstract class KotlinLightCodeInsightTestCase : com.intellij.testFramework.Light
}
// FIX ME WHEN BUNCH as36 REMOVED
// FIX ME WHEN BUNCH 191 REMOVED
abstract class KotlinLightIdeaTestCase : LightIdeaTestCase() {
protected inline val project_: Project get() = project
protected inline val module_: Module get() = module
@@ -19,10 +19,6 @@ dependencies {
testCompile(intellijPluginDep("stream-debugger"))
Platform[191].orLower {
testCompileOnly(intellijDep()) { includeJars("java-api", "java-impl") }
}
Platform[192].orHigher {
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl", "aether-dependency-resolver") }
testRuntime(intellijPluginDep("java"))
@@ -33,7 +33,7 @@ abstract class AbstractPerformanceTypingIndentationTest : KotlinLightPlatformCod
val originalFileText = FileUtil.loadFile(File(originFilePath), true)
try {
val configurator = FormatSettingsUtil.createConfigurator(originalFileText, CodeStyle.getSettings(project_))
val configurator = FormatSettingsUtil.createConfigurator(originalFileText, CodeStyle.getSettings(project))
configurator.configureSettings()
performanceTest<Unit, Unit> {
@@ -48,8 +48,8 @@ abstract class AbstractPerformanceTypingIndentationTest : KotlinLightPlatformCod
executeAction(IdeActions.ACTION_EDITOR_ENTER)
}
tearDown {
val actualTextWithCaret = StringBuilder(editor_.document.text).insert(
editor_.caretModel.offset,
val actualTextWithCaret = StringBuilder(editor.document.text).insert(
editor.caretModel.offset,
EditorTestUtil.CARET_TAG
).toString()
@@ -57,7 +57,7 @@ abstract class AbstractPerformanceTypingIndentationTest : KotlinLightPlatformCod
}
}
} finally {
CodeStyle.getSettings(project_).clearCodeStyleSettings()
CodeStyle.getSettings(project).clearCodeStyleSettings()
}
}
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.refactoring.rename.RenameJavaSyntheticPropertyHandler
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinPropertyProcessorCompat
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinPropertyProcessor
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
@@ -148,7 +148,7 @@ class KotlinElementDescriptionProvider : ElementDescriptionProvider {
is KtImportAlias -> KotlinBundle.message("find.usages.import.alias")
is RenameJavaSyntheticPropertyHandler.SyntheticPropertyWrapper -> KotlinBundle.message("find.usages.property")
is KtLightClassForFacade -> KotlinBundle.message("find.usages.facade.class")
is RenameKotlinPropertyProcessorCompat.PropertyMethodWrapper -> KotlinBundle.message("find.usages.property.accessor")
is RenameKotlinPropertyProcessor.PropertyMethodWrapper -> KotlinBundle.message("find.usages.property.accessor")
else -> null
}
@@ -1,14 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.refactoring.move
import com.intellij.refactoring.move.MoveHandlerDelegate
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesHandler
// FIX ME WHEN BUNCH 191 REMOVED
typealias MoveHandlerDelegateCompat = MoveHandlerDelegate
// FIX ME WHEN BUNCH 191 REMOVED
typealias MoveFilesOrDirectoriesHandlerCompat = MoveFilesOrDirectoriesHandler
@@ -25,6 +25,7 @@ import com.intellij.openapi.ui.Messages
import com.intellij.psi.*
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveHandlerDelegate
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.intellij.refactoring.util.CommonRefactoringUtil
@@ -33,7 +34,6 @@ import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.move.MoveHandlerDelegateCompat
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinSelectNestedClassRefactoringDialog
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinNestedClassesDialog
@@ -91,7 +91,7 @@ private val defaultHandlerActions = object : MoveKotlinDeclarationsHandlerAction
}
class MoveKotlinDeclarationsHandler internal constructor(private val handlerActions: MoveKotlinDeclarationsHandlerActions) :
MoveHandlerDelegateCompat() {
MoveHandlerDelegate() {
constructor() : this(defaultHandlerActions)
@@ -13,9 +13,9 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveHandler
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesHandler
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.idea.refactoring.move.MoveFilesOrDirectoriesHandlerCompat
import org.jetbrains.kotlin.idea.refactoring.move.logFusForMoveRefactoring
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesModel
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
class KotlinMoveFilesOrDirectoriesHandler : MoveFilesOrDirectoriesHandlerCompat() {
class KotlinMoveFilesOrDirectoriesHandler : MoveFilesOrDirectoriesHandler() {
private fun adjustElements(elements: Array<out PsiElement>): Array<PsiFileSystemItem>? {
return elements.map {
when {
@@ -5,12 +5,130 @@
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.psi.search.SearchScope
import com.intellij.refactoring.listeners.RefactoringElementListener
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.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings
import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals
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.ArrayList
// FIX ME WHEN BUNCH 191 REMOVED
class RenameKotlinClassifierProcessor : RenameKotlinClassifierProcessorCompat() {
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) = KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_CLASS
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_CLASS = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_FOR_TEXT_FOR_CLASS
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.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
classOrObject.withExpectedActuals().forEach {
val file = it.containingKtFile
val virtualFile = file.virtualFile
if (virtualFile != null) {
val nameWithoutExtensions = virtualFile.nameWithoutExtension
if (nameWithoutExtensions == it.name) {
val newFileName = newName + "." + virtualFile.extension
allRenames.put(file, newFileName)
forElement(file).prepareRenaming(file, newFileName, allRenames)
}
}
}
}
protected fun processFoundReferences(
element: PsiElement,
references: Collection<PsiReference>
): Collection<PsiReference> {
if (element is KtObjectDeclaration && element.isCompanion()) {
return references.filter { !it.isCompanionObjectClassReference() }
}
return references
}
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 collisions = SmartList<UsageInfo>()
checkRedeclarations(declaration, 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<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() }
}
override fun findReferences(
element: PsiElement,
@@ -1,131 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
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.listeners.RefactoringElementListener
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.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings
import org.jetbrains.kotlin.idea.refactoring.withExpectedActuals
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.*
// FIX ME WHEN BUNCH 191 REMOVED
abstract class RenameKotlinClassifierProcessorCompat : 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) = KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_CLASS
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_CLASS = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_FOR_TEXT_FOR_CLASS
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.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
classOrObject.withExpectedActuals().forEach {
val file = it.containingKtFile
val virtualFile = file.virtualFile
if (virtualFile != null) {
val nameWithoutExtensions = virtualFile.nameWithoutExtension
if (nameWithoutExtensions == it.name) {
val newFileName = newName + "." + virtualFile.extension
allRenames.put(file, newFileName)
forElement(file).prepareRenaming(file, newFileName, allRenames)
}
}
}
}
protected fun processFoundReferences(
element: PsiElement,
references: Collection<PsiReference>
): Collection<PsiReference> {
if (element is KtObjectDeclaration && element.isCompanion()) {
return references.filter { !it.isCompanionObjectClassReference() }
}
return references
}
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 collisions = SmartList<UsageInfo>()
checkRedeclarations(declaration, 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<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() }
}
}
@@ -5,12 +5,249 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
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.RefactoringBundle
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.*
import com.intellij.refactoring.util.CommonRefactoringUtil
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.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.*
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.util.ArrayList
// FIX ME WHEN BUNCH 191 REMOVED
class RenameKotlinFunctionProcessor : RenameKotlinFunctionProcessorCompat() {
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) = KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_METHOD
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_METHOD = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_FOR_TEXT_FOR_METHOD
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.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)
}
protected fun processFoundReferences(
element: PsiElement,
allReferences: Collection<PsiReference>
): Collection<PsiReference> {
return when {
getJvmName(element) == null -> allReferences
element is KtElement -> allReferences.filterIsInstance<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
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
result += SmartList<UsageInfo>().also { collisions ->
checkRedeclarations(declaration, 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, KotlinBundle.message("text.rename.as.part.of.phrase"))
if (chosenElements.size > 1) FunctionWithSupersWrapper(element, chosenElements) else wrappedMethod
}
}
if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) {
return substitutedJavaElement.kotlinOrigin as? KtNamedFunction
}
val canRename = try {
PsiElementRenameHandler.canRename(element.project, editor, substitutedJavaElement)
} catch (e: CommonRefactoringUtil.RefactoringErrorHintException) {
false
}
return if (canRename) substitutedJavaElement else element
}
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(), RefactoringBundle.message("rename.title"), 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)
}
val originalName = (element.unwrapped as? KtNamedFunction)?.name ?: return
for (declaration in ((element as? FunctionWithSupersWrapper)?.supers ?: listOf(element))) {
val psiMethod = wrapPsiMethod(declaration) ?: continue
allRenames[declaration] = newName
val baseName = psiMethod.name
val newBaseName = if (KotlinTypeMapper.InternalNameMapper.demangleInternalName(baseName) == originalName) {
KotlinTypeMapper.InternalNameMapper.mangleInternalName(
newName,
KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix(baseName)!!
)
} else newName
if (psiMethod.containingClass != null) {
psiMethod.forEachOverridingMethod(scope) {
val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it
if (overrider is SyntheticElement) return@forEachOverridingMethod true
val overriderName = overrider.name
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newBaseName)
if (newOverriderName != null) {
RenameProcessor.assertNonCompileElement(overrider)
allRenames[overrider] = newOverriderName
}
return@forEachOverridingMethod true
}
}
}
}
override fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = SmartList<UsageInfo>()
ForeignUsagesRenameProcessor.processAll(element, newName, usages, fallbackHandler = { usage ->
if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) {
usage.apply()
return@processAll
}
if (usage.isAmbiguousImportUsage()) {
ambiguousImportUsages += usage
} else {
if (!renameMangledUsageIfPossible(usage, element, newName)) {
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()")
}
override fun findReferences(
element: PsiElement,
@@ -1,250 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
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.RefactoringBundle
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.*
import com.intellij.refactoring.util.CommonRefactoringUtil
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.codegen.state.KotlinTypeMapper.InternalNameMapper.demangleInternalName
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.mangleInternalName
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.*
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.util.*
// FIX ME WHEN BUNCH 191 REMOVED
abstract class RenameKotlinFunctionProcessorCompat : 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) = KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_METHOD
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_METHOD = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_FOR_TEXT_FOR_METHOD
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.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)
}
protected fun processFoundReferences(
element: PsiElement,
allReferences: Collection<PsiReference>
): Collection<PsiReference> {
return when {
getJvmName(element) == null -> allReferences
element is KtElement -> allReferences.filterIsInstance<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
checkConflictsAndReplaceUsageInfos(element, allRenames, result)
result += SmartList<UsageInfo>().also { collisions ->
checkRedeclarations(declaration, 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, KotlinBundle.message("text.rename.as.part.of.phrase"))
if (chosenElements.size > 1) FunctionWithSupersWrapper(element, chosenElements) else wrappedMethod
}
}
if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) {
return substitutedJavaElement.kotlinOrigin as? KtNamedFunction
}
val canRename = try {
PsiElementRenameHandler.canRename(element.project, editor, substitutedJavaElement)
} catch (e: CommonRefactoringUtil.RefactoringErrorHintException) {
false
}
return if (canRename) substitutedJavaElement else element
}
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(), RefactoringBundle.message("rename.title"), 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)
}
val originalName = (element.unwrapped as? KtNamedFunction)?.name ?: return
for (declaration in ((element as? FunctionWithSupersWrapper)?.supers ?: listOf(element))) {
val psiMethod = wrapPsiMethod(declaration) ?: continue
allRenames[declaration] = newName
val baseName = psiMethod.name
val newBaseName = if (demangleInternalName(baseName) == originalName) {
mangleInternalName(newName, getModuleNameSuffix(baseName)!!)
} else newName
if (psiMethod.containingClass != null) {
psiMethod.forEachOverridingMethod(scope) {
val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it
if (overrider is SyntheticElement) return@forEachOverridingMethod true
val overriderName = overrider.name
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newBaseName)
if (newOverriderName != null) {
RenameProcessor.assertNonCompileElement(overrider)
allRenames[overrider] = newOverriderName
}
return@forEachOverridingMethod true
}
}
}
}
override fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = SmartList<UsageInfo>()
ForeignUsagesRenameProcessor.processAll(element, newName, usages, fallbackHandler = { usage ->
if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) {
usage.apply()
return@processAll
}
if (usage.isAmbiguousImportUsage()) {
ambiguousImportUsages += usage
} else {
if (!renameMangledUsageIfPossible(usage, element, newName)) {
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()")
}
}
@@ -5,34 +5,417 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
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.RefactoringBundle
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.asJava.propertyNameByAccessor
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
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.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
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.KotlinRefactoringSettings
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.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
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
// FIX ME WHEN BUNCH 191 REMOVED
class RenameKotlinPropertyProcessor : RenameKotlinPropertyProcessorCompat() {
class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
val namedUnwrappedElement = element.namedUnwrappedElement
return namedUnwrappedElement is KtProperty || namedUnwrappedElement is PropertyMethodWrapper ||
(namedUnwrappedElement is KtParameter && namedUnwrappedElement.hasValOrVar())
}
override fun isToSearchInComments(psiElement: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_FIELD
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_FOR_TEXT_FOR_FIELD
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.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
}
protected fun processFoundReferences(
element: PsiElement,
allReferences: Collection<PsiReference>
): Collection<PsiReference> {
val references = allReferences.filterNot { it is KtDestructuringDeclarationReference }
val (getterJvmName, setterJvmName) = getJvmNames(element)
return when {
getterJvmName == null && setterJvmName == null -> references
element is KtElement -> references.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) references.filterNot { it is KtReference } else references
}
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 = KotlinBundle.message("text.0.will.clash.with.existing.1.in.2", what, withWhat, 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 resolutionFacade = declaration.getResolutionFacade()
val descriptor = declaration.unsafeResolveToDescriptor(resolutionFacade) as VariableDescriptor
val collisions = SmartList<UsageInfo>()
checkRedeclarations(declaration, newName, collisions, resolutionFacade, descriptor)
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 message = if (containsText != null)
KotlinBundle.message("text.do.you.want.to.rename.base.property.from.0", containsText)
else
KotlinBundle.message("text.do.you.want.to.rename.base.property")
val result = Messages.showYesNoCancelDialog(
deepestSuperDeclaration.project,
message,
KotlinBundle.message("text.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, RefactoringBundle.message("rename.title"), 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(
KotlinBundle.message("text.do.you.want.to.rename.0.as.well", accessorToRename.name),
RefactoringBundle.message("rename.title"),
Messages.getQuestionIcon()
) == Messages.YES
) {
allRenames[accessorToRename] = newAccessorName
}
}
for (propertyMethod in propertyMethods) {
val mangledPropertyName = if (propertyMethod is KtLightMethod && propertyMethod.isMangled) {
val suffix = KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix(propertyMethod.name)
if (suffix != null && newPropertyName != null) KotlinTypeMapper.InternalNameMapper.mangleInternalName(
newPropertyName,
suffix
) else null
} else null
val adjustedPropertyName = mangledPropertyName ?: newPropertyName
if (element is KtDeclaration && adjustedPropertyName != null) {
val wrapper = PropertyMethodWrapper(propertyMethod)
when {
JvmAbi.isGetterName(propertyMethod.name) && getterJvmName == null ->
allRenames[wrapper] = JvmAbi.getterName(adjustedPropertyName)
JvmAbi.isSetterName(propertyMethod.name) && setterJvmName == null ->
allRenames[wrapper] = JvmAbi.setterName(adjustedPropertyName)
}
}
addRenameElements(propertyMethod, (element as PsiNamedElement).name, adjustedPropertyName, allRenames, scope)
}
}
protected enum class UsageKind {
SIMPLE_PROPERTY_USAGE,
GETTER_USAGE,
SETTER_USAGE
}
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 demangledName =
if (newName != null && overrider is KtLightMethod && overrider.isMangled) KotlinTypeMapper.InternalNameMapper.demangleInternalName(
newName
) else null
val adjustedName = demangledName ?: newName
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, adjustedName)
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
}
override fun findReferences(
element: PsiElement,
@@ -1,407 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
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.RefactoringBundle
import com.intellij.refactoring.rename.RenameProcessor
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.codegen.state.KotlinTypeMapper.InternalNameMapper.demangleInternalName
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.mangleInternalName
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.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethodsWithPopup
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.references.KtReference
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.resolve.BindingContext
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
// FIX ME WHEN BUNCH 191 REMOVED
abstract class RenameKotlinPropertyProcessorCompat : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
val namedUnwrappedElement = element.namedUnwrappedElement
return namedUnwrappedElement is KtProperty || namedUnwrappedElement is PropertyMethodWrapper ||
(namedUnwrappedElement is KtParameter && namedUnwrappedElement.hasValOrVar())
}
override fun isToSearchInComments(psiElement: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_FIELD
override fun setToSearchInComments(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.RENAME_SEARCH_IN_COMMENTS_FOR_FIELD = enabled
}
override fun isToSearchForTextOccurrences(element: PsiElement) = KotlinRefactoringSettings.instance.RENAME_SEARCH_FOR_TEXT_FOR_FIELD
override fun setToSearchForTextOccurrences(element: PsiElement, enabled: Boolean) {
KotlinRefactoringSettings.instance.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
}
protected fun processFoundReferences(
element: PsiElement,
allReferences: Collection<PsiReference>
): Collection<PsiReference> {
val references = allReferences.filterNot { it is KtDestructuringDeclarationReference }
val (getterJvmName, setterJvmName) = getJvmNames(element)
return when {
getterJvmName == null && setterJvmName == null -> references
element is KtElement -> references.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) references.filterNot { it is KtReference } else references
}
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 = KotlinBundle.message("text.0.will.clash.with.existing.1.in.2", what, withWhat, 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 resolutionFacade = declaration.getResolutionFacade()
val descriptor = declaration.unsafeResolveToDescriptor(resolutionFacade) as VariableDescriptor
val collisions = SmartList<UsageInfo>()
checkRedeclarations(declaration, newName, collisions, resolutionFacade, descriptor)
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 message = if (containsText != null)
KotlinBundle.message("text.do.you.want.to.rename.base.property.from.0", containsText)
else
KotlinBundle.message("text.do.you.want.to.rename.base.property")
val result = Messages.showYesNoCancelDialog(
deepestSuperDeclaration.project,
message,
KotlinBundle.message("text.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, RefactoringBundle.message("rename.title"), 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(
KotlinBundle.message("text.do.you.want.to.rename.0.as.well", accessorToRename.name),
RefactoringBundle.message("rename.title"),
Messages.getQuestionIcon()
) == Messages.YES
) {
allRenames[accessorToRename] = newAccessorName
}
}
for (propertyMethod in propertyMethods) {
val mangledPropertyName = if (propertyMethod is KtLightMethod && propertyMethod.isMangled) {
val suffix = getModuleNameSuffix(propertyMethod.name)
if (suffix != null && newPropertyName != null) mangleInternalName(newPropertyName, suffix) else null
} else null
val adjustedPropertyName = mangledPropertyName ?: newPropertyName
if (element is KtDeclaration && adjustedPropertyName != null) {
val wrapper = PropertyMethodWrapper(propertyMethod)
when {
JvmAbi.isGetterName(propertyMethod.name) && getterJvmName == null ->
allRenames[wrapper] = JvmAbi.getterName(adjustedPropertyName)
JvmAbi.isSetterName(propertyMethod.name) && setterJvmName == null ->
allRenames[wrapper] = JvmAbi.setterName(adjustedPropertyName)
}
}
addRenameElements(propertyMethod, (element as PsiNamedElement).name, adjustedPropertyName, allRenames, scope)
}
}
protected enum class UsageKind {
SIMPLE_PROPERTY_USAGE,
GETTER_USAGE,
SETTER_USAGE
}
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 demangledName =
if (newName != null && overrider is KtLightMethod && overrider.isMangled) demangleInternalName(newName) else null
val adjustedName = demangledName ?: newName
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, adjustedName)
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
}
}
@@ -5,14 +5,192 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
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.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.getImportAlias
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinMethodReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
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.*
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.ArrayList
// FIX ME WHEN BUNCH 191 REMOVED
abstract class RenameKotlinPsiProcessor : RenameKotlinPsiProcessorCompat() {
abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
class MangledJavaRefUsageInfo(
val manglingSuffix: String,
element: PsiElement,
ref: PsiReference,
referenceElement: PsiElement
) : MoveRenameUsageInfo(
referenceElement,
ref,
ref.getRangeInElement().getStartOffset(),
ref.getRangeInElement().getEndOffset(),
element,
false
)
override fun canProcessElement(element: PsiElement): Boolean = element is KtNamedDeclaration
protected fun findReferences(
element: PsiElement,
searchParameters: KotlinReferencesSearchParameters
): Collection<PsiReference> {
val references = ReferencesSearch.search(searchParameters).toMutableSet()
if (element is KtNamedFunction || (element is KtProperty && !element.isLocal) || (element is KtParameter && element.hasValOrVar())) {
element.toLightMethods().flatMapTo(references) { method ->
MethodReferencesSearch.search(
KotlinMethodReferencesSearchParameters(
method,
kotlinOptions = KotlinReferencesSearchOptions(
acceptImportAlias = false
)
)
)
}
}
return references.filter {
// have to filter so far as
// - text-matched reference could be named as imported alias and found in ReferencesSearch
// - MethodUsagesSearcher could create its own MethodReferencesSearchParameters regardless provided one
it.element.getNonStrictParentOfType<KtImportDirective>() != null || it.getImportAlias() == null
}
}
override fun createUsageInfo(element: PsiElement, ref: PsiReference, referenceElement: PsiElement): UsageInfo {
if (ref !is KtReference) {
val targetElement = ref.resolve()
if (targetElement is KtLightMethod && targetElement.isMangled) {
KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix(targetElement.name)?.let {
return MangledJavaRefUsageInfo(
it,
element,
ref,
referenceElement
)
}
}
}
return super.createUsageInfo(element, ref, referenceElement)
}
override fun getElementToSearchInStringsAndComments(element: PsiElement): PsiElement? {
val unwrapped = element.unwrapped ?: return null
if ((unwrapped is KtDeclaration) && KtPsiUtil.isLocal(unwrapped)) return null
return element
}
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
}
protected fun renameMangledUsageIfPossible(usage: UsageInfo, element: PsiElement, newName: String): Boolean {
val chosenName = (if (usage is MangledJavaRefUsageInfo) {
KotlinTypeMapper.InternalNameMapper.mangleInternalName(newName, usage.manglingSuffix)
} else {
val reference = usage.reference
if (reference is KtReference) {
if (element is KtLightMethod && element.isMangled) {
KotlinTypeMapper.InternalNameMapper.demangleInternalName(newName)
} else null
} else null
}) ?: return false
usage.reference?.handleElementRename(chosenName)
return true
}
override fun renameElement(
element: PsiElement,
newName: String,
usages: Array<UsageInfo>,
listener: RefactoringElementListener?
) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
ForeignUsagesRenameProcessor.processAll(element, newName, usages, fallbackHandler = { usage ->
if (renameMangledUsageIfPossible(usage, element, newName)) return@processAll
simpleUsages += usage
})
RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener)
}
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()) {
if (!renameMangledUsageIfPossible(it, element, newName)) {
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 { directive -> directive.importedFqName == newFqName }) {
val newImportDirective = KtPsiFactory(element).createImportDirective(ImportPath(newFqName, false))
importDirective.parent.addAfter(newImportDirective, importDirective)
}
}
}
}
element.ambiguousImportUsages = null
}
}
override fun findReferences(
element: PsiElement,
@@ -1,195 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
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.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.getImportAlias
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinMethodReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
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.*
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.ArrayList
import kotlin.collections.*
// FIX ME WHEN BUNCH 191 REMOVED
abstract class RenameKotlinPsiProcessorCompat : RenamePsiElementProcessor() {
class MangledJavaRefUsageInfo(
val manglingSuffix: String,
element: PsiElement,
ref: PsiReference,
referenceElement: PsiElement
) : MoveRenameUsageInfo(
referenceElement,
ref,
ref.getRangeInElement().getStartOffset(),
ref.getRangeInElement().getEndOffset(),
element,
false
)
override fun canProcessElement(element: PsiElement): Boolean = element is KtNamedDeclaration
protected fun findReferences(
element: PsiElement,
searchParameters: KotlinReferencesSearchParameters
): Collection<PsiReference> {
val references = ReferencesSearch.search(searchParameters).toMutableSet()
if (element is KtNamedFunction || (element is KtProperty && !element.isLocal) || (element is KtParameter && element.hasValOrVar())) {
element.toLightMethods().flatMapTo(references) { method ->
MethodReferencesSearch.search(
KotlinMethodReferencesSearchParameters(
method,
kotlinOptions = KotlinReferencesSearchOptions(
acceptImportAlias = false
)
)
)
}
}
return references.filter {
// have to filter so far as
// - text-matched reference could be named as imported alias and found in ReferencesSearch
// - MethodUsagesSearcher could create its own MethodReferencesSearchParameters regardless provided one
it.element.getNonStrictParentOfType<KtImportDirective>() != null || it.getImportAlias() == null
}
}
override fun createUsageInfo(element: PsiElement, ref: PsiReference, referenceElement: PsiElement): UsageInfo {
if (ref !is KtReference) {
val targetElement = ref.resolve()
if (targetElement is KtLightMethod && targetElement.isMangled) {
KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix(targetElement.name)?.let {
return MangledJavaRefUsageInfo(
it,
element,
ref,
referenceElement
)
}
}
}
return super.createUsageInfo(element, ref, referenceElement)
}
override fun getElementToSearchInStringsAndComments(element: PsiElement): PsiElement? {
val unwrapped = element.unwrapped ?: return null
if ((unwrapped is KtDeclaration) && KtPsiUtil.isLocal(unwrapped)) return null
return element
}
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
}
protected fun renameMangledUsageIfPossible(usage: UsageInfo, element: PsiElement, newName: String): Boolean {
val chosenName = (if (usage is MangledJavaRefUsageInfo) {
KotlinTypeMapper.InternalNameMapper.mangleInternalName(newName, usage.manglingSuffix)
} else {
val reference = usage.reference
if (reference is KtReference) {
if (element is KtLightMethod && element.isMangled) {
KotlinTypeMapper.InternalNameMapper.demangleInternalName(newName)
} else null
} else null
}) ?: return false
usage.reference?.handleElementRename(chosenName)
return true
}
override fun renameElement(
element: PsiElement,
newName: String,
usages: Array<UsageInfo>,
listener: RefactoringElementListener?
) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
ForeignUsagesRenameProcessor.processAll(element, newName, usages, fallbackHandler = { usage ->
if (renameMangledUsageIfPossible(usage, element, newName)) return@processAll
simpleUsages += usage
})
RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener)
}
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()) {
if (!renameMangledUsageIfPossible(it, element, newName)) {
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 { directive -> directive.importedFqName == newFqName }) {
val newImportDirective = KtPsiFactory(element).createImportDirective(ImportPath(newFqName, false))
importDirective.parent.addAfter(newImportDirective, importDirective)
}
}
}
}
element.ambiguousImportUsages = null
}
}
}
@@ -23,7 +23,7 @@ abstract class AbstractTypingIndentationTestBase : KotlinLightPlatformCodeInsigh
val originFilePath = testFileName + testFileExtension
val originalFileText = FileUtil.loadFile(File(originFilePath), true)
try {
val configurator = FormatSettingsUtil.createConfigurator(originalFileText, CodeStyle.getSettings(project_))
val configurator = FormatSettingsUtil.createConfigurator(originalFileText, CodeStyle.getSettings(project))
if (!inverted) {
configurator.configureSettings()
} else {
@@ -32,16 +32,16 @@ abstract class AbstractTypingIndentationTestBase : KotlinLightPlatformCodeInsigh
doNewlineTest(originFilePath, afterFilePath)
} finally {
CodeStyle.getSettings(project_).clearCodeStyleSettings()
CodeStyle.getSettings(project).clearCodeStyleSettings()
}
}
private fun doNewlineTest(beforeFilePath: String, afterFilePath: String) {
configureByFile(beforeFilePath)
type('\n')
val caretModel = editor_.caretModel
val caretModel = editor.caretModel
val offset = caretModel.offset
val actualTextWithCaret = StringBuilder(editor_.document.text).insert(offset, EditorTestUtil.CARET_TAG).toString()
val actualTextWithCaret = StringBuilder(editor.document.text).insert(offset, EditorTestUtil.CARET_TAG).toString()
KotlinTestUtils.assertEqualsToFile(File(afterFilePath), actualTextWithCaret)
}
@@ -1,17 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.refactoring.move
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.refactoring.move.MoveHandlerDelegate
// FIX ME WHEN BUNCH 191 REMOVED
internal fun MoveHandlerDelegate.canMoveCompat(
elements: Array<out PsiElement>,
targetContainer: PsiElement?,
reference: PsiReference?
): Boolean = canMove(elements, targetContainer, reference)
@@ -51,90 +51,90 @@ class MoveKotlinDeclarationsHandlerTest : KotlinMultiFileTestCase() {
fun testObjectLiteral() = doTest { rootDir, handler ->
val objectDeclaration = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtObjectDeclaration>()!!
assert(!handler.canMoveCompat(arrayOf<PsiElement>(objectDeclaration), null, null))
assert(!handler.canMove(arrayOf<PsiElement>(objectDeclaration), null, null))
}
fun testLocalClass() = doTest { rootDir, handler ->
val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtClass>()!!
assert(!handler.canMoveCompat(arrayOf<PsiElement>(klass), null, null))
assert(!handler.canMove(arrayOf<PsiElement>(klass), null, null))
}
fun testLocalFun() = doTest { rootDir, handler ->
val function = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtNamedFunction>()!!
assert(!handler.canMoveCompat(arrayOf<PsiElement>(function), null, null))
assert(!handler.canMove(arrayOf<PsiElement>(function), null, null))
}
fun testLocalVal() = doTest { rootDir, handler ->
val property = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtProperty>()!!
assert(!handler.canMoveCompat(arrayOf<PsiElement>(property), null, null))
assert(!handler.canMove(arrayOf<PsiElement>(property), null, null))
}
fun testMemberFun() = doTest { rootDir, handler ->
val function = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtNamedFunction>()!!
assert(!handler.canMoveCompat(arrayOf<PsiElement>(function), null, null))
assert(!handler.canMove(arrayOf<PsiElement>(function), null, null))
}
fun testMemberVal() = doTest { rootDir, handler ->
val property = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtProperty>()!!
assert(!handler.canMoveCompat(arrayOf<PsiElement>(property), null, null))
assert(!handler.canMove(arrayOf<PsiElement>(property), null, null))
}
fun testNestedClass() = doTest { rootDir, handler ->
val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtClass>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(klass), null, null))
assert(handler.canMove(arrayOf<PsiElement>(klass), null, null))
}
fun testInnerClass() = doTest { rootDir, handler ->
val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtClass>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(klass), null, null))
assert(handler.canMove(arrayOf<PsiElement>(klass), null, null))
}
fun testTopLevelClass() = doTest { rootDir, handler ->
val klass = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtClass>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(klass), null, null))
assert(handler.canMove(arrayOf<PsiElement>(klass), null, null))
}
fun testTopLevelFun() = doTest { rootDir, handler ->
val function = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtNamedFunction>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(function), null, null))
assert(handler.canMove(arrayOf<PsiElement>(function), null, null))
}
fun testTopLevelVal() = doTest { rootDir, handler ->
val property = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtProperty>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(property), null, null))
assert(handler.canMove(arrayOf<PsiElement>(property), null, null))
}
fun testMultipleNestedClasses() = doTest { rootDir, handler ->
val classes = getElementsAtCarets(rootDir, "test.kt").map { it.getNonStrictParentOfType<KtClass>()!! }
assert(handler.canMoveCompat(classes.toTypedArray(), null, null))
assert(handler.canMove(classes.toTypedArray(), null, null))
}
fun testNestedAndTopLevelClass() = doTest { rootDir, handler ->
val classes = getElementsAtCarets(rootDir, "test.kt").map { it.getNonStrictParentOfType<KtClass>()!! }
assert(!handler.canMoveCompat(classes.toTypedArray(), null, null))
assert(!handler.canMove(classes.toTypedArray(), null, null))
}
fun testMultipleTopLevelDeclarations() = doTest { rootDir, handler ->
val declarations = getElementsAtCarets(rootDir, "test.kt").map { it.getNonStrictParentOfType<KtNamedDeclaration>()!! }
assert(handler.canMoveCompat(declarations.toTypedArray(), null, null))
assert(handler.canMove(declarations.toTypedArray(), null, null))
}
fun testMultipleTopLevelDeclarationsInDifferentFiles() = doTest { rootDir, handler ->
val declarations = listOf("test.kt", "test2.kt").flatMap { getElementsAtCarets(rootDir, it) }
.map { it.getNonStrictParentOfType<KtNamedDeclaration>()!! }
assert(handler.canMoveCompat(declarations.toTypedArray(), null, null))
assert(handler.canMove(declarations.toTypedArray(), null, null))
val files = listOf("test.kt", "test2.kt").map { getPsiFile(rootDir, it) }
assert(handler.canMoveCompat(files.toTypedArray(), null, null))
assert(handler.canMove(files.toTypedArray(), null, null))
}
fun testMultipleTopLevelDeclarationsInDifferentDirs() = doTest { rootDir, handler ->
val declarations = listOf("test1/test.kt", "test2/test2.kt").flatMap { getElementsAtCarets(rootDir, it) }
.map { it.getNonStrictParentOfType<KtNamedDeclaration>()!! }
assert(!handler.canMoveCompat(declarations.toTypedArray(), null, null))
assert(!handler.canMove(declarations.toTypedArray(), null, null))
val files = listOf("test1/test.kt", "test2/test2.kt").map { getPsiFile(rootDir, it) }
assert(!handler.canMoveCompat(files.toTypedArray(), null, null))
assert(!handler.canMove(files.toTypedArray(), null, null))
}
fun testFileAndTopLevelDeclarations() = doTest { rootDir, handler ->
@@ -142,20 +142,20 @@ class MoveKotlinDeclarationsHandlerTest : KotlinMultiFileTestCase() {
rootDir,
"test2.kt"
)
assert(!handler.canMoveCompat(elements.toTypedArray(), null, null))
assert(!handler.canMove(elements.toTypedArray(), null, null))
}
fun testCommonTargets() = doTest { rootDir, handler ->
val elementsToMove = arrayOf<PsiElement>(getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtClass>()!!)
val targetPackage = JavaPsiFacade.getInstance(project).findPackage("pack")!!
assert(handler.canMoveCompat(elementsToMove, targetPackage, null))
assert(handler.canMove(elementsToMove, targetPackage, null))
val targetDirectory = getPsiDirectory(rootDir, "pack")
assert(handler.canMoveCompat(elementsToMove, targetDirectory, null))
assert(handler.canMove(elementsToMove, targetDirectory, null))
val targetFile = getPsiFile(rootDir, "pack/test2.kt")
assert(handler.canMoveCompat(elementsToMove, targetFile, null))
assert(handler.canMove(elementsToMove, targetFile, null))
}
fun testTopLevelClassToClass() = doTest { rootDir, handler ->
@@ -164,14 +164,14 @@ class MoveKotlinDeclarationsHandlerTest : KotlinMultiFileTestCase() {
val topLevelTarget = targetFile.declarations.firstIsInstance<KtClass>()
assert(topLevelTarget.name == "B")
assert(!handler.canMoveCompat(elementsToMove, topLevelTarget, null))
assert(!handler.canMove(elementsToMove, topLevelTarget, null))
val annotationTarget = targetFile.declarations.first { it.name == "Ann" } as KtClass
assert(!handler.canMoveCompat(elementsToMove, annotationTarget, null))
assert(!handler.canMove(elementsToMove, annotationTarget, null))
val nestedTarget = topLevelTarget.declarations.firstIsInstance<KtClass>()
assert(nestedTarget.name == "C")
assert(!handler.canMoveCompat(elementsToMove, nestedTarget, null))
assert(!handler.canMove(elementsToMove, nestedTarget, null))
}
fun testNestedClassToClass() = doTest { rootDir, handler ->
@@ -180,33 +180,33 @@ class MoveKotlinDeclarationsHandlerTest : KotlinMultiFileTestCase() {
val topLevelTarget = targetFile.declarations.firstIsInstance<KtClass>()
assert(topLevelTarget.name == "B")
assert(handler.canMoveCompat(elementsToMove, topLevelTarget, null))
assert(handler.canMove(elementsToMove, topLevelTarget, null))
val annotationTarget = targetFile.declarations.first { it.name == "Ann" } as KtClass
assert(!handler.canMoveCompat(elementsToMove, annotationTarget, null))
assert(!handler.canMove(elementsToMove, annotationTarget, null))
val nestedTarget = topLevelTarget.declarations.firstIsInstance<KtClass>()
assert(nestedTarget.name == "C")
assert(handler.canMoveCompat(elementsToMove, nestedTarget, null))
assert(handler.canMove(elementsToMove, nestedTarget, null))
}
fun testTypeAlias() = doTest { rootDir, handler ->
val typeAlias = getElementAtCaret(rootDir, "test.kt").getNonStrictParentOfType<KtTypeAlias>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(typeAlias), null, null))
assert(handler.canMove(arrayOf<PsiElement>(typeAlias), null, null))
}
fun testTopLevelClassInScript() = doTest { rootDir, handler ->
val klass = getElementAtCaret(rootDir, "test.kts").getNonStrictParentOfType<KtClass>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(klass), null, null))
assert(handler.canMove(arrayOf<PsiElement>(klass), null, null))
}
fun testTopLevelFunInScript() = doTest { rootDir, handler ->
val function = getElementAtCaret(rootDir, "test.kts").getNonStrictParentOfType<KtNamedFunction>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(function), null, null))
assert(handler.canMove(arrayOf<PsiElement>(function), null, null))
}
fun testTopLevelValInScript() = doTest { rootDir, handler ->
val property = getElementAtCaret(rootDir, "test.kts").getNonStrictParentOfType<KtProperty>()!!
assert(handler.canMoveCompat(arrayOf<PsiElement>(property), null, null))
assert(handler.canMove(arrayOf<PsiElement>(property), null, null))
}
}
-4
View File
@@ -15,10 +15,6 @@ dependencies {
compileOnly(intellijCoreDep()) { includeJars("intellij-core", "guava", rootProject = rootProject) }
compileOnly(intellijDep()) { includeJars("platform-api", "platform-impl", rootProject = rootProject) }
Platform[191].orLower {
compileOnly(intellijDep()) { includeJars("java-api", "java-impl") }
}
Platform[192].orHigher {
compileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
@@ -23,6 +23,8 @@ import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.readWriteAccess
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.search.or
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -201,7 +203,8 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
private fun KtParameter.rename(newName: String) {
val renamer = RenamePsiElementProcessor.forElement(this)
val findReferences = findReferences(renamer)
val searchScope = project.projectScope() or useScope
val findReferences = renamer.findReferences(this, searchScope, false)
val usageInfos =
findReferences.mapNotNull { reference ->
val element = reference.element
@@ -1,19 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.nj2k.postProcessing.processings
import com.intellij.psi.PsiReference
import com.intellij.refactoring.rename.RenamePsiElementProcessor
import org.jetbrains.kotlin.idea.search.or
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.psi.KtParameter
// FIX ME WHEN BUNCH 191 REMOVED
internal fun KtParameter.findReferences(renamer: RenamePsiElementProcessor): MutableCollection<PsiReference> {
val searchScope = this.project.projectScope() or this.useScope
return renamer.findReferences(this, searchScope, false)
}