diff --git a/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/after.kt.template b/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/after.kt.template new file mode 100644 index 00000000000..8ee0d5bd2e7 --- /dev/null +++ b/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/after.kt.template @@ -0,0 +1,7 @@ +class A { + companion object { + fun foo() = 1 + } +} + +fun bar() = A.foo() \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/before.kt.template b/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/before.kt.template new file mode 100644 index 00000000000..3372ee25001 --- /dev/null +++ b/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/before.kt.template @@ -0,0 +1,5 @@ +class A { + fun foo() = 1 +} + +fun bar() = A().foo() \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/description.html b/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/description.html new file mode 100644 index 00000000000..02dd4b063cf --- /dev/null +++ b/idea/resources/intentionDescriptions/MoveMemberToCompanionObjectIntention/description.html @@ -0,0 +1,5 @@ + + +This intention moves class member to the corresponding companion object. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 9145661dd3a..be7a2599e0b 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1164,6 +1164,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention + Kotlin + + (KtNamedDeclaration::class.java, + "Move to companion object") { + override fun applicabilityRange(element: KtNamedDeclaration): TextRange? { + if (element !is KtNamedFunction && element !is KtProperty && element !is KtClassOrObject) return null + if (element is KtEnumEntry) return null + if (element is KtNamedFunction && element.bodyExpression == null) return null + if (element is KtNamedFunction && element.valueParameterList == null) return null + if ((element is KtNamedFunction || element is KtProperty) && element.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return null + if (element.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return null + val containingClass = element.containingClassOrObject as? KtClass ?: return null + if (containingClass.isLocal() || containingClass.isInner()) return null + return element.nameIdentifier?.textRange + } + + class JavaUsageInfo(refExpression: PsiReferenceExpression) : UsageInfo(refExpression) + + class ImplicitReceiverUsageInfo(refExpression: KtSimpleNameExpression, val callExpression: KtExpression) : UsageInfo(refExpression) + class ExplicitReceiverUsageInfo(refExpression: KtSimpleNameExpression, val receiverExpression: KtExpression) : UsageInfo(refExpression) + + private fun getNameSuggestionsForOuterInstance(element: KtNamedDeclaration): List { + val containingClass = element.containingClassOrObject as KtClass + val containingClassDescriptor = containingClass.resolveToDescriptor() as ClassDescriptorWithResolutionScopes + val companionDescriptor = containingClassDescriptor.companionObjectDescriptor + val companionMemberScope = (companionDescriptor ?: containingClassDescriptor).scopeForMemberDeclarationResolution + val validator = CollectingNameValidator(element.getValueParameters().mapNotNull { it.name }) { + companionMemberScope.getContributedVariables(Name.guess(it), NoLookupLocation.FROM_IDE).isEmpty() + } + return KotlinNameSuggester.suggestNamesByType(containingClassDescriptor.defaultType, validator, "receiver") + } + + private fun runTemplateForInstanceParam( + declaration: KtNamedDeclaration, + nameSuggestions: List, + editor: Editor? + ) { + if (nameSuggestions.isNotEmpty() && editor != null) { + val restoredElement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(declaration) + val restoredParam = restoredElement.getValueParameters().first() + + val paramRefs = ReferencesSearch.search(restoredParam, LocalSearchScope(restoredElement)).toList() + + editor.caretModel.moveToOffset(restoredElement.startOffset) + + val templateBuilder = TemplateBuilderImpl(restoredElement) + templateBuilder.replaceElement(restoredParam.nameIdentifier!!, "ParamName", ChooseStringExpression(nameSuggestions), true) + paramRefs.forEach { templateBuilder.replaceElement(it, "ParamName", "ParamRef", false) } + templateBuilder.run(editor, true) + } + } + + private fun moveReceiverToArgumentList(refElement: PsiElement) { + when (refElement) { + is PsiReferenceExpression -> { + val qualifier = refElement.qualifier + val call = refElement.parent as? PsiMethodCallExpression + if (call != null && qualifier != null) { + val argumentList = call.argumentList + argumentList.addBefore(qualifier, argumentList.expressions.firstOrNull()) + } + } + + is KtSimpleNameExpression -> { + val call = refElement.parent as? KtCallExpression + val receiver = call?.getQualifiedExpressionForSelector()?.receiverExpression + if (call != null && receiver != null) { + val psiFactory = KtPsiFactory(refElement) + val argumentList = call.valueArgumentList + ?: call.addAfter(psiFactory.createCallArguments("()"), call.typeArgumentList ?: refElement) as KtValueArgumentList + argumentList.addArgumentBefore(psiFactory.createArgument(receiver), + argumentList.arguments.firstOrNull()) + } + } + } + } + + private fun doMove(element: KtNamedDeclaration, + externalUsages: SmartList, + outerInstanceUsages: SmartList, + editor: Editor?) { + val project = element.project + val containingClass = element.containingClassOrObject as KtClass + + val javaCodeStyleManager = JavaCodeStyleManager.getInstance(project) + + val companionObject = containingClass.getOrCreateCompanionObject() + val companionLightClass = companionObject.toLightClass()!! + + val ktPsiFactory = KtPsiFactory(project) + val javaPsiFactory = JavaPsiFacade.getInstance(project).elementFactory + val javaCompanionRef = javaPsiFactory.createReferenceExpression(companionLightClass) + val ktCompanionRef = ktPsiFactory.createExpression(companionObject.fqName!!.asString()) + + val elementsToShorten = SmartList() + + val nameSuggestions: List + if (outerInstanceUsages.isNotEmpty() && element is KtNamedFunction) { + val parameterList = element.valueParameterList!! + val parameters = parameterList.parameters + + val newParamType = (containingClass.resolveToDescriptor() as ClassDescriptor).defaultType + + nameSuggestions = getNameSuggestionsForOuterInstance(element) + + val newParam = parameterList.addParameterBefore( + ktPsiFactory.createParameter("${nameSuggestions.first()}: ${IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(newParamType)}"), + parameters.firstOrNull() + ) + + val newOuterInstanceRef = ktPsiFactory.createExpression(newParam.name!!) + for (usage in outerInstanceUsages) { + when (usage) { + is OuterInstanceReferenceUsageInfo.ExplicitThis -> { + usage.expression?.replace(newOuterInstanceRef) + } + + is OuterInstanceReferenceUsageInfo.ImplicitReceiver -> { + usage.callElement?.let { it.replace(ktPsiFactory.createExpressionByPattern("$0.$1", newOuterInstanceRef, it)) } + } + } + } + } + else { + nameSuggestions = emptyList() + } + + element.removeModifier(KtTokens.OPEN_KEYWORD) + element.removeModifier(KtTokens.FINAL_KEYWORD) + + val newDeclaration = Mover.Default(element, companionObject) + + for (usage in externalUsages) { + val usageElement = usage.element ?: continue + + if (nameSuggestions.isNotEmpty()) { + moveReceiverToArgumentList(usageElement) + } + + when (usage) { + is JavaUsageInfo -> { + (usageElement as? PsiReferenceExpression) + ?.qualifierExpression + ?.replace(javaCompanionRef) + ?.let { javaCodeStyleManager.shortenClassReferences(it) } + } + + is ExplicitReceiverUsageInfo -> { + elementsToShorten += usage.receiverExpression.replaced(ktCompanionRef) + } + + is ImplicitReceiverUsageInfo -> { + usage.callExpression + .let { it.replaced(ktPsiFactory.createExpressionByPattern("$0.$1", ktCompanionRef, it)) } + .let { elementsToShorten += (it as KtQualifiedExpression).receiverExpression } + } + } + } + + ShortenReferences.DEFAULT.process(elementsToShorten) + + runTemplateForInstanceParam(newDeclaration, nameSuggestions, editor) + } + + override fun applyTo(element: KtNamedDeclaration, editor: Editor?) { + val project = element.project + + if (element is KtClassOrObject) { + val nameSuggestions = if (traverseOuterInstanceReferences(element, true)) getNameSuggestionsForOuterInstance(element) else emptyList() + val outerInstanceName = nameSuggestions.firstOrNull() + var movedClass: KtClassOrObject? = null + val mover = object: Mover { + override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration { + return Mover.Default(originalElement, targetContainer).apply { movedClass = this as KtClassOrObject } + } + } + val moveDescriptor = MoveDeclarationsDescriptor(listOf(element), + KotlinMoveTargetForCompanion(element.containingClassOrObject!! as KtClass), + MoveDeclarationsDelegate.NestedClass(null, outerInstanceName), + moveCallback = MoveCallback { runTemplateForInstanceParam(movedClass!!, nameSuggestions, editor) }) + MoveKotlinDeclarationsProcessor(project, moveDescriptor, mover).run() + return + } + + if (HierarchySearchRequest(element, element.useScope, false).searchOverriders().any()) { + val description = RefactoringUIUtil.getDescription(element, false).capitalize() + CommonRefactoringUtil.showErrorHint(project, editor, "$description is overridden by declaration(s) in a subclass", text, null) + return + } + + val externalUsages = SmartList() + val outerInstanceUsages = SmartList() + val conflicts = MultiMap() + + val outerInstanceReferences = collectOuterInstanceReferences(element) + if (outerInstanceReferences.isNotEmpty()) { + if (element is KtProperty) { + conflicts.putValue(element, "Usages of outer class instance inside of property '${element.name}' won't be processed") + } + else { + outerInstanceReferences.filterNotTo(outerInstanceUsages) { it.reportConflictIfAny(conflicts) } + } + } + + project.runSynchronouslyWithProgress("Searching for ${element.name}", true) { + ReferencesSearch.search(element).mapNotNullTo(externalUsages) { ref -> + when (ref) { + is PsiReferenceExpression -> JavaUsageInfo(ref) + is KtSimpleNameReference -> { + val refExpr = ref.expression + if (element.isAncestor(refExpr)) return@mapNotNullTo null + val context = refExpr.analyze(BodyResolveMode.PARTIAL) + val resolvedCall = refExpr.getResolvedCall(context) ?: return@mapNotNullTo null + + val callExpression = resolvedCall.call.callElement as? KtExpression ?: return@mapNotNullTo null + + val extensionReceiver = resolvedCall.extensionReceiver + if (extensionReceiver != null && extensionReceiver !is ImplicitReceiver) { + conflicts.putValue(callExpression, + "Calls with explicit extension receiver won't be processed: ${callExpression.text}") + return@mapNotNullTo null + } + + val dispatchReceiver = resolvedCall.dispatchReceiver ?: return@mapNotNullTo null + if (dispatchReceiver is ExpressionReceiver) { + ExplicitReceiverUsageInfo(refExpr, dispatchReceiver.expression) + } + else { + ImplicitReceiverUsageInfo(refExpr, callExpression) + } + } + else -> null + } + } + } + + project.checkConflictsInteractively(conflicts) { + doMove(element, externalUsages, outerInstanceUsages, editor) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt index b0de4316647..aa079a0aeb9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt @@ -19,7 +19,10 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.utils.getOrPutNullable @@ -56,6 +59,18 @@ class KotlinMoveTargetForExistingElement(val targetElement: KtElement): KotlinMo override fun verify(file: PsiFile): String? = null } +class KotlinMoveTargetForCompanion(val targetClass: KtClass): KotlinMoveTarget { + override val targetContainerFqName = targetClass.getCompanionObjects().firstOrNull()?.fqName + ?: targetClass.fqName!!.child(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) + + override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetClass.getOrCreateCompanionObject() + + override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetClass.getCompanionObjects().firstOrNull() + + // No additional verification is needed + override fun verify(file: PsiFile): String? = null +} + class KotlinMoveTargetForDeferredFile( override val targetContainerFqName: FqName, override val directory: PsiDirectory?, diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt index 7de659896d1..6f30ddec975 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt @@ -31,12 +31,15 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject -import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector sealed class MoveDeclarationsDelegate { abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo abstract fun findUsages(descriptor: MoveDeclarationsDescriptor): List - abstract fun collectConflicts(usages: MutableList, conflicts: MultiMap) + abstract fun collectConflicts( + descriptor: MoveDeclarationsDescriptor, + usages: MutableList, + conflicts: MultiMap + ) abstract fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) abstract fun preprocessUsages(project: Project, usages: List) @@ -48,7 +51,11 @@ sealed class MoveDeclarationsDelegate { override fun findUsages(descriptor: MoveDeclarationsDescriptor): List = emptyList() - override fun collectConflicts(usages: MutableList, conflicts: MultiMap) { + override fun collectConflicts( + descriptor: MoveDeclarationsDescriptor, + usages: MutableList, + conflicts: MultiMap + ) { } @@ -81,44 +88,30 @@ sealed class MoveDeclarationsDelegate { return collectOuterInstanceReferences(classToMove) } - override fun collectConflicts(usages: MutableList, conflicts: MultiMap) { + override fun collectConflicts( + descriptor: MoveDeclarationsDescriptor, + usages: MutableList, + conflicts: MultiMap + ) { 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() + val isConflict = when (usage) { + is ImplicitCompanionAsDispatchReceiverUsageInfo -> { + if (descriptor.moveTarget !is KotlinMoveTargetForCompanion) { + conflicts.putValue(element, "Implicit companion object will be inaccessible: ${element.text}") + } + true } - usage.isDoubleReceiver -> { - conflicts.putValue( - fullCall, - "Call with two implicit receivers won't be processed: ${fullCall.text}" - ) - usageIterator.remove() - } + is OuterInstanceReferenceUsageInfo -> usage.reportConflictIfAny(conflicts) + + else -> false + } + if (isConflict) { + usageIterator.remove() } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt index 077899bd790..dadf5e6ce8d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt @@ -318,10 +318,10 @@ class MoveKotlinDeclarationsProcessor( collectUsages(kotlinToLightElements, usages) checkVisibilityInUsages(usages) checkVisibilityInDeclarations() - descriptor.delegate.collectConflicts(usages, conflicts) + descriptor.delegate.collectConflicts(descriptor, usages, conflicts) } - descriptor.delegate.collectConflicts(usagesToProcessBeforeMove, conflicts) + descriptor.delegate.collectConflicts(descriptor, usagesToProcessBeforeMove, conflicts) return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray()) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt index 076526333e2..c79a3f47f4f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt @@ -37,13 +37,13 @@ import com.intellij.refactoring.util.NonCodeUsageInfo import com.intellij.usageView.UsageInfo import com.intellij.util.IncorrectOperationException import com.intellij.util.SmartList +import com.intellij.util.containers.MultiMap 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 @@ -453,14 +453,14 @@ sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, val isIndirect } @JvmOverloads -fun traverseOuterInstanceReferences(innerClass: KtClass, stopAtFirst: Boolean, body: (OuterInstanceReferenceUsageInfo) -> Unit = {}): Boolean { - if (!innerClass.isInner()) return false +fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boolean, body: (OuterInstanceReferenceUsageInfo) -> Unit = {}): Boolean { + if (member is KtObjectDeclaration || member is KtClass && !member.isInner()) return false - val context = innerClass.analyzeFully() - val innerClassDescriptor = innerClass.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false - val outerClassDescriptor = innerClassDescriptor.containingDeclaration as? ClassDescriptor ?: return false + val context = member.analyzeFully() + val containingClassOrObject = member.containingClassOrObject ?: return false + val outerClassDescriptor = containingClassOrObject.resolveToDescriptor() as ClassDescriptor var found = false - innerClass.accept( + member.accept( object : PsiRecursiveElementWalkingVisitor() { private fun getOuterInstanceReference(element: PsiElement): OuterInstanceReferenceUsageInfo? { return when (element) { @@ -512,6 +512,31 @@ fun traverseOuterInstanceReferences(innerClass: KtClass, stopAtFirst: Boolean, b return found } -fun collectOuterInstanceReferences(innerClass: KtClass): List { - return SmartList().apply { traverseOuterInstanceReferences(innerClass, false) { add(it) } } +fun collectOuterInstanceReferences(member: KtNamedDeclaration): List { + return SmartList().apply { traverseOuterInstanceReferences(member, false) { add(it) } } } + +fun OuterInstanceReferenceUsageInfo.reportConflictIfAny(conflicts: MultiMap): Boolean { + val element = element ?: return false + + if (isIndirectOuter) { + conflicts.putValue(element, "Indirect outer instances won't be extracted: ${element.text}") + return true + } + + if (this !is OuterInstanceReferenceUsageInfo.ImplicitReceiver) return false + + val fullCall = callElement?.let { it.getQualifiedExpressionForSelector() ?: it } ?: return false + return when { + fullCall is KtQualifiedExpression -> { + conflicts.putValue(fullCall, "Qualified call won't be processed: ${fullCall.text}") + true + } + + isDoubleReceiver -> { + conflicts.putValue(fullCall, "Call with two implicit receivers won't be processed: ${fullCall.text}") + true + } + else -> false + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/.intention b/idea/testData/intentions/moveToCompanion/.intention new file mode 100644 index 00000000000..d009fec6746 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention diff --git a/idea/testData/intentions/moveToCompanion/abstractFunction.kt b/idea/testData/intentions/moveToCompanion/abstractFunction.kt new file mode 100644 index 00000000000..c5a2a07bb89 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/abstractFunction.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +abstract class A { + abstract fun foo() +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/abstractProperty.kt b/idea/testData/intentions/moveToCompanion/abstractProperty.kt new file mode 100644 index 00000000000..e63f136d530 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/abstractProperty.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +abstract class A { + abstract val foo: Int +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/dropFinal.kt b/idea/testData/intentions/moveToCompanion/dropFinal.kt new file mode 100644 index 00000000000..7dda2c1963d --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/dropFinal.kt @@ -0,0 +1,5 @@ +class A { + final fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/dropFinal.kt.after b/idea/testData/intentions/moveToCompanion/dropFinal.kt.after new file mode 100644 index 00000000000..6c40582ea62 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/dropFinal.kt.after @@ -0,0 +1,7 @@ +class A { + companion object { + fun foo() { + + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/dropOpen.kt b/idea/testData/intentions/moveToCompanion/dropOpen.kt new file mode 100644 index 00000000000..f3d6421d400 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/dropOpen.kt @@ -0,0 +1,5 @@ +open class A { + open fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/dropOpen.kt.after b/idea/testData/intentions/moveToCompanion/dropOpen.kt.after new file mode 100644 index 00000000000..dfa96bb3ae3 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/dropOpen.kt.after @@ -0,0 +1,7 @@ +open class A { + companion object { + fun foo() { + + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/enumEntry.kt b/idea/testData/intentions/moveToCompanion/enumEntry.kt new file mode 100644 index 00000000000..9c7d74d599a --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/enumEntry.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +enum class E { + A, + B, + C +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/functionWithOverride.kt b/idea/testData/intentions/moveToCompanion/functionWithOverride.kt new file mode 100644 index 00000000000..08b88e4060d --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/functionWithOverride.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +interface A { + fun foo() +} + +class B: A { + override fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/functionWithoutBody.kt b/idea/testData/intentions/moveToCompanion/functionWithoutBody.kt new file mode 100644 index 00000000000..135198780f3 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/functionWithoutBody.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +interface A { + fun foo() +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/inInnerClass.kt b/idea/testData/intentions/moveToCompanion/inInnerClass.kt new file mode 100644 index 00000000000..1bc95469d6d --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/inInnerClass.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +class A { + inner class B { + fun foo() { + + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/inLocalClass.kt b/idea/testData/intentions/moveToCompanion/inLocalClass.kt new file mode 100644 index 00000000000..49fac6a871e --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/inLocalClass.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +fun foo() { + class A { + fun bar() { + + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/inObject.kt b/idea/testData/intentions/moveToCompanion/inObject.kt new file mode 100644 index 00000000000..3d6fde87db6 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/inObject.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +object A { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/localFunction.kt b/idea/testData/intentions/moveToCompanion/localFunction.kt new file mode 100644 index 00000000000..f70051cd6b1 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/localFunction.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +fun foo() { + fun bar() { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/overriddenFunction.kt b/idea/testData/intentions/moveToCompanion/overriddenFunction.kt new file mode 100644 index 00000000000..16ef47f6a48 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/overriddenFunction.kt @@ -0,0 +1,12 @@ +// SHOULD_FAIL_WITH: Function foo is overridden by declaration(s) in a subclass +open class A { + open fun foo() { + + } +} + +class B: A() { + override fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/overriddenProperty.kt b/idea/testData/intentions/moveToCompanion/overriddenProperty.kt new file mode 100644 index 00000000000..3a9e9e2e800 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/overriddenProperty.kt @@ -0,0 +1,8 @@ +// SHOULD_FAIL_WITH: Property foo is overridden by declaration(s) in a subclass +open class A { + open val foo: Int = 1 +} + +class B: A() { + override val foo: Int = 2 +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/propertyUsingClassInstance.kt b/idea/testData/intentions/moveToCompanion/propertyUsingClassInstance.kt new file mode 100644 index 00000000000..ba832c99f1a --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/propertyUsingClassInstance.kt @@ -0,0 +1,5 @@ +// SHOULD_FAIL_WITH: Usages of outer class instance inside of property 'y' won't be processed +class A { + val x = 1 + val y = x + 1 +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/propertyWithOverride.kt b/idea/testData/intentions/moveToCompanion/propertyWithOverride.kt new file mode 100644 index 00000000000..f0078af1356 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/propertyWithOverride.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +interface A { + val foo: Int +} + +class B: A { + override val foo: Int = 1 +} \ No newline at end of file diff --git a/idea/testData/intentions/moveToCompanion/topLevelFunction.kt b/idea/testData/intentions/moveToCompanion/topLevelFunction.kt new file mode 100644 index 00000000000..eb72d64f233 --- /dev/null +++ b/idea/testData/intentions/moveToCompanion/topLevelFunction.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +fun foo() { + +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/test.kt new file mode 100644 index 00000000000..30bdb53b23d --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/test.kt @@ -0,0 +1,62 @@ +package test + +inline fun 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 + fun test(a: A, n: Int) { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + a.OuterY() + a.outerFoo(a.outerBar) + + a.OuterY() + a.outerFoo(a.outerBar) + + O.Y() + O.foo(O.bar) + + with (O) { + Y() + foo(bar) + 1.extFoo(1.extBar) + } + } + } + + object O { + class Y + + fun foo(n: Int) {} + + val bar = 1 + + fun Int.extFoo(n: Int) {} + + val Int.extBar: Int get() = 1 + } + +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/test2/usages.java new file mode 100644 index 00000000000..4df8d322dac --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/test2/usages.java @@ -0,0 +1,9 @@ +package test2; + +import test.A; + +class Test { + void foo() { + A.Companion.test(new A(), 1); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/usages.kt new file mode 100644 index 00000000000..2e7444d9816 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/after/usages.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A + +fun foo() { + A.test(A(), 1) +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/test.kt new file mode 100644 index 00000000000..961282c843c --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/test.kt @@ -0,0 +1,62 @@ +package test + +inline fun 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 + } + + fun test(n: Int) { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + OuterY() + outerFoo(outerBar) + + this.OuterY() + this.outerFoo(this@A.outerBar) + + O.Y() + O.foo(O.bar) + + with (O) { + Y() + foo(bar) + 1.extFoo(1.extBar) + } + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/test2/usages.java new file mode 100644 index 00000000000..e03dba8aac3 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/test2/usages.java @@ -0,0 +1,9 @@ +package test2; + +import test.A; + +class Test { + void foo() { + new A().test(1); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/usages.kt new file mode 100644 index 00000000000..10462afd187 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/before/usages.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A + +fun foo() { + A().test(1) +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/moveFunction.test b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/moveFunction.test new file mode 100644 index 00000000000..6881d7e6cc9 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveFunction/moveFunction.test @@ -0,0 +1,4 @@ +{ + "mainFile": "test.kt", + "intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention" +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test.kt new file mode 100644 index 00000000000..241f402d9e5 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test.kt @@ -0,0 +1,65 @@ +package test + +inline fun 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 + + class B(private val a: A) { + fun test() { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + a.OuterY() + a.outerFoo(a.outerBar) + + a.OuterY() + a.outerFoo(a.outerBar) + + O.Y() + O.foo(O.bar) + + with (O) { + Y() + foo(bar) + 1.extFoo(1.extBar) + } + } + } + } + + object O { + class Y + + fun foo(n: Int) {} + + val bar = 1 + + fun Int.extFoo(n: Int) {} + + val Int.extBar: Int get() = 1 + } + +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test2/usages.java new file mode 100644 index 00000000000..21a25f656eb --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test2/usages.java @@ -0,0 +1,9 @@ +package test2; + +import test.A; + +class Test { + A.Companion.B foo() { + return new A.Companion.B(new A()); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test2/usages2.java b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test2/usages2.java new file mode 100644 index 00000000000..a3f87deee6a --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/test2/usages2.java @@ -0,0 +1,10 @@ +package test2; + +import test.A; +import test.A.Companion.B; + +class Test { + B foo() { + return new B(new A()); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/usages.kt new file mode 100644 index 00000000000..704f78351a3 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/usages.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A + +fun foo(): A.Companion.B { + return A.Companion.B(A()) +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/usages2.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/usages2.kt new file mode 100644 index 00000000000..6fe5ec8f35a --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/after/usages2.kt @@ -0,0 +1,8 @@ +package test2 + +import test.A +import test.A.Companion.B + +fun foo2(): B { + return B(A()) +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test.kt new file mode 100644 index 00000000000..c1f68e15a7d --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test.kt @@ -0,0 +1,64 @@ +package test + +inline fun 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 B { + fun test() { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + 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) + } + } + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test2/usages.java new file mode 100644 index 00000000000..0858bb64317 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test2/usages.java @@ -0,0 +1,9 @@ +package test2; + +import test.A; + +class Test { + A.B foo() { + return new A().new B(); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test2/usages2.java b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test2/usages2.java new file mode 100644 index 00000000000..c4eba0b1f69 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/test2/usages2.java @@ -0,0 +1,10 @@ +package test2; + +import test.A; +import test.A.B; + +class Test { + B foo() { + return new A().new B(); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/usages.kt new file mode 100644 index 00000000000..439c6744def --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/usages.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A + +fun foo(): A.B { + return A().B() +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/usages2.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/usages2.kt new file mode 100644 index 00000000000..7a6a74fa8c5 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/before/usages2.kt @@ -0,0 +1,8 @@ +package test2 + +import test.A +import test.A.B + +fun foo2(): B { + return A().B() +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/moveInnerClass.test b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/moveInnerClass.test new file mode 100644 index 00000000000..6881d7e6cc9 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/moveInnerClass.test @@ -0,0 +1,4 @@ +{ + "mainFile": "test.kt", + "intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention" +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test.kt new file mode 100644 index 00000000000..cc53d9c7d0b --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test.kt @@ -0,0 +1,53 @@ +package test + +inline fun 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 + + class B { + fun test() { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + O.Y() + O.foo(O.bar) + + with (O) { + Y() + foo(bar) + 1.extFoo(1.extBar) + } + } + } + } + + object O { + class Y + + fun foo(n: Int) {} + + val bar = 1 + + fun Int.extFoo(n: Int) {} + + val Int.extBar: Int get() = 1 + } + +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test2/usages.java new file mode 100644 index 00000000000..e7fa830bcbd --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test2/usages.java @@ -0,0 +1,9 @@ +package test2; + +import test.A; + +class Test { + A.Companion.B foo() { + return new A.Companion.B(); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test2/usages2.java b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test2/usages2.java new file mode 100644 index 00000000000..bfb6dee9097 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/test2/usages2.java @@ -0,0 +1,9 @@ +package test2; + +import test.A.Companion.B; + +class Test { + B foo() { + return new B(); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/usages.kt new file mode 100644 index 00000000000..11821ac9e28 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/usages.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A + +fun foo(): A.Companion.B { + return A.Companion.B() +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/usages2.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/usages2.kt new file mode 100644 index 00000000000..96cd7ae2868 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/after/usages2.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A.Companion.B + +fun foo(): B { + return B() +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test.kt new file mode 100644 index 00000000000..c88fdccb070 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test.kt @@ -0,0 +1,52 @@ +package test + +inline fun 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 { + fun test() { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + O.Y() + O.foo(O.bar) + + with (O) { + Y() + foo(bar) + 1.extFoo(1.extBar) + } + } + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test2/usages.java new file mode 100644 index 00000000000..7816fedd348 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test2/usages.java @@ -0,0 +1,9 @@ +package test2; + +import test.A; + +class Test { + A.B foo() { + return new A.B(); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test2/usages2.java b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test2/usages2.java new file mode 100644 index 00000000000..c543d09b86f --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/test2/usages2.java @@ -0,0 +1,9 @@ +package test2; + +import test.A.B; + +class Test { + B foo() { + return new B(); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/usages.kt new file mode 100644 index 00000000000..b4343208678 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/usages.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A + +fun foo(): A.B { + return A.B() +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/usages2.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/usages2.kt new file mode 100644 index 00000000000..afebe70e74c --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/before/usages2.kt @@ -0,0 +1,7 @@ +package test2 + +import test.A.B + +fun foo(): B { + return B() +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/moveNonInnerClass.test b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/moveNonInnerClass.test new file mode 100644 index 00000000000..6881d7e6cc9 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/moveNonInnerClass.test @@ -0,0 +1,4 @@ +{ + "mainFile": "test.kt", + "intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention" +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/test.kt new file mode 100644 index 00000000000..7e4bcadda27 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/test.kt @@ -0,0 +1,58 @@ +package test + +inline fun 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 + var test: Int + get() = 1 + set(value: Int) { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + O.Y() + O.foo(O.bar) + + with (O) { + Y() + foo(bar) + 1.extFoo(1.extBar) + } + } + } + + object O { + class Y + + fun foo(n: Int) {} + + val bar = 1 + + fun Int.extFoo(n: Int) {} + + val Int.extBar: Int get() = 1 + } + +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/test2/usages.java new file mode 100644 index 00000000000..ec6016380e2 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/test2/usages.java @@ -0,0 +1,10 @@ +package test2; + +import test.A; + +class Test { + void foo() { + int x = A.Companion.getTest(); + A.Companion.setTest(1); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/usages.kt new file mode 100644 index 00000000000..c577178c290 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/after/usages.kt @@ -0,0 +1,8 @@ +package test2 + +import test.A + +fun foo() { + val x = A.test + A.test = 1 +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/test.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/test.kt new file mode 100644 index 00000000000..7a9e16b17f4 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/test.kt @@ -0,0 +1,58 @@ +package test + +inline fun 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 + } + + var test: Int + get() = 1 + set(value: Int) { + X() + + Y() + foo(bar) + 1.extFoo(1.extBar) + + O.Y() + O.foo(O.bar) + + with (O) { + Y() + foo(bar) + 1.extFoo(1.extBar) + } + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/test2/usages.java b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/test2/usages.java new file mode 100644 index 00000000000..4195d453e56 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/test2/usages.java @@ -0,0 +1,10 @@ +package test2; + +import test.A; + +class Test { + void foo() { + int x = new A().getTest(); + new A().setTest(1); + } +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/usages.kt b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/usages.kt new file mode 100644 index 00000000000..746a29d8694 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/before/usages.kt @@ -0,0 +1,8 @@ +package test2 + +import test.A + +fun foo() { + val x = A().test + A().test = 1 +} \ No newline at end of file diff --git a/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/moveProperty.test b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/moveProperty.test new file mode 100644 index 00000000000..6881d7e6cc9 --- /dev/null +++ b/idea/testData/multiFileIntentions/moveToCompanion/moveProperty/moveProperty.test @@ -0,0 +1,4 @@ +{ + "mainFile": "test.kt", + "intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention" +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis.kt b/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis.kt index b46dea7f309..1a1f6d3ce5d 100644 --- a/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis.kt +++ b/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis.kt @@ -1,5 +1,6 @@ // "Delete redundant extension property" "false" // ACTION: Convert property to function +// ACTION: Move to companion object class C : Thread() { val Thread.priority: Int diff --git a/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis2.kt b/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis2.kt index b492247ef7c..260867bd4c0 100644 --- a/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis2.kt +++ b/idea/testData/quickfix/migration/conflictingExtension/wrongExplicitThis2.kt @@ -1,4 +1,5 @@ // "Delete redundant extension property" "false" +// ACTION: Move to companion object class C : Thread() { var Thread.priority: Int diff --git a/idea/testData/quickfix/typeAddition/noAddErrorType.kt b/idea/testData/quickfix/typeAddition/noAddErrorType.kt index 13de5200f32..98980c9ac97 100644 --- a/idea/testData/quickfix/typeAddition/noAddErrorType.kt +++ b/idea/testData/quickfix/typeAddition/noAddErrorType.kt @@ -1,6 +1,7 @@ // "Specify type explicitly" "false" // ACTION: Convert property to function // ACTION: Introduce backing property +// ACTION: Move to companion object // ERROR: Unresolved reference: foo class A() { diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTest.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTest.java index 5c684310f1e..b8b8bb8d008 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTest.java @@ -25,6 +25,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiFile; import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; @@ -190,6 +191,9 @@ public abstract class AbstractIntentionTest extends KotlinCodeInsightTestCase { catch (BaseRefactoringProcessor.ConflictsInTestsException e) { assertEquals("Failure message mismatch.", shouldFailString, StringUtil.join(e.getMessages(), ", ")); } + catch (CommonRefactoringUtil.RefactoringErrorHintException e) { + assertEquals("Failure message mismatch.", shouldFailString, e.getMessage()); + } } @Override diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 409b0e4d49c..090be6de978 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -6530,6 +6530,162 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/moveOutOfCompanion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MoveOutOfCompanion extends AbstractIntentionTest { + public void testAllFilesPresentInMoveOutOfCompanion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/moveOutOfCompanion"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("moveAndDropCompanion.kt") + public void testMoveAndDropCompanion() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveOutOfCompanion/moveAndDropCompanion.kt"); + doTest(fileName); + } + + @TestMetadata("moveFunction.kt") + public void testMoveFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveOutOfCompanion/moveFunction.kt"); + doTest(fileName); + } + + @TestMetadata("moveFunctionWithExternalRefs.kt") + public void testMoveFunctionWithExternalRefs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveOutOfCompanion/moveFunctionWithExternalRefs.kt"); + doTest(fileName); + } + + @TestMetadata("moveProperty.kt") + public void testMoveProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveOutOfCompanion/moveProperty.kt"); + doTest(fileName); + } + + @TestMetadata("movePropertyWithExternalRefs.kt") + public void testMovePropertyWithExternalRefs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveOutOfCompanion/movePropertyWithExternalRefs.kt"); + doTest(fileName); + } + + @TestMetadata("notCompanion1.kt") + public void testNotCompanion1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveOutOfCompanion/notCompanion1.kt"); + doTest(fileName); + } + + @TestMetadata("notCompanion2.kt") + public void testNotCompanion2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveOutOfCompanion/notCompanion2.kt"); + doTest(fileName); + } + } + + @TestMetadata("idea/testData/intentions/moveToCompanion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MoveToCompanion extends AbstractIntentionTest { + @TestMetadata("abstractFunction.kt") + public void testAbstractFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/abstractFunction.kt"); + doTest(fileName); + } + + @TestMetadata("abstractProperty.kt") + public void testAbstractProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/abstractProperty.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInMoveToCompanion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/moveToCompanion"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("dropFinal.kt") + public void testDropFinal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/dropFinal.kt"); + doTest(fileName); + } + + @TestMetadata("dropOpen.kt") + public void testDropOpen() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/dropOpen.kt"); + doTest(fileName); + } + + @TestMetadata("enumEntry.kt") + public void testEnumEntry() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/enumEntry.kt"); + doTest(fileName); + } + + @TestMetadata("functionWithOverride.kt") + public void testFunctionWithOverride() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/functionWithOverride.kt"); + doTest(fileName); + } + + @TestMetadata("functionWithoutBody.kt") + public void testFunctionWithoutBody() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/functionWithoutBody.kt"); + doTest(fileName); + } + + @TestMetadata("inInnerClass.kt") + public void testInInnerClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/inInnerClass.kt"); + doTest(fileName); + } + + @TestMetadata("inLocalClass.kt") + public void testInLocalClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/inLocalClass.kt"); + doTest(fileName); + } + + @TestMetadata("inObject.kt") + public void testInObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/inObject.kt"); + doTest(fileName); + } + + @TestMetadata("localFunction.kt") + public void testLocalFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/localFunction.kt"); + doTest(fileName); + } + + @TestMetadata("overriddenFunction.kt") + public void testOverriddenFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/overriddenFunction.kt"); + doTest(fileName); + } + + @TestMetadata("overriddenProperty.kt") + public void testOverriddenProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/overriddenProperty.kt"); + doTest(fileName); + } + + @TestMetadata("propertyUsingClassInstance.kt") + public void testPropertyUsingClassInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/propertyUsingClassInstance.kt"); + doTest(fileName); + } + + @TestMetadata("propertyWithOverride.kt") + public void testPropertyWithOverride() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/propertyWithOverride.kt"); + doTest(fileName); + } + + @TestMetadata("topLevelFunction.kt") + public void testTopLevelFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/moveToCompanion/topLevelFunction.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/objectLiteralToLambda") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/MultiFileIntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/MultiFileIntentionTestGenerated.java index 7cc883d12d3..a83633ea579 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/MultiFileIntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/MultiFileIntentionTestGenerated.java @@ -101,6 +101,36 @@ public class MultiFileIntentionTestGenerated extends AbstractMultiFileIntentionT doTest(fileName); } + @TestMetadata("moveOutOfCompanion/moveClass/moveClass.test") + public void testMoveOutOfCompanion_moveClass_MoveClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/moveOutOfCompanion/moveClass/moveClass.test"); + doTest(fileName); + } + + @TestMetadata("moveToCompanion/moveFunction/moveFunction.test") + public void testMoveToCompanion_moveFunction_MoveFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/moveToCompanion/moveFunction/moveFunction.test"); + doTest(fileName); + } + + @TestMetadata("moveToCompanion/moveInnerClass/moveInnerClass.test") + public void testMoveToCompanion_moveInnerClass_MoveInnerClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/moveToCompanion/moveInnerClass/moveInnerClass.test"); + doTest(fileName); + } + + @TestMetadata("moveToCompanion/moveNonInnerClass/moveNonInnerClass.test") + public void testMoveToCompanion_moveNonInnerClass_MoveNonInnerClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/moveToCompanion/moveNonInnerClass/moveNonInnerClass.test"); + doTest(fileName); + } + + @TestMetadata("moveToCompanion/moveProperty/moveProperty.test") + public void testMoveToCompanion_moveProperty_MoveProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/moveToCompanion/moveProperty/moveProperty.test"); + doTest(fileName); + } + @TestMetadata("objectLiteralToLambda/objectLiteralToLambda.test") public void testObjectLiteralToLambda_ObjectLiteralToLambda() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileIntentions/objectLiteralToLambda/objectLiteralToLambda.test");