Pull Up/Extract Super: Support members declared in the companion object of the original class

This commit is contained in:
Alexey Sedunov
2016-09-20 19:06:25 +03:00
parent 982d4d4ead
commit 8a5628cde5
18 changed files with 132 additions and 24 deletions
+1
View File
@@ -149,6 +149,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
- [`KT-11017`](https://youtrack.jetbrains.com/issue/KT-11017) Implement "Extract Superclass" refactoring
- [`KT-11017`](https://youtrack.jetbrains.com/issue/KT-11017) Implement "Extract Interface" refactoring
Pull Up: Support properties declared in the primary constructor
Pull Up: Support members declared in the companion object of the original class
#### Android Lint
@@ -139,4 +139,17 @@ abstract class KtNamedDeclarationStub<T extends KotlinStubWithFqName<?>> extends
}
return KtNamedDeclarationUtil.getFQName(this);
}
@Override
public void delete() throws IncorrectOperationException {
KtClassOrObject classOrObject = KtPsiUtilKt.getContainingClassOrObject(this);
super.delete();
if (classOrObject instanceof KtObjectDeclaration
&& ((KtObjectDeclaration) classOrObject).isCompanion()
&& classOrObject.getDeclarations().isEmpty()) {
classOrObject.delete();
}
}
}
@@ -880,3 +880,8 @@ fun checkSuperMethods(
return askUserForMethodsToSearch(declarationDescriptor, overriddenElementsToDescriptor)
}
fun KtNamedDeclaration.isCompanionMemberOf(klass: KtClassOrObject): Boolean {
val containingObject = containingClassOrObject as? KtObjectDeclaration ?: return false
return containingObject.isCompanion() && containingObject.containingClassOrObject == klass
}
@@ -36,7 +36,11 @@ import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptySet
class KotlinMemberInfo(member: KtNamedDeclaration, val isSuperClass: Boolean = false) : MemberInfoBase<KtNamedDeclaration>(member) {
class KotlinMemberInfo @JvmOverloads constructor(
member: KtNamedDeclaration,
val isSuperClass: Boolean = false,
val isCompanionMember: Boolean = false
) : MemberInfoBase<KtNamedDeclaration>(member) {
companion object {
private val RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions {
modifiers = DescriptorRendererModifier.INNER.singletonOrEmptySet()
@@ -62,6 +66,9 @@ class KotlinMemberInfo(member: KtNamedDeclaration, val isSuperClass: Boolean = f
if (memberDescriptor is MemberDescriptor && memberDescriptor.modality == Modality.ABSTRACT) {
displayName = "abstract $displayName"
}
if (isCompanionMember) {
displayName = "companion $displayName"
}
val overriddenDescriptors = (memberDescriptor as? CallableMemberDescriptor)?.overriddenDescriptors ?: emptySet()
if (overriddenDescriptors.isNotEmpty()) {
@@ -85,6 +85,21 @@ fun extractClassMembers(
collectSuperTypeEntries: Boolean = true,
filter: ((KtNamedDeclaration) -> Boolean)? = null
): List<KotlinMemberInfo> {
fun KtClassOrObject.extractFromClassBody(
filter: ((KtNamedDeclaration) -> Boolean)?,
isCompanion: Boolean,
result: MutableCollection<KotlinMemberInfo>
) {
declarations
.filter {
it is KtNamedDeclaration
&& it !is KtConstructor<*>
&& !(it is KtObjectDeclaration && it.isCompanion())
&& (filter == null || filter(it))
}
.mapTo(result) { KotlinMemberInfo(it as KtNamedDeclaration, isCompanionMember = isCompanion) }
}
if (aClass !is KtClassOrObject) return emptyList()
val result = ArrayList<KotlinMemberInfo>()
@@ -98,8 +113,8 @@ fun extractClassMembers(
val classDescriptor = type?.constructor?.declarationDescriptor as? ClassDescriptor
classDescriptor?.source?.getPsi() as? KtClass
}
.filter { it.isInterface() }
.mapTo(result) { KotlinMemberInfo(it, true) }
.filter { it.isInterface() }
.mapTo(result) { KotlinMemberInfo(it, true) }
}
aClass.getPrimaryConstructor()
@@ -107,12 +122,8 @@ fun extractClassMembers(
?.filter { it.hasValOrVar() }
?.mapTo(result) { KotlinMemberInfo(it) }
aClass.declarations
.filter { it is KtNamedDeclaration
&& it !is KtConstructor<*>
&& !(it is KtObjectDeclaration && it.isCompanion())
&& (filter == null || filter(it)) }
.mapTo(result) { KotlinMemberInfo(it as KtNamedDeclaration) }
aClass.extractFromClassBody(filter, false, result)
(aClass as? KtClass)?.getCompanionObjects()?.firstOrNull()?.extractFromClassBody(filter, true, result)
return result
}
@@ -34,7 +34,7 @@ class KotlinMemberSelectionTable(
abstractColumnHeader: String?
) : AbstractMemberSelectionTable<KtNamedDeclaration, KotlinMemberInfo>(memberInfos, memberInfoModel, abstractColumnHeader) {
override fun getAbstractColumnValue(memberInfo: KotlinMemberInfo): Any? {
if (memberInfo.isStatic()) return null
if (memberInfo.isStatic || memberInfo.isCompanionMember) return null
val member = memberInfo.member
if (member !is KtNamedFunction && member !is KtProperty && member !is KtParameter) return null
@@ -28,8 +28,12 @@ import com.intellij.refactoring.classMembers.MemberInfoModel
import com.intellij.refactoring.memberPullUp.PullUpProcessor
import com.intellij.refactoring.util.DocCommentPolicy
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.refactoring.isCompanionMemberOf
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
import org.jetbrains.kotlin.idea.refactoring.memberInfo.*
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable
import org.jetbrains.kotlin.idea.refactoring.memberInfo.toJavaMemberInfo
import org.jetbrains.kotlin.psi.*
class KotlinPullUpDialog(
@@ -62,14 +66,18 @@ class KotlinPullUpDialog(
val superClass = superClass ?: return false
if (superClass is PsiClass) return false
if (superClass !is KtClass) return false
if (!superClass.isInterface()) return true
val member = memberInfo.member
if (member.isCompanionMemberOf(sourceClass)) return false
if (!superClass.isInterface()) return true
return member is KtNamedFunction || (member is KtProperty && !member.mustBeAbstractInInterface()) || member is KtParameter
}
override fun isAbstractWhenDisabled(memberInfo: KotlinMemberInfo): Boolean {
val member = memberInfo.member
if (member.isCompanionMemberOf(sourceClass)) return false
return ((member is KtProperty || member is KtParameter) && superClass !is PsiClass)
|| (member is KtNamedFunction && superClass is PsiClass)
}
@@ -78,7 +86,10 @@ class KotlinPullUpDialog(
val superClass = superClass ?: return false
val member = memberInfo.member
if (superClass is PsiClass && !member.canMoveMemberToJavaClass(superClass)) return false
if (superClass is PsiClass) {
if (!member.canMoveMemberToJavaClass(superClass)) return false
if (member.isCompanionMemberOf(sourceClass)) return false
}
if (memberInfo in memberInfoStorage.getDuplicatedMemberInfos(superClass)) return false
if (member in memberInfoStorage.getExtending(superClass)) return false
return true
@@ -34,10 +34,12 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.core.dropDefaultValue
import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.intentions.setType
import org.jetbrains.kotlin.idea.refactoring.createJavaField
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
import org.jetbrains.kotlin.idea.refactoring.isCompanionMemberOf
import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier
import org.jetbrains.kotlin.idea.util.anonymousObjectSuperTypeOrNull
import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiUnifier
@@ -383,6 +385,9 @@ class KotlinPullUpHelper(
val newType = substitutor.substitute(lightMethod.returnType)
val newField = createJavaField(member, data.targetClass)
newField.typeElement?.replace(elementFactory.createTypeElement(newType))
if (member.isCompanionMemberOf(data.sourceClass)) {
newField.modifierList?.setModifierProperty(PsiModifier.STATIC, true)
}
if (member is KtParameter) {
(member.parent as? KtParameterList)?.removeParameter(member)
}
@@ -461,12 +466,15 @@ class KotlinPullUpHelper(
member is KtProperty -> member.mustBeAbstractInInterface()
else -> false
}
val classToAddTo = if (member.isCompanionMemberOf(data.sourceClass)) data.targetClass.getOrCreateCompanionObject() else data.targetClass
if (toAbstract) {
if (!originalIsAbstract) {
makeAbstract(memberCopy, data.memberDescriptors[member] as CallableMemberDescriptor, data.sourceToTargetClassSubstitutor, data.targetClass)
}
movedMember = doAddCallableMember(memberCopy, clashingSuper, data.targetClass)
movedMember = doAddCallableMember(memberCopy, clashingSuper, classToAddTo)
if (member.typeReference == null) {
movedMember.typeReference?.addToShorteningWaitSet()
}
@@ -474,7 +482,7 @@ class KotlinPullUpHelper(
removeOriginalMemberOrAddOverride(member)
}
else {
movedMember = doAddCallableMember(memberCopy, clashingSuper, data.targetClass)
movedMember = doAddCallableMember(memberCopy, clashingSuper, classToAddTo)
if (member is KtParameter && movedMember is KtParameter) {
member.valOrVarKeyword?.delete()
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.RefactoringBundle
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
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.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
import org.jetbrains.kotlin.resolve.source.getPsi
@@ -109,6 +111,13 @@ private fun KotlinPullUpData.checkClashWithSuperDeclaration(
conflicts.putValue(member, message.capitalize())
}
private fun PsiClass.isSourceOrTarget(data: KotlinPullUpData): Boolean {
var element = unwrapped
if (element is KtObjectDeclaration && element.isCompanion()) element = element.containingClassOrObject
return element == data.sourceClass || element == data.targetClass
}
private fun KotlinPullUpData.checkAccidentalOverrides(
member: KtNamedDeclaration,
memberDescriptor: DeclarationDescriptor,
@@ -119,7 +128,7 @@ private fun KotlinPullUpData.checkAccidentalOverrides(
HierarchySearchRequest<PsiElement>(targetClass, targetClass.useScope)
.searchInheritors()
.asSequence()
.filterNot { it.unwrapped == sourceClass || it.unwrapped == targetClass }
.filterNot { it.isSourceOrTarget(this) }
.mapNotNull { it.unwrapped as? KtClassOrObject }
.forEach {
val subClassDescriptor = resolutionFacade.resolveToDescriptor(it) as ClassDescriptor
@@ -64,18 +64,18 @@ fun addMemberToTarget(targetMember: KtNamedDeclaration, targetClass: KtClassOrOb
return movedMember
}
private fun KtParameter.needToBeAbstract(targetClass: KtClassOrObject): Boolean {
return hasModifier(KtTokens.ABSTRACT_KEYWORD) || targetClass is KtClass && targetClass.isInterface()
}
private fun KtParameter.toProperty(): KtProperty = KtPsiFactory(this).createProperty(text)
fun doAddCallableMember(
memberCopy: KtCallableDeclaration,
clashingSuper: KtCallableDeclaration?,
targetClass: KtClass
targetClass: KtClassOrObject
): KtCallableDeclaration {
val memberToAdd =
if (memberCopy is KtParameter && (memberCopy.hasModifier(KtTokens.ABSTRACT_KEYWORD) || targetClass.isInterface())) {
memberCopy.toProperty()
}
else memberCopy
val memberToAdd = if (memberCopy is KtParameter && memberCopy.needToBeAbstract(targetClass)) memberCopy.toProperty() else memberCopy
if (clashingSuper != null && clashingSuper.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
return clashingSuper.replaced(if (memberToAdd is KtParameter && clashingSuper is KtProperty) memberToAdd.toProperty() else memberToAdd)
@@ -1,6 +1,4 @@
class Class {
companion object {
}
}
fun f() {
@@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull;
abstract class A {
public final int x;
public static final int _x;
public abstract boolean foo(int n);
@@ -1,5 +1,10 @@
// WITH_RUNTIME
abstract class <caret>B: A() {
companion object {
// INFO: {"checked": "true"}
val _x = 1
}
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
@@ -1,5 +1,6 @@
// WITH_RUNTIME
abstract class B: A() {
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
@@ -2,6 +2,14 @@
open class A
abstract class <caret>B: A() {
companion object {
// INFO: {"checked": "true"}
val _x = 1
// INFO: {"checked": "true"}
fun _foo(n: Int): Boolean = n > 0
}
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
@@ -24,6 +24,14 @@ abstract class A {
class Y {
}
companion object {
// INFO: {"checked": "true"}
val _x = 1
// INFO: {"checked": "true"}
fun _foo(n: Int): Boolean = n > 0
}
}
abstract class B: A() {
@@ -2,6 +2,14 @@
interface T
abstract class <caret>B: T {
companion object {
// INFO: {"checked": "true"}
val _x = 1
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
}
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
@@ -19,9 +19,23 @@ interface T {
class Y {
}
companion object {
// INFO: {"checked": "true"}
val _x: Int
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
}
}
abstract class B: T {
companion object {
// INFO: {"checked": "true"}
override val _x = 1
}
// INFO: {"checked": "true"}
override val x = 1
// INFO: {"checked": "true"}