Pull Up: Support super-interfaces

This commit is contained in:
Alexey Sedunov
2015-07-20 21:19:45 +03:00
parent 1a09741c0a
commit 7dc430e50b
19 changed files with 266 additions and 19 deletions
@@ -39,6 +39,29 @@ abstract public class JetClassOrObject : JetTypeParameterListOwnerStub<KotlinCla
public fun getDelegationSpecifierList(): JetDelegationSpecifierList? = getStubOrPsiChild(JetStubElementTypes.DELEGATION_SPECIFIER_LIST)
open public fun getDelegationSpecifiers(): List<JetDelegationSpecifier> = getDelegationSpecifierList()?.getDelegationSpecifiers().orEmpty()
public fun addDelegationSpecifier(delegationSpecifier: JetDelegationSpecifier): JetDelegationSpecifier {
getDelegationSpecifierList()?.let {
return EditCommaSeparatedListHelper.addItem(it, getDelegationSpecifiers(), delegationSpecifier)
}
val psiFactory = JetPsiFactory(this)
val specifierListToAdd = psiFactory.createDelegatorToSuperCall("A()").replace(delegationSpecifier).getParent()
val colon = addBefore(psiFactory.createColon(), getBody())
return (addAfter(specifierListToAdd, colon) as JetDelegationSpecifierList).getDelegationSpecifiers().first()
}
public fun removeDelegationSpecifier(delegationSpecifier: JetDelegationSpecifier) {
val specifierList = getDelegationSpecifierList() ?: return
assert(delegationSpecifier.getParent() === specifierList)
if (specifierList.getDelegationSpecifiers().size() > 1) {
EditCommaSeparatedListHelper.removeItem<JetElement>(delegationSpecifier)
}
else {
deleteChildRange(findChildByType<PsiElement>(JetTokens.COLON) ?: specifierList, specifierList)
}
}
public fun getAnonymousInitializers(): List<JetClassInitializer> = getBody()?.getAnonymousInitializers().orEmpty()
public fun getNameAsDeclaration(): JetObjectDeclarationName? =
@@ -378,6 +378,10 @@ public class JetPsiFactory(private val project: Project) {
return createClass("class A: $text").getDelegationSpecifiers().first() as JetDelegatorToSuperCall
}
public fun createDelegatorToSuperClass(text: String): JetDelegatorToSuperClass {
return createClass("class A: $text").getDelegationSpecifiers().first() as JetDelegatorToSuperClass
}
public fun createConstructorDelegationCall(text: String): JetConstructorDelegationCall {
val colonOrEmpty = if (text.isEmpty()) "" else ": "
return createClass("class A { constructor()$colonOrEmpty$text {}").getSecondaryConstructors().first().getDelegationCall()
@@ -17,25 +17,40 @@
package org.jetbrains.kotlin.idea.refactoring.memberInfo
import com.intellij.psi.PsiMember
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.classMembers.MemberInfoBase
import com.intellij.refactoring.util.classMembers.MemberInfo
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.*
public class KotlinMemberInfo(member: JetNamedDeclaration) : MemberInfoBase<JetNamedDeclaration>(member) {
public class KotlinMemberInfo(member: JetNamedDeclaration, val isSuperClass: Boolean = false) : MemberInfoBase<JetNamedDeclaration>(member) {
init {
val memberDescriptor = member.resolveToDescriptor()
isStatic = member.getParent() is JetFile
displayName = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(memberDescriptor)
val overriddenDescriptors = (memberDescriptor as? CallableMemberDescriptor)?.getOverriddenDescriptors() ?: emptySet()
if (overriddenDescriptors.isNotEmpty()) {
overrides = overriddenDescriptors.any { it.getModality() != Modality.ABSTRACT }
if (member is JetClass && isSuperClass) {
if (member.isInterface()) {
displayName = RefactoringBundle.message("member.info.implements.0", member.getName())
overrides = false
}
else {
displayName = RefactoringBundle.message("member.info.extends.0", member.getName())
overrides = true
}
}
else {
displayName = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(memberDescriptor)
val overriddenDescriptors = (memberDescriptor as? CallableMemberDescriptor)?.getOverriddenDescriptors() ?: emptySet()
if (overriddenDescriptors.isNotEmpty()) {
overrides = overriddenDescriptors.any { it.getModality() != Modality.ABSTRACT }
}
}
}
}
@@ -49,7 +64,7 @@ public fun KotlinMemberInfo.toJavaMemberInfo(): MemberInfo? {
}
if (psiMember == null) return null
val info = MemberInfo(psiMember)
val info = MemberInfo(psiMember, isSuperClass, null)
info.setToAbstract(isToAbstract())
return info
}
@@ -18,14 +18,14 @@ package org.jetbrains.kotlin.idea.refactoring.memberInfo
import com.intellij.refactoring.classMembers.AbstractMemberInfoStorage
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetConstructor
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.JetObjectDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverloadUtil
import org.jetbrains.kotlin.resolve.source.getPsi
@@ -69,11 +69,21 @@ public class KotlinMemberInfoStorage(
}
override fun extractClassMembers(aClass: JetClassOrObject, temp: ArrayList<KotlinMemberInfo>) {
val context = aClass.analyze()
aClass.getDeclarations()
.filter { it is JetNamedDeclaration
&& it !is JetConstructor<*>
&& !(it is JetObjectDeclaration && it.isCompanion())
&& myFilter.includeMember(it) }
.mapTo(temp) { KotlinMemberInfo(it as JetNamedDeclaration) }
aClass.getDelegationSpecifiers()
.filterIsInstance<JetDelegatorToSuperClass>()
.map {
val type = context[BindingContext.TYPE, it.getTypeReference()]
val classDescriptor = type?.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
classDescriptor?.getSource()?.getPsi() as? JetClass
}
.filter { it != null && it.isInterface() }
.mapTo(temp) { KotlinMemberInfo(it!!, true) }
}
}
@@ -160,7 +160,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
new Function1<JetNamedDeclaration, KotlinMemberInfo>() {
@Override
public KotlinMemberInfo invoke(JetNamedDeclaration declaration) {
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration);
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
memberInfo.setChecked(elementsToMove.contains(declaration));
return memberInfo;
}
@@ -69,6 +69,7 @@ public class KotlinPullUpDialog(
override fun isMemberEnabled(memberInfo: KotlinMemberInfo): Boolean {
val superClass = getSuperClass() ?: return false
if (memberInfo in memberInfoStorage.getDuplicatedMemberInfos(superClass)) return false
if (memberInfo.getMember() in memberInfoStorage.getExtending(superClass)) return false
return true
}
}
@@ -36,11 +36,9 @@ import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedNameForRendering
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.typeUtil.supertypes
import java.util.HashSet
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.lang.Language
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightField
@@ -26,8 +25,9 @@ import com.intellij.refactoring.classMembers.MemberInfoBase
import com.intellij.refactoring.memberPullUp.PullUpData
import com.intellij.refactoring.memberPullUp.PullUpHelper
import com.intellij.refactoring.util.RefactoringUtil
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.*
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.JetLanguage
@@ -52,9 +52,10 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.keysToMap
import java.util.*
import java.util.ArrayList
import java.util.Comparator
import java.util.LinkedHashMap
import java.util.LinkedHashSet
class KotlinPullUpHelper(
private val javaData: PullUpData,
@@ -510,6 +511,35 @@ class KotlinPullUpHelper(
override fun move(info: MemberInfoBase<PsiMember>, substitutor: PsiSubstitutor) {
val member = info.getMember().namedUnwrappedElement as? JetNamedDeclaration ?: return
if (member is JetClass && info.getOverrides() != null) {
val psiFactory = JetPsiFactory(member)
val classDescriptor = data.memberDescriptors[member] as? ClassDescriptor ?: return
val currentSpecifier =
data.sourceClass.getDelegationSpecifiers()
.filterIsInstance<JetDelegatorToSuperClass>()
.firstOrNull {
val referencedType = data.sourceClassContext[BindingContext.TYPE, it.getTypeReference()]
referencedType?.getConstructor()?.getDeclarationDescriptor() == classDescriptor
} ?: return
data.sourceClass.removeDelegationSpecifier(currentSpecifier)
if (!DescriptorUtils.isSubclass(data.targetClassDescriptor, classDescriptor)) {
val referencedType = data.sourceClassContext[BindingContext.TYPE, currentSpecifier.getTypeReference()]!!
val typeInTargetClass = data.sourceToTargetClassSubstitutor.substitute(referencedType, Variance.INVARIANT)
if (typeInTargetClass != null && !typeInTargetClass.isError) {
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass)
data.targetClass.addDelegationSpecifier(psiFactory.createDelegatorToSuperClass(renderedType)).addToShorteningWaitSet()
}
}
return
}
val markedElements = markElements(member)
val memberCopy = member.copy() as JetNamedDeclaration
@@ -0,0 +1,14 @@
open class A: Z {
}
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "true"}
interface Y
// INFO: {"checked": "true"}
interface Z
class <caret>B: A(), X, Y, Z
@@ -0,0 +1,14 @@
open class A: Z, X, Y {
}
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "true"}
interface Y
// INFO: {"checked": "true"}
interface Z
class B: A()
@@ -0,0 +1,14 @@
open class A: Z<String> {
}
// INFO: {"checked": "true"}
interface X<T, U>
// INFO: {"checked": "true"}
interface Y<V : Any>
// INFO: {"checked": "true"}
interface Z<T>
class <caret>B<T : Any, U, V>: A(), X<U, V>, Y<T>, Z<U>
@@ -0,0 +1,14 @@
open class A: Z<String>, X<Any?, Any?>, Y<Any> {
}
// INFO: {"checked": "true"}
interface X<T, U>
// INFO: {"checked": "true"}
interface Y<V : Any>
// INFO: {"checked": "true"}
interface Z<T>
class B<T : Any, U, V>: A()
@@ -0,0 +1,12 @@
// TARGET_CLASS: X
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class <caret>B: X, Y, Z
@@ -0,0 +1,12 @@
// TARGET_CLASS: X
// INFO: {"checked": "true"}
interface X : Z
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class B: Y
+14
View File
@@ -0,0 +1,14 @@
open class A: Z {
}
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class <caret>B: A(), X, Y, Z
@@ -0,0 +1,14 @@
open class A: Z, X {
}
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class B: A(), Y
@@ -0,0 +1,14 @@
open class A {
}
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class <caret>B: A(), X, Y, Z
@@ -0,0 +1,14 @@
open class A : X, Z {
}
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class B: A(), Y
@@ -113,6 +113,36 @@ public class PullUpTestGenerated extends AbstractPullUpTest {
doTest(fileName);
}
@TestMetadata("moveAllSuperInterfaces.kt")
public void testMoveAllSuperInterfaces() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveAllSuperInterfaces.kt");
doTest(fileName);
}
@TestMetadata("moveAllSuperInterfacesWithGenerics.kt")
public void testMoveAllSuperInterfacesWithGenerics() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveAllSuperInterfacesWithGenerics.kt");
doTest(fileName);
}
@TestMetadata("moveSuperInterfaces.kt")
public void testMoveSuperInterfaces() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveSuperInterfaces.kt");
doTest(fileName);
}
@TestMetadata("moveSuperInterfacesToEmptySpecifierList.kt")
public void testMoveSuperInterfacesToEmptySpecifierList() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveSuperInterfacesToEmptySpecifierList.kt");
doTest(fileName);
}
@TestMetadata("moveSuperInterfaceToItSelf.kt")
public void testMoveSuperInterfaceToItSelf() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/moveSuperInterfaceToItSelf.kt");
doTest(fileName);
}
@TestMetadata("multipleInitializersInConstructorsEq.kt")
public void testMultipleInitializersInConstructorsEq() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt");