Move: Nested classes support

#KT-9027 In Progress
This commit is contained in:
Alexey Sedunov
2016-01-12 20:41:02 +03:00
parent 0304bd1dc1
commit d662b02e95
159 changed files with 2588 additions and 197 deletions
+2 -2
View File
@@ -330,7 +330,7 @@
order="before moveJavaFileOrDir"/>
<refactoring.moveHandler
id="kotlin.moveTopLevelDeclarations"
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsHandler"
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsHandler"
order="before kotlin.moveFilesOrDirectories"/>
<moveFileHandler implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinFileHandler" />
<refactoring.moveDirectoryWithClassesHelper
@@ -1059,7 +1059,7 @@
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention</className>
<className>org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention</className>
<category>Kotlin</category>
</intentionAction>
@@ -45,7 +45,8 @@ import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.refactoring.addTypeArgumentsIfNeeded
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.getQualifiedTypeArgumentList
import org.jetbrains.kotlin.idea.refactoring.move.PackageNameInfo
import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo
import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo
import org.jetbrains.kotlin.idea.refactoring.move.lazilyProcessInternalReferencesToUpdateOnPackageNameChange
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
import org.jetbrains.kotlin.idea.references.mainReference
@@ -167,7 +168,7 @@ class KotlinInlineValHandler : InlineActionHandler() {
val targetPackages = referenceExpressions.mapNotNullTo(LinkedHashSet()) { (it.containingFile as? KtFile)?.packageFqName }
for (targetPackage in targetPackages) {
if (targetPackage == file.packageFqName) continue
val packageNameInfo = PackageNameInfo(file.packageFqName, targetPackage.toUnsafe())
val packageNameInfo = ContainerChangeInfo(ContainerInfo.Package(file.packageFqName), ContainerInfo.Package(targetPackage))
initializer.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory ->
val infos = expr.internalUsageInfos
?: LinkedHashMap<FqName, (KtSimpleNameExpression) -> UsageInfo?>()
@@ -16,47 +16,47 @@
package org.jetbrains.kotlin.idea.refactoring.move
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler
import com.intellij.usageView.UsageInfo
import com.intellij.psi.PsiClass
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.references.matchesTarget
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import java.util.ArrayList
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import java.util.*
class MoveJavaInnerClassKotlinUsagesHandler: MoveInnerClassUsagesHandler {
override fun correctInnerClassUsage(usage: UsageInfo, outerClass: PsiClass) {
val innerCall = usage.element?.parent as? KtCallExpression ?: return
val receiver = (innerCall.parent as? KtQualifiedExpression)?.receiverExpression
val outerClassRef = when (receiver) {
val receiver = (innerCall.parent as? KtQualifiedExpression)?.receiverExpression ?: return
val outerClassRefExpr = when (receiver) {
is KtCallExpression -> receiver.calleeExpression
is KtQualifiedExpression -> receiver.getQualifiedElementSelector()
else -> null
} as? KtSimpleNameExpression
if (outerClassRef?.mainReference?.resolve() != outerClass) return
val outerCall = outerClassRef!!.parent as? KtCallExpression ?: return
val outerClassRef = outerClassRefExpr?.mainReference ?: return
if (!outerClassRef.matchesTarget(outerClass)) return
val psiFactory = KtPsiFactory(usage.project)
val argumentList = innerCall.valueArgumentList
if (argumentList != null) {
val newArguments = ArrayList<String>()
newArguments.add(outerCall.text!!)
newArguments.add(receiver.text!!)
argumentList.arguments.mapTo(newArguments) { it.text!! }
argumentList.replace(psiFactory.createCallArguments(newArguments.joinToString(prefix = "(", postfix = ")")))
}
else {
innerCall.lambdaArguments.firstOrNull()?.let { lambdaArg ->
val anchor = PsiTreeUtil.skipSiblingsBackward(lambdaArg, PsiWhiteSpace::class.java)
innerCall.addAfter(psiFactory.createCallArguments("(${outerCall.text})"), anchor)
innerCall.addAfter(psiFactory.createCallArguments("(${receiver.text})"), anchor)
}
}
}
}
}
@@ -18,14 +18,11 @@ package org.jetbrains.kotlin.idea.refactoring.move.changePackage
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.PackageWrapper
import org.jetbrains.kotlin.idea.codeInsight.shorten.runWithElementsToShortenIsEmptyIgnored
import org.jetbrains.kotlin.idea.refactoring.move.PackageNameInfo
import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo
import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo
import org.jetbrains.kotlin.idea.refactoring.move.getInternalReferencesToUpdateOnPackageNameChange
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.KotlinMoveTarget
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsOptions
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.Mover
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.name.FqName
@@ -39,26 +36,28 @@ class KotlinChangePackageRefactoring(val file: KtFile) {
val packageDirective = file.packageDirective ?: return
val currentFqName = packageDirective.fqName
val declarationProcessor = MoveKotlinTopLevelDeclarationsProcessor(
val declarationProcessor = MoveKotlinDeclarationsProcessor(
project,
MoveKotlinTopLevelDeclarationsOptions(
MoveDeclarationsDescriptor(
elementsToMove = file.declarations.filterIsInstance<KtNamedDeclaration>(),
moveTarget = object: KotlinMoveTarget {
override val packageWrapper = PackageWrapper(file.manager, newFqName.asString())
override val targetContainerFqName = newFqName
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = originalPsi.containingFile
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = originalPsi.containingFile as? KtFile
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null
override fun verify(file: PsiFile) = null
},
delegate = MoveDeclarationsDelegate.TopLevel,
updateInternalReferences = false
),
Mover.Idle // we don't need to move any declarations physically
)
val declarationUsages = declarationProcessor.findUsages().toList()
val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(PackageNameInfo(currentFqName, newFqName.toUnsafe()))
val changeInfo = ContainerChangeInfo(ContainerInfo.Package(currentFqName), ContainerInfo.Package(newFqName))
val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(changeInfo)
project.executeWriteCommand("Change file's package to '${newFqName.asString()}'") {
packageDirective.fqName = newFqName
@@ -14,56 +14,50 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.refactoring.PackageWrapper
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.getOrPutNullable
import java.util.HashMap
import java.util.*
interface KotlinMoveTarget {
val packageWrapper: PackageWrapper?
fun getOrCreateTargetPsi(originalPsi: PsiElement): PsiFile?
fun getTargetPsiIfExists(originalPsi: PsiElement): PsiFile?
val targetContainerFqName: FqName?
fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement?
fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement?
// Check possible errors and return corresponding message, or null if no errors are detected
fun verify(file: PsiFile): String?
}
object EmptyKotlinMoveTarget: KotlinMoveTarget {
override val packageWrapper: PackageWrapper? get() = null
override val targetContainerFqName = null
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = null
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null
override fun verify(file: PsiFile) = null
}
class KotlinMoveTargetForExistingFile(val targetFile: KtFile): KotlinMoveTarget {
override val packageWrapper: PackageWrapper? = targetFile.packageFqName.asString().let { packageName ->
PackageWrapper(PsiManager.getInstance(targetFile.project), packageName)
}
class KotlinMoveTargetForExistingElement(val targetElement: KtElement): KotlinMoveTarget {
override val targetContainerFqName = targetElement.getContainingKtFile().packageFqName
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetFile
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetElement
override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetFile
override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetElement
// No additional verification is needed
override fun verify(file: PsiFile): String? = null
}
class KotlinMoveTargetForDeferredFile(
project: Project,
private val packageFqName: FqName,
private val createFile: (KtFile) -> KtFile?): KotlinMoveTarget {
override val targetContainerFqName: FqName,
private val createFile: (KtFile) -> KtFile?
): KotlinMoveTarget {
private val createdFiles = HashMap<KtFile, KtFile?>()
override val packageWrapper: PackageWrapper = PackageWrapper(PsiManager.getInstance(project), packageFqName.asString())
override fun getOrCreateTargetPsi(originalPsi: PsiElement): PsiFile? {
override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement? {
val originalFile = originalPsi.containingFile as? KtFile ?: return null
return createdFiles.getOrPutNullable(originalFile) { createFile(originalFile) }
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.codeInsight.navigation.NavigationUtil
@@ -27,7 +27,7 @@ import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
import org.jetbrains.kotlin.idea.quickfix.moveCaret
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -86,12 +86,13 @@ class MoveDeclarationToSeparateFileIntention :
}
return
}
val moveTarget = KotlinMoveTargetForDeferredFile(project, packageName) {
val moveTarget = KotlinMoveTargetForDeferredFile(packageName) {
createKotlinFile(targetFileName, directory, packageName.asString())
}
val moveOptions = MoveKotlinTopLevelDeclarationsOptions(
val moveOptions = MoveDeclarationsDescriptor(
elementsToMove = listOf(element),
moveTarget = moveTarget,
delegate = MoveDeclarationsDelegate.TopLevel,
searchInCommentsAndStrings = false,
searchInNonCode = false,
updateInternalReferences = true,
@@ -102,6 +103,6 @@ class MoveDeclarationToSeparateFileIntention :
FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset)
}
)
MoveKotlinTopLevelDeclarationsProcessor(project, moveOptions).run()
MoveKotlinDeclarationsProcessor(project, moveOptions).run()
}
}
@@ -0,0 +1,180 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
sealed class MoveDeclarationsDelegate {
abstract fun getOriginalContainerFqName(descriptor: MoveDeclarationsDescriptor, originalFile: KtFile): FqName
abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo
abstract fun findUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo>
abstract fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>)
abstract fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration)
abstract fun preprocessUsages(project: Project, usages: List<UsageInfo>)
object TopLevel : MoveDeclarationsDelegate() {
override fun getOriginalContainerFqName(descriptor: MoveDeclarationsDescriptor, originalFile: KtFile) = originalFile.packageFqName
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
return ContainerChangeInfo(ContainerInfo.Package(originalDeclaration.getContainingKtFile().packageFqName),
ContainerInfo.Package(moveTarget.targetContainerFqName!!))
}
override fun findUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
override fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>) {
}
override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
}
override fun preprocessUsages(project: Project, usages: List<UsageInfo>) {
}
}
class NestedClass(
val newClassName: String? = null,
val outerInstanceParameterName: String? = null
) : MoveDeclarationsDelegate() {
override fun getOriginalContainerFqName(descriptor: MoveDeclarationsDescriptor, originalFile: KtFile): FqName {
return descriptor.elementsToMove.first().containingClassOrObject!!.fqName!!
}
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!)
val movingToClass = (moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement is KtClassOrObject
val newInfo = if (movingToClass) {
ContainerInfo.Class(moveTarget.targetContainerFqName!!)
} else {
ContainerInfo.Package(moveTarget.targetContainerFqName!!)
}
return ContainerChangeInfo(originalInfo, newInfo)
}
override fun findUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> {
val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass ?: return emptyList()
return collectOuterInstanceReferences(classToMove)
}
override fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>) {
val usageIterator = usages.iterator()
while (usageIterator.hasNext()) {
val usage = usageIterator.next();
val element = usage.element ?: continue
if (usage is ImplicitCompanionAsDispatchReceiverUsageInfo) {
conflicts.putValue(element, "Implicit companion object will be inaccessible: ${element.text}")
usageIterator.remove()
continue
}
if (usage !is OuterInstanceReferenceUsageInfo) continue
if (usage.isIndirectOuter) {
conflicts.putValue(element, "Indirect outer instances won't be extracted: ${element.text}")
usageIterator.remove()
}
if (usage !is OuterInstanceReferenceUsageInfo.ImplicitReceiver) continue
val fullCall = usage.callElement?.let { it.getQualifiedExpressionForSelector() ?: it } ?: continue
when {
fullCall is KtQualifiedExpression -> {
conflicts.putValue(
fullCall,
"Qualified call won't be processed: ${fullCall.text}"
)
usageIterator.remove()
}
usage.isDoubleReceiver -> {
conflicts.putValue(
fullCall,
"Call with two implicit receivers won't be processed: ${fullCall.text}"
)
usageIterator.remove()
}
}
}
}
override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
with(originalDeclaration) {
newClassName?.let { setName(it) }
if (this is KtClass) {
if ((descriptor.moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement !is KtClassOrObject) {
if (hasModifier(KtTokens.INNER_KEYWORD)) removeModifier(KtTokens.INNER_KEYWORD)
if (hasModifier(KtTokens.PROTECTED_KEYWORD)) removeModifier(KtTokens.PROTECTED_KEYWORD)
}
if (outerInstanceParameterName != null) {
val type = (containingClassOrObject!!.resolveToDescriptor() as ClassDescriptor).defaultType
val parameter = KtPsiFactory(project)
.createParameter("private val $outerInstanceParameterName: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(type)}")
createPrimaryConstructorParameterListIfAbsent().addParameter(parameter)
}
}
}
}
override fun preprocessUsages(project: Project, usages: List<UsageInfo>) {
if (outerInstanceParameterName == null) return
val psiFactory = KtPsiFactory(project)
val newOuterInstanceRef = psiFactory.createExpression(outerInstanceParameterName)
for (usage in usages) {
val referencedNestedClass = (usage as? MoveRenameUsageInfo)?.referencedElement?.unwrapped as? KtClassOrObject
val outerClass = referencedNestedClass?.containingClassOrObject
val lightOuterClass = outerClass?.toLightClass()
if (lightOuterClass != null) {
MoveInnerClassUsagesHandler.EP_NAME
.forLanguage(usage.element!!.language)
?.correctInnerClassUsage(usage, lightOuterClass)
}
when (usage) {
is OuterInstanceReferenceUsageInfo.ExplicitThis -> {
usage.expression?.replace(newOuterInstanceRef)
}
is OuterInstanceReferenceUsageInfo.ImplicitReceiver -> {
usage.callElement?.let { it.replace(psiFactory.createExpressionByPattern("$0.$1", outerInstanceParameterName, it)) }
}
}
}
}
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.LangDataKeys
@@ -32,11 +32,11 @@ import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackages
import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.psi.*
import java.util.*
class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() {
class MoveKotlinDeclarationsHandler : MoveHandlerDelegate() {
private fun getSourceDirectories(elements: Array<out PsiElement>) = elements.mapTo(LinkedHashSet()) { it.containingFile?.parent }
private fun doMoveWithCheck(
@@ -14,8 +14,9 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.ide.util.EditorHelper
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil
@@ -25,16 +26,17 @@ import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesUtil
import com.intellij.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.refactoring.util.TextOccurrencesUtil
import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageViewBundle
import com.intellij.usageView.UsageViewDescriptor
import com.intellij.usageView.UsageViewUtil
import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList
import com.intellij.util.VisibilityUtil
import com.intellij.util.containers.MultiMap
import gnu.trove.THashMap
@@ -54,9 +56,8 @@ import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.isInsideOf
import org.jetbrains.kotlin.psi.psiUtil.isPrivate
@@ -64,58 +65,64 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.utils.keysToMap
import java.util.*
interface Mover: (KtNamedDeclaration, KtFile) -> KtNamedDeclaration {
object Default: Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetFile: KtFile): KtNamedDeclaration {
val newElement = targetFile.add(originalElement) as KtNamedDeclaration
originalElement.deleteSingle()
return newElement
interface Mover: (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
object Default : Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration {
return when (targetContainer) {
is KtFile -> targetContainer.add(originalElement) as KtNamedDeclaration
is KtClassOrObject -> targetContainer.addDeclaration(originalElement) as KtNamedDeclaration
else -> error("Unexpected element: ${targetContainer.getElementTextWithContext()}")
}.apply { originalElement.deleteSingle() }
}
}
object Idle: Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetFile: KtFile) = originalElement
object Idle : Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement) = originalElement
}
}
class MoveKotlinTopLevelDeclarationsOptions(
class MoveDeclarationsDescriptor(
val elementsToMove: Collection<KtNamedDeclaration>,
val moveTarget: KotlinMoveTarget,
val delegate: MoveDeclarationsDelegate,
val searchInCommentsAndStrings: Boolean = true,
val searchInNonCode: Boolean = true,
val updateInternalReferences: Boolean = true,
val deleteSourceFiles: Boolean = false,
val moveCallback: MoveCallback? = null
val moveCallback: MoveCallback? = null,
val openInEditor: Boolean = false
)
class MoveKotlinTopLevelDeclarationsProcessor(
class MoveKotlinDeclarationsProcessor(
val project: Project,
val options: MoveKotlinTopLevelDeclarationsOptions,
val descriptor: MoveDeclarationsDescriptor,
val mover: Mover = Mover.Default) : BaseRefactoringProcessor(project) {
companion object {
private val REFACTORING_NAME: String = KotlinRefactoringBundle.message("refactoring.move.top.level.declarations")
}
private var nonCodeUsages: Array<NonCodeUsageInfo>? = null
private val elementsToMove = options.elementsToMove.filter { e -> e.containingFile != options.moveTarget.getTargetPsiIfExists(e) }
private val elementsToMove = descriptor.elementsToMove.filter { e -> e.parent != descriptor.moveTarget.getTargetPsiIfExists(e) }
private val kotlinToLightElementsBySourceFile = elementsToMove
.groupBy { it.getContainingKtFile() }
.mapValues { it.value.keysToMap { it.toLightElements() } }
private val conflicts = MultiMap<PsiElement, String>()
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor {
return MoveMultipleElementsViewDescriptor(
elementsToMove.toTypedArray(),
MoveClassesOrPackagesUtil.getPackageName(options.moveTarget.packageWrapper)
)
val targetContainerFqName = descriptor.moveTarget.targetContainerFqName?.let {
if (it.isRoot) UsageViewBundle.message("default.package.presentable.name") else it.asString()
}
return MoveMultipleElementsViewDescriptor(elementsToMove.toTypedArray(), targetContainerFqName)
}
private val usagesToProcessBeforeMove = SmartList<UsageInfo>()
public override fun findUsages(): Array<UsageInfo> {
val newPackageName = options.moveTarget.packageWrapper?.qualifiedName ?: ""
val newContainerName = descriptor.moveTarget.targetContainerFqName?.asString() ?: ""
fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableList<UsageInfo>) {
kotlinToLightElements.values.flatMap { it }.flatMapTo(result) { lightElement ->
val newFqName = StringUtil.getQualifiedName(newPackageName, lightElement.name)
val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name)
val foundReferences = HashSet<PsiReference>()
val projectScope = lightElement.project.projectScope()
@@ -133,8 +140,8 @@ class MoveKotlinTopLevelDeclarationsProcessor(
TextOccurrencesUtil.findNonCodeUsages(
lightElement,
name,
options.searchInCommentsAndStrings,
options.searchInNonCode,
descriptor.searchInCommentsAndStrings,
descriptor.searchInNonCode,
newFqName,
results
)
@@ -169,8 +176,9 @@ class MoveKotlinTopLevelDeclarationsProcessor(
val container = element.getUsageContext()
if (!declarationToContainers.getOrPut(declaration) { HashSet<PsiElement>() }.add(container)) continue
// todo: ok for now, will be replaced by proper visibility analysis
val currentPackage = element.containingFile?.containingDirectory?.getPackage()
if (currentPackage?.qualifiedName == newPackageName) continue
if (currentPackage?.qualifiedName == newContainerName) continue
conflicts.putValue(
declaration,
@@ -184,7 +192,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
}
fun collectConflictsInDeclarations() {
if (newPackageName == UNKNOWN_PACKAGE_FQ_NAME.asString()) return
if (newContainerName == UNKNOWN_PACKAGE_FQ_NAME.asString()) return
val declarationToReferenceTargets = HashMap<KtNamedDeclaration, MutableSet<PsiElement>>()
for (declaration in elementsToMove) {
@@ -207,8 +215,9 @@ class MoveKotlinTopLevelDeclarationsProcessor(
if (!declarationToReferenceTargets.getOrPut(declaration) { HashSet<PsiElement>() }.add(refTarget)) continue
// todo: ok for now, will be replaced by proper visibility analysis
val currentPackage = declaration.containingFile?.containingDirectory?.getPackage()
if (currentPackage?.qualifiedName == newPackageName) continue
if (currentPackage?.qualifiedName == newContainerName) continue
conflicts.putValue(
declaration,
@@ -224,14 +233,29 @@ class MoveKotlinTopLevelDeclarationsProcessor(
val usages = ArrayList<UsageInfo>()
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
// No need to find and process usages if package is not changed
if (sourceFile.packageFqName.asString() == newPackageName) return UsageInfo.EMPTY_ARRAY
kotlinToLightElements.keys.forEach {
if (descriptor.updateInternalReferences) {
val packageNameInfo = descriptor.delegate.getContainerChangeInfo(it, descriptor.moveTarget)
val (usagesToProcessLater, usagesToProcessEarly) = it
.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
.partition { it is MoveRenameUsageInfoForExtension }
usages.addAll(usagesToProcessLater)
usagesToProcessBeforeMove.addAll(usagesToProcessEarly)
}
}
// No need to find and process usages if package is not changed
val originalContainerName = descriptor.delegate.getOriginalContainerFqName(descriptor, sourceFile).asString()
if (originalContainerName == newContainerName) return UsageInfo.EMPTY_ARRAY
usages += descriptor.delegate.findUsages(descriptor)
collectUsages(kotlinToLightElements, usages)
collectConflictsInUsages(usages)
collectConflictsInDeclarations()
descriptor.delegate.collectConflicts(usages, conflicts)
}
descriptor.delegate.collectConflicts(usagesToProcessBeforeMove, conflicts)
return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray())
}
@@ -241,27 +265,15 @@ class MoveKotlinTopLevelDeclarationsProcessor(
}
override fun performRefactoring(usages: Array<out UsageInfo>) {
fun moveDeclaration(
declaration: KtNamedDeclaration,
moveTarget: KotlinMoveTarget,
usagesToProcessAfterMove: MutableList<UsageInfo>
): KtNamedDeclaration? {
fun moveDeclaration(declaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): KtNamedDeclaration? {
val file = declaration.containingFile as? KtFile
assert(file != null) { "${declaration.javaClass}: ${declaration.text}" }
val targetFile = moveTarget.getOrCreateTargetPsi(declaration) as? KtFile
?: throw AssertionError("Couldn't create Kotlin file for: ${declaration.javaClass}: ${declaration.text}")
val targetContainer = moveTarget.getOrCreateTargetPsi(declaration)
?: throw AssertionError("Couldn't create Kotlin file for: ${declaration.javaClass}: ${declaration.text}")
if (options.updateInternalReferences) {
val packageNameInfo = PackageNameInfo(file!!.packageFqName, targetFile.packageFqName.toUnsafe())
val (usagesToProcessLater, usagesToProcessNow) = declaration
.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
.partition { it is MoveRenameUsageInfoForExtension }
postProcessMoveUsages(usagesToProcessNow, shorteningMode = ShorteningMode.NO_SHORTENING)
usagesToProcessAfterMove.addAll(usagesToProcessLater)
}
val newElement = mover(declaration, targetFile)
descriptor.delegate.preprocessDeclaration(descriptor, declaration)
val newElement = mover(declaration, targetContainer)
newElement.addToShorteningWaitSet()
@@ -271,6 +283,10 @@ class MoveKotlinTopLevelDeclarationsProcessor(
try {
val usageList = usages.toArrayList()
descriptor.delegate.preprocessUsages(project, usageList)
postProcessMoveUsages(usagesToProcessBeforeMove, shorteningMode = ShorteningMode.NO_SHORTENING)
val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>(
object: TObjectHashingStrategy<PsiElement> {
override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean {
@@ -293,7 +309,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
)
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
for ((oldDeclaration, oldLightElements) in kotlinToLightElements) {
val newDeclaration = moveDeclaration(oldDeclaration, options.moveTarget, usageList)
val newDeclaration = moveDeclaration(oldDeclaration, descriptor.moveTarget)
if (newDeclaration == null) {
for (oldElement in oldLightElements) {
oldToNewElementsMapping[oldElement] = oldElement
@@ -307,9 +323,13 @@ class MoveKotlinTopLevelDeclarationsProcessor(
for ((oldElement, newElement) in oldLightElements.asSequence().zip(newDeclaration.toLightElements().asSequence())) {
oldToNewElementsMapping[oldElement] = newElement
}
if (descriptor.openInEditor) {
EditorHelper.openInEditor(newDeclaration)
}
}
if (options.deleteSourceFiles) {
if (descriptor.deleteSourceFiles) {
sourceFile.delete()
}
}
@@ -324,7 +344,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
override fun performPsiSpoilingRefactoring() {
nonCodeUsages?.let { nonCodeUsages -> RenameUtil.renameNonCodeUsages(myProject, nonCodeUsages) }
options.moveCallback?.refactoringCompleted()
descriptor.moveCallback?.refactoringCompleted()
}
fun execute(usages: List<UsageInfo>) {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui
import com.intellij.ide.util.DirectoryUtil
import com.intellij.ide.util.PropertiesComponent
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog">
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog">
<grid id="1a398" binding="mainPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<xy x="10" y="10" width="472" height="414"/>
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui;
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentAdapter;
@@ -69,7 +69,7 @@ import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable;
import org.jetbrains.kotlin.idea.refactoring.move.MoveUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.*;
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*;
import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile;
@@ -533,7 +533,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
}
return new KotlinMoveTargetForDeferredFile(
myProject,
new FqName(getTargetPackage()),
new Function1<KtFile, KtFile>() {
@Override
@@ -556,7 +555,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return null;
}
return new KotlinMoveTargetForExistingFile(jetFile);
return new KotlinMoveTargetForExistingElement(jetFile);
}
File targetDir = targetFile.getParentFile();
@@ -570,7 +569,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
}
return new KotlinMoveTargetForDeferredFile(
myProject,
new FqName(psiPackage.getQualifiedName()),
new Function1<KtFile, KtFile>() {
@Override
@@ -757,10 +755,18 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
deleteSourceFile = ret == Messages.YES;
}
MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions(
elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), true, deleteSourceFile, moveCallback
MoveDeclarationsDescriptor options = new MoveDeclarationsDescriptor(
elementsToMove,
target,
MoveDeclarationsDelegate.TopLevel.INSTANCE,
isSearchInComments(),
isSearchInNonJavaFiles(),
true,
deleteSourceFile,
moveCallback,
false
);
invokeRefactoring(new MoveKotlinTopLevelDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE));
invokeRefactoring(new MoveKotlinDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE));
}
catch (IncorrectOperationException e) {
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null, myProject);
@@ -27,7 +27,7 @@ import com.intellij.usageView.UsageInfo
import com.intellij.util.Function
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import java.util.ArrayList
@@ -37,13 +37,13 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
private class FileUsagesWrapper(
val psiFile: PsiFile,
val usages: List<UsageInfo>,
val moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor?
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
) : UsageInfo(psiFile)
private class MoveContext(
val newParent: PsiDirectory,
val internalUsages: MoveKotlinFileHandler.InternalUsagesWrapper,
val moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor?
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
)
private val fileHandler = MoveKotlinFileHandler()
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.packageMatchesDirectory
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedDeclaration
@@ -42,48 +42,50 @@ class MoveKotlinFileHandler : MoveFileHandler() {
// so that it can be kept in the transition map
private class MoveContext(
psiManager: PsiManager,
val declarationMoveProcessor: MoveKotlinTopLevelDeclarationsProcessor
val declarationMoveProcessor: MoveKotlinDeclarationsProcessor
): LightElement(psiManager, KotlinLanguage.INSTANCE) {
override fun toString() = ""
}
private fun KtFile.getPackageNameInfo(newParent: PsiDirectory?, clearUserData: Boolean): PackageNameInfo? {
private fun KtFile.getPackageNameInfo(newParent: PsiDirectory?, clearUserData: Boolean): ContainerChangeInfo? {
val shouldUpdatePackageDirective = updatePackageDirective ?: packageMatchesDirectory()
updatePackageDirective = if (clearUserData) null else shouldUpdatePackageDirective
if (!shouldUpdatePackageDirective) return null
val oldPackageName = packageFqName
val newPackage = newParent?.getPackage() ?: return PackageNameInfo(oldPackageName, UNKNOWN_PACKAGE_FQ_NAME)
val newPackage = newParent?.getPackage() ?: return ContainerChangeInfo(ContainerInfo.Package(oldPackageName),
ContainerInfo.UnknownPackage)
val newPackageName = FqNameUnsafe(newPackage.qualifiedName)
if (oldPackageName.asString() == newPackageName.asString()) return null
if (newPackageName != UNKNOWN_PACKAGE_FQ_NAME && !newPackageName.hasIdentifiersOnly()) return null
if (!newPackageName.hasIdentifiersOnly()) return null
return PackageNameInfo(oldPackageName, newPackageName)
return ContainerChangeInfo(ContainerInfo.Package(oldPackageName), ContainerInfo.Package(newPackageName.toSafe()))
}
fun initMoveProcessor(psiFile: PsiFile, newParent: PsiDirectory?): MoveKotlinTopLevelDeclarationsProcessor? {
fun initMoveProcessor(psiFile: PsiFile, newParent: PsiDirectory?): MoveKotlinDeclarationsProcessor? {
if (psiFile !is KtFile) return null
val packageNameInfo = psiFile.getPackageNameInfo(newParent, false) ?: return null
val project = psiFile.project
val newPackageName = packageNameInfo.newPackageName
val moveTarget = when (newPackageName) {
UNKNOWN_PACKAGE_FQ_NAME -> EmptyKotlinMoveTarget
val newPackage = packageNameInfo.newContainer
val moveTarget = when (newPackage) {
ContainerInfo.UnknownPackage -> EmptyKotlinMoveTarget
else -> KotlinMoveTargetForDeferredFile(project, newPackageName.toSafe()) {
else -> KotlinMoveTargetForDeferredFile(newPackage.fqName!!) {
MoveFilesOrDirectoriesUtil.doMoveFile(psiFile, newParent)
newParent?.findFile(psiFile.name) as? KtFile
}
}
val declarationMoveProcessor = MoveKotlinTopLevelDeclarationsProcessor(
val declarationMoveProcessor = MoveKotlinDeclarationsProcessor(
project,
MoveKotlinTopLevelDeclarationsOptions(
MoveDeclarationsDescriptor(
elementsToMove = psiFile.declarations.filterIsInstance<KtNamedDeclaration>(),
moveTarget = moveTarget,
delegate = MoveDeclarationsDelegate.TopLevel,
updateInternalReferences = false
),
Mover.Idle
@@ -126,9 +128,7 @@ class MoveKotlinFileHandler : MoveFileHandler() {
if (file !is KtFile) return
val newDirectory = file.parent ?: return
val packageNameInfo = file.getPackageNameInfo(newDirectory, true) ?: return
val newPackageName = packageNameInfo.newPackageName
assert(newPackageName.isSafe) { newPackageName }
file.packageDirective?.fqName = newPackageName.toSafe()
file.packageDirective?.fqName = packageNameInfo.newContainer.fqName!!
}
override fun retargetUsages(usageInfos: List<UsageInfo>?, oldToNewMap: Map<PsiElement, PsiElement>) {
@@ -136,7 +136,7 @@ class MoveKotlinFileHandler : MoveFileHandler() {
retargetUsages(usageInfos, moveContext.declarationMoveProcessor)
}
fun retargetUsages(usageInfos: List<UsageInfo>?, moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor) {
fun retargetUsages(usageInfos: List<UsageInfo>?, moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor) {
postProcessMoveUsages(usageInfos?.firstIsInstanceOrNull<InternalUsagesWrapper>()?.usages ?: emptyList())
moveDeclarationsProcessor.project.runWithElementsToShortenIsEmptyIgnored {
usageInfos?.let { moveDeclarationsProcessor.execute(it) }
@@ -22,6 +22,7 @@ import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.RefactoringSettings
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
@@ -36,17 +37,20 @@ import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.usageView.UsageInfo
import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.codeInsight.KotlinFileReferencesResolver
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.refactoring.fqName.isImported
import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode
@@ -60,22 +64,48 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
val UNKNOWN_PACKAGE_FQ_NAME = FqNameUnsafe("org.jetbrains.kotlin.idea.refactoring.move.<unknown-package>")
class PackageNameInfo(val oldPackageName: FqName, val newPackageName: FqNameUnsafe)
sealed class ContainerInfo() {
abstract val fqName: FqName?
abstract fun matches(descriptor: DeclarationDescriptor): Boolean
fun KtElement.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo: PackageNameInfo): List<UsageInfo> {
object UnknownPackage : ContainerInfo() {
override val fqName = null
override fun matches(descriptor: DeclarationDescriptor) = descriptor is PackageViewDescriptor
}
class Package(override val fqName: FqName): ContainerInfo() {
override fun matches(descriptor: DeclarationDescriptor): Boolean {
return descriptor is PackageFragmentDescriptor && descriptor.fqName == fqName
}
}
class Class(override val fqName: FqName) : ContainerInfo() {
override fun matches(descriptor: DeclarationDescriptor): Boolean {
return descriptor is ClassDescriptor && descriptor.importableFqName == fqName
}
}
}
data class ContainerChangeInfo(val oldContainer: ContainerInfo, val newContainer: ContainerInfo)
fun KtElement.getInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo: ContainerChangeInfo): List<UsageInfo> {
val usages = ArrayList<UsageInfo>()
lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) }
lazilyProcessInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) }
return usages
}
fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
packageNameInfo: PackageNameInfo,
containerChangeInfo: ContainerChangeInfo,
body: (originalRefExpr: KtSimpleNameExpression, usageFactory: (KtSimpleNameExpression) -> UsageInfo?) -> Unit
) {
val file = containingFile as? KtFile ?: return
@@ -99,46 +129,61 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null
// Special case for enum entry superclass references (they have empty text and don't need to be processed by the refactoring)
if (refExpr.textRange.isEmpty) {
return null
}
if (refExpr.textRange.isEmpty) return null
if (descriptor is ClassDescriptor && descriptor.isInner && refExpr.parent is KtCallExpression) return null
val isCallable = descriptor is CallableDescriptor
val isExtension = isCallable && declaration.isExtensionDeclaration()
if (isCallable && !isExtension) {
if (isCallable) {
val containingDescriptor = descriptor.containingDeclaration
if (refExpr.getReceiverExpression() != null) {
return fun(refExpr: KtSimpleNameExpression): UsageInfo? {
val receiver = refExpr.getReceiverExpression() ?: return null
val receiverRef = receiver.getQualifiedElementSelector() as? KtSimpleNameExpression ?: return null
if (bindingContext[BindingContext.QUALIFIER, receiverRef] == null) return null
return processReference(receiverRef, bindingContext)?.invoke(receiverRef)
if (isExtension && containingDescriptor is ClassDescriptor) {
val implicitClass = (refExpr.getResolvedCall(bindingContext)?.dispatchReceiver as? ImplicitClassReceiver)?.classDescriptor
if (DescriptorUtils.isCompanionObject(implicitClass)) {
return { ImplicitCompanionAsDispatchReceiverUsageInfo(it) }
}
return null
}
if (!isExtension) {
if (refExpr.getReceiverExpression() != null) {
return fun(refExpr: KtSimpleNameExpression): UsageInfo? {
val receiver = refExpr.getReceiverExpression() ?: return null
val receiverRef = receiver.getQualifiedElementSelector() as? KtSimpleNameExpression ?: return null
if (bindingContext[BindingContext.QUALIFIER, receiverRef] == null) return null
return processReference(receiverRef, bindingContext)?.invoke(receiverRef)
}
}
if (!(containingDescriptor is PackageFragmentDescriptor
|| containingDescriptor is ClassDescriptor && containingDescriptor.kind == ClassKind.OBJECT)) return null
}
if (containingDescriptor !is PackageFragmentDescriptor) return null
}
val fqName = DescriptorUtils.getFqName(descriptor)
if (!fqName.isSafe) return null
val packageName = DescriptorUtils.getParentOfType(descriptor, PackageFragmentDescriptor::class.java, false)?.let {
DescriptorUtils.getFqName(it).toSafe()
}
val (oldContainer, newContainer) = containerChangeInfo
val oldPackageName = packageNameInfo.oldPackageName
val newPackageName = packageNameInfo.newPackageName
val containerFqName = descriptor
.parents
.mapNotNull {
when {
oldContainer.matches(it) -> oldContainer.fqName
newContainer.matches(it) -> newContainer.fqName
else -> null
}
}
.firstOrNull()
fun doCreateUsageInfo(refExpr: KtSimpleNameExpression): UsageInfo? {
if (isAncestor(declaration, false)) {
if (descriptor.importableFqName == null) return null
if (descriptor is ClassDescriptor && descriptor.isInner && refExpr.parent is KtCallExpression) return null
if (isUnqualifiedExtensionReference(refExpr.mainReference, declaration)) return null
if (packageName == null || !newPackageName.isSafe) return null
if (containerFqName == null || newContainer is ContainerInfo.UnknownPackage) return null
return fqName.asString().let {
val prefix = packageName.asString()
val prefix = containerFqName.asString()
val prefixOffset = it.indexOf(prefix)
val newFqName = FqName(it.replaceRange(prefixOffset..prefixOffset + prefix.length - 1, newPackageName.asString()))
val newFqName = FqName(it.replaceRange(prefixOffset..prefixOffset + prefix.length - 1, newContainer.fqName!!.asString()))
MoveRenameSelfUsageInfo(refExpr.mainReference, declaration, newFqName)
}
}
@@ -146,11 +191,8 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
return createMoveUsageInfoIfPossible(refExpr.mainReference, declaration, false)
}
if (!isExtension &&
packageName != oldPackageName &&
packageName?.asString() != newPackageName.asString() &&
!isImported(descriptor)) return null
return ::doCreateUsageInfo
if (isExtension || containerFqName != null || isImported(descriptor)) return ::doCreateUsageInfo
return null
}
val referenceToContext = KotlinFileReferencesResolver.resolve(file = file, elements = listOf(this))
@@ -163,6 +205,8 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
}
}
class ImplicitCompanionAsDispatchReceiverUsageInfo(callee: KtSimpleNameExpression) : UsageInfo(callee)
class MoveRenameUsageInfoForExtension(
element: PsiElement,
reference: PsiReference,
@@ -276,7 +320,7 @@ fun postProcessMoveUsages(usages: List<UsageInfo>,
val nonCodeUsages = ArrayList<NonCodeUsageInfo>()
for (usage in sortedUsages) {
usageLoop@ for (usage in sortedUsages) {
when (usage) {
is NonCodeUsageInfo -> {
nonCodeUsages.add(usage)
@@ -295,21 +339,17 @@ fun postProcessMoveUsages(usages: List<UsageInfo>,
is MoveRenameUsageInfo -> {
val oldElement = usage.referencedElement!!
val newElement = counterpart(oldElement)
usage.reference?.let {
try {
if (it is KtSimpleNameReference) {
it.bindToElement(newElement, shorteningMode)
}
else if (it is PsiReferenceExpression && updateJavaReference(it, oldElement, newElement)) {
}
else {
it.bindToElement(newElement)
}
}
catch (e: IncorrectOperationException) {
// Suppress exception if bindToElement is not implemented
val reference = usage.reference ?: (usage.element as? KtSimpleNameExpression)?.mainReference
try {
when {
reference is KtSimpleNameReference -> reference.bindToElement(newElement, shorteningMode)
reference is PsiReferenceExpression && updateJavaReference(reference, oldElement, newElement) -> continue@usageLoop
else -> reference?.bindToElement(newElement)
}
}
catch (e: IncorrectOperationException) {
// Suppress exception if bindToElement is not implemented
}
}
}
}
@@ -388,3 +428,86 @@ fun moveFilesOrDirectories(
show()
}
}
sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, val isIndirectOuter: Boolean) : UsageInfo(element) {
class ExplicitThis(
expression: KtThisExpression,
isIndirectOuter: Boolean
) : OuterInstanceReferenceUsageInfo(expression, isIndirectOuter) {
val expression: KtThisExpression?
get() = element as? KtThisExpression
}
class ImplicitReceiver(
callElement: KtElement,
isIndirectOuter: Boolean,
val isDoubleReceiver: Boolean
) : OuterInstanceReferenceUsageInfo(callElement, isIndirectOuter) {
val callElement: KtElement?
get() = element as? KtElement
}
}
@JvmOverloads
fun traverseOuterInstanceReferences(innerClass: KtClass, stopAtFirst: Boolean, body: (OuterInstanceReferenceUsageInfo) -> Unit = {}): Boolean {
if (!innerClass.isInner()) return false
val context = innerClass.analyzeFully()
val innerClassDescriptor = innerClass.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false
val outerClassDescriptor = innerClassDescriptor.containingDeclaration as? ClassDescriptor ?: return false
var found = false
innerClass.accept(
object : PsiRecursiveElementWalkingVisitor() {
private fun getOuterInstanceReference(element: PsiElement): OuterInstanceReferenceUsageInfo? {
return when (element) {
is KtThisExpression -> {
val descriptor = context[BindingContext.REFERENCE_TARGET, element.instanceReference]
val isIndirect = when {
descriptor == outerClassDescriptor -> false
DescriptorUtils.isAncestor(descriptor, outerClassDescriptor, true) -> true
else -> return null
}
OuterInstanceReferenceUsageInfo.ExplicitThis(element, isIndirect)
}
is KtSimpleNameExpression -> {
val resolvedCall = element.getResolvedCall(context) ?: return null
val dispatchReceiver = resolvedCall.dispatchReceiver as? ImplicitReceiver
val extensionReceiver = resolvedCall.extensionReceiver as? ImplicitReceiver
var isIndirect = false
val isDoubleReceiver = when {
dispatchReceiver?.declarationDescriptor == outerClassDescriptor -> extensionReceiver != null
extensionReceiver?.declarationDescriptor == outerClassDescriptor -> dispatchReceiver != null
else -> {
isIndirect = true
when {
DescriptorUtils.isAncestor(dispatchReceiver?.declarationDescriptor, outerClassDescriptor, true) ->
extensionReceiver != null
DescriptorUtils.isAncestor(extensionReceiver?.declarationDescriptor, outerClassDescriptor, true) ->
dispatchReceiver != null
else -> return null
}
}
}
OuterInstanceReferenceUsageInfo.ImplicitReceiver(resolvedCall.call.callElement, isIndirect, isDoubleReceiver)
}
else -> null
}
}
override fun visitElement(element: PsiElement) {
getOuterInstanceReference(element)?.let {
body(it)
found = true
if (stopAtFirst) stopWalking()
return
}
super.visitElement(element)
}
}
)
return found
}
fun collectOuterInstanceReferences(innerClass: KtClass): List<OuterInstanceReferenceUsageInfo> {
return SmartList<OuterInstanceReferenceUsageInfo>().apply { traverseOuterInstanceReferences(innerClass, false) { add(it) } }
}
@@ -1,4 +1,4 @@
{
"mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention"
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention"
}
@@ -1,4 +1,4 @@
{
"mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention"
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention"
}
@@ -1,5 +1,5 @@
{
"mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention",
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention",
"isApplicable": "false"
}
@@ -0,0 +1,14 @@
package test
class A {
inner class OuterOuterY
inner class B {
inner class C {
fun test() {
OuterOuterY()
this@A.OuterOuterY()
}
}
}
}
@@ -0,0 +1,14 @@
package test
class A {
inner class OuterOuterY
inner class B {
inner class <caret>C {
fun test() {
OuterOuterY()
this@A.OuterOuterY()
}
}
}
}
@@ -0,0 +1,2 @@
Indirect outer instances won't be extracted: OuterOuterY()
Indirect outer instances won't be extracted: this@A
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "a",
"withRuntime": "true"
}
@@ -0,0 +1,26 @@
package test
class C(private val b: A.B) {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
b.OuterY()
b.outerFoo(b.outerBar)
b.OuterY()
b.outerFoo(b.outerBar)
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,42 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class B {
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.C;
class Test {
C foo() {
return new C(new A().new B());
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.C;
class Test {
C foo() {
return new C(new A().new B());
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.C;
class Test {
C foo() {
return new C(new A().new B());
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.C
fun foo(): C {
return C(A().B())
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.C
fun foo2(): C {
return C(A().B())
}
@@ -0,0 +1,9 @@
package test2
import test.A
import test.A.B
import test.C
fun foo2(): C {
return C(A().B())
}
@@ -0,0 +1,66 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class B {
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
inner class <caret>C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
OuterY()
outerFoo(outerBar)
this@B.OuterY()
this@B.outerFoo(this@B.outerBar)
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B.C foo() {
return new A().new B().new C();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B.C;
class Test {
C foo() {
return new A().new B().new C();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B;
class Test {
B.C foo() {
return new A().new B().new C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B.C {
return A().B().C()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B.C
fun foo2(): C {
return A().B().C()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo2(): B.C {
return A().B().C()
}
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "b",
"withRuntime": "true"
}
@@ -0,0 +1,20 @@
package test
class C {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,36 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class B {
}
}
@@ -0,0 +1,9 @@
package test2;
import test.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.C
fun foo(): C {
return C()
}
@@ -0,0 +1,7 @@
package test2
import test.C
fun foo(): C {
return C()
}
@@ -0,0 +1,8 @@
package test2
import test.A.B
import test.C
fun foo(): C {
return C()
}
@@ -0,0 +1,54 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class B {
class <caret>C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B.C foo() {
return new A.B.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B;
class Test {
B.C foo() {
return new B.C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B.C {
return A.B.C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B.C
fun foo(): C {
return C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B
fun foo(): B.C {
return B.C()
}
@@ -0,0 +1,5 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"withRuntime": "true"
}
@@ -0,0 +1,20 @@
package test
class B {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,34 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo(): B {
return B()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo2(): B {
return B()
}
@@ -0,0 +1,52 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class <caret>B {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B foo() {
return new A().new B();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B {
return A().B()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo2(): B {
return A().B()
}
@@ -0,0 +1,5 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"withRuntime": "true"
}
@@ -0,0 +1,26 @@
package test
class B(private val a: A) {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
a.OuterY()
a.outerFoo(a.outerBar)
a.OuterY()
a.outerFoo(a.outerBar)
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,40 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new B(new A());
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new B(new A());
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo(): B {
return B(A())
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo2(): B {
return B(A())
}
@@ -0,0 +1,64 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class <caret>B {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
OuterY()
outerFoo(outerBar)
this@A.OuterY()
this@A.outerFoo(this@A.outerBar)
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B foo() {
return new A().new B();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B {
return A().B()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo2(): B {
return A().B()
}
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "a",
"withRuntime": "true"
}
@@ -0,0 +1,19 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A() {
fun X.bar() {}
}
class X {
fun Int.foo() {}
inner class Y {
fun test() {
1.foo()
with(1) { foo() }
with(A()) { bar() }
}
}
}
@@ -0,0 +1,19 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A() {
fun X.bar() {}
}
class X {
fun Int.foo() {}
inner class <caret>Y {
fun test() {
1.foo()
with(1) { foo() }
with(A()) { bar() }
}
}
}
@@ -0,0 +1,3 @@
Call with two implicit receivers won't be processed: bar()
Call with two implicit receivers won't be processed: foo()
Qualified call won't be processed: 1.foo()
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "a",
"withRuntime": "true"
}
@@ -0,0 +1,55 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class B {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
}
class C {
fun test() {
B.X()
B.Companion.Y()
B.foo(B.bar)
//1.extFoo(1.extBar) // conflict
B.O.Y()
B.O.foo(B.O.bar)
with (B.O) {
B.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.C foo() {
return new A.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.C foo() {
return new A.C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.C {
return A.C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.C
fun foo(): C {
return C()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo(): A.C {
return A.C()
}
@@ -0,0 +1,54 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class B {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class <caret>C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B.C foo() {
return new A.B.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B;
class Test {
B.C foo() {
return new B.C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B.C {
return A.B.C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B.C
fun foo(): C {
return C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B
fun foo(): B.C {
return B.C()
}
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"targetClass": "test.A",
"withRuntime": "true"
}
@@ -0,0 +1,55 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class B {
}
class C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.C foo() {
return new A.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.C;
class Test {
C foo() {
return new C();
}
}

Some files were not shown because too many files have changed in this diff Show More