Intentions: Move class member to companion object

#KT-9697 In Progress
This commit is contained in:
Alexey Sedunov
2016-01-29 15:02:19 +03:00
parent fe8a0ec2bc
commit d13ac6b5a4
70 changed files with 1417 additions and 45 deletions
@@ -0,0 +1,7 @@
class A {
companion object {
<spot>fun foo() = 1</spot>
}
}
fun bar() = A.foo()
@@ -0,0 +1,5 @@
class A {
<spot>fun foo() = 1</spot>
}
fun bar() = A().foo()
@@ -0,0 +1,5 @@
<html>
<body>
This intention moves class member to the corresponding companion object.
</body>
</html>
+5
View File
@@ -1164,6 +1164,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Convert object literal to lambda"
groupName="Kotlin"
@@ -0,0 +1,305 @@
/*
* 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.intentions
import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.codeInsight.template.TemplateBuilderImpl
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiReferenceExpression
import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.usageView.UsageInfo
import com.intellij.util.SmartList
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.CollectingNameValidator
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.move.OuterInstanceReferenceUsageInfo
import org.jetbrains.kotlin.idea.refactoring.move.collectOuterInstanceReferences
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.move.reportConflictIfAny
import org.jetbrains.kotlin.idea.refactoring.move.traverseOuterInstanceReferences
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
class MoveMemberToCompanionObjectIntention : SelfTargetingRangeIntention<KtNamedDeclaration>(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<String> {
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<String>,
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<UsageInfo>,
outerInstanceUsages: SmartList<UsageInfo>,
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<KtElement>()
val nameSuggestions: List<String>
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<UsageInfo>()
val outerInstanceUsages = SmartList<UsageInfo>()
val conflicts = MultiMap<PsiElement, String>()
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)
}
}
}
@@ -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?,
@@ -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<UsageInfo>
abstract fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>)
abstract fun collectConflicts(
descriptor: MoveDeclarationsDescriptor,
usages: MutableList<UsageInfo>,
conflicts: MultiMap<PsiElement, String>
)
abstract fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration)
abstract fun preprocessUsages(project: Project, usages: List<UsageInfo>)
@@ -48,7 +51,11 @@ sealed class MoveDeclarationsDelegate {
override fun findUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
override fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>) {
override fun collectConflicts(
descriptor: MoveDeclarationsDescriptor,
usages: MutableList<UsageInfo>,
conflicts: MultiMap<PsiElement, String>
) {
}
@@ -81,44 +88,30 @@ sealed class MoveDeclarationsDelegate {
return collectOuterInstanceReferences(classToMove)
}
override fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>) {
override fun collectConflicts(
descriptor: MoveDeclarationsDescriptor,
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()
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()
}
}
}
@@ -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())
}
@@ -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<OuterInstanceReferenceUsageInfo> {
return SmartList<OuterInstanceReferenceUsageInfo>().apply { traverseOuterInstanceReferences(innerClass, false) { add(it) } }
fun collectOuterInstanceReferences(member: KtNamedDeclaration): List<OuterInstanceReferenceUsageInfo> {
return SmartList<OuterInstanceReferenceUsageInfo>().apply { traverseOuterInstanceReferences(member, false) { add(it) } }
}
fun OuterInstanceReferenceUsageInfo.reportConflictIfAny(conflicts: MultiMap<PsiElement, String>): 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
}
}
+1
View File
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
abstract class A {
abstract fun <caret>foo()
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
abstract class A {
abstract val <caret>foo: Int
}
+5
View File
@@ -0,0 +1,5 @@
class A {
final fun <caret>foo() {
}
}
@@ -0,0 +1,7 @@
class A {
companion object {
fun foo() {
}
}
}
+5
View File
@@ -0,0 +1,5 @@
open class A {
open fun <caret>foo() {
}
}
@@ -0,0 +1,7 @@
open class A {
companion object {
fun foo() {
}
}
}
+7
View File
@@ -0,0 +1,7 @@
// IS_APPLICABLE: false
enum class E {
A,
<caret>B,
C
}
@@ -0,0 +1,11 @@
// IS_APPLICABLE: false
interface A {
fun foo()
}
class B: A {
override fun <caret>foo() {
}
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
interface A {
fun <caret>foo()
}
@@ -0,0 +1,9 @@
// IS_APPLICABLE: false
class A {
inner class B {
fun <caret>foo() {
}
}
}
@@ -0,0 +1,9 @@
// IS_APPLICABLE: false
fun foo() {
class A {
fun <caret>bar() {
}
}
}
+7
View File
@@ -0,0 +1,7 @@
// IS_APPLICABLE: false
object A {
fun <caret>foo() {
}
}
@@ -0,0 +1,7 @@
// IS_APPLICABLE: false
fun foo() {
fun <caret>bar() {
}
}
@@ -0,0 +1,12 @@
// SHOULD_FAIL_WITH: Function <b><code>foo</code></b> is overridden by declaration(s) in a subclass
open class A {
open fun <caret>foo() {
}
}
class B: A() {
override fun foo() {
}
}
@@ -0,0 +1,8 @@
// SHOULD_FAIL_WITH: Property <b><code>foo</code></b> is overridden by declaration(s) in a subclass
open class A {
open val <caret>foo: Int = 1
}
class B: A() {
override val foo: Int = 2
}
@@ -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 <caret>y = x + 1
}
@@ -0,0 +1,9 @@
// IS_APPLICABLE: false
interface A {
val foo: Int
}
class B: A {
override val <caret>foo: Int = 1
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
fun <caret>foo() {
}
@@ -0,0 +1,62 @@
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
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
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
void foo() {
A.Companion.test(new A(), 1);
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo() {
A.test(A(), 1)
}
@@ -0,0 +1,62 @@
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
}
fun <caret>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)
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
void foo() {
new A().test(1);
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo() {
A().test(1)
}
@@ -0,0 +1,4 @@
{
"mainFile": "test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention"
}
@@ -0,0 +1,65 @@
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
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
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.Companion.B foo() {
return new A.Companion.B(new A());
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.Companion.B;
class Test {
B foo() {
return new B(new A());
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.Companion.B {
return A.Companion.B(A())
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.Companion.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)
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,4 @@
{
"mainFile": "test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention"
}
@@ -0,0 +1,53 @@
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
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
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.Companion.B foo() {
return new A.Companion.B();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.Companion.B;
class Test {
B foo() {
return new B();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.Companion.B {
return A.Companion.B()
}
@@ -0,0 +1,7 @@
package test2
import test.A.Companion.B
fun foo(): 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
}
class <caret>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)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B foo() {
return new A.B();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B;
class Test {
B foo() {
return new B();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B {
return A.B()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B
fun foo(): B {
return B()
}
@@ -0,0 +1,4 @@
{
"mainFile": "test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention"
}
@@ -0,0 +1,58 @@
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
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
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
class Test {
void foo() {
int x = A.Companion.getTest();
A.Companion.setTest(1);
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
fun foo() {
val x = A.test
A.test = 1
}
@@ -0,0 +1,58 @@
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
}
var <caret>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)
}
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
class Test {
void foo() {
int x = new A().getTest();
new A().setTest(1);
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
fun foo() {
val x = A().test
A().test = 1
}
@@ -0,0 +1,4 @@
{
"mainFile": "test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention"
}
@@ -1,5 +1,6 @@
// "Delete redundant extension property" "false"
// ACTION: Convert property to function
// ACTION: Move to companion object
class C : Thread() {
val Thread.<caret>priority: Int
@@ -1,4 +1,5 @@
// "Delete redundant extension property" "false"
// ACTION: Move to companion object
class C : Thread() {
var Thread.<caret>priority: Int
+1
View File
@@ -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() {
@@ -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
@@ -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)
@@ -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");