Pull Up: Kotlin -> Java interoperability

This commit is contained in:
Alexey Sedunov
2015-08-04 15:02:50 +03:00
committed by Alexey Sedunov
parent ffc56a7c31
commit a9783ffe81
55 changed files with 733 additions and 91 deletions
@@ -462,6 +462,9 @@ public fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMeth
val method = targetClass.add(methodToAdd) as PsiMethod val method = targetClass.add(methodToAdd) as PsiMethod
copyModifierListItems(template.getModifierList(), method.getModifierList()) copyModifierListItems(template.getModifierList(), method.getModifierList())
if (targetClass.isInterface()) {
method.getModifierList().setModifierProperty(PsiModifier.FINAL, false)
}
copyTypeParameters(template, method) { method, typeParameterList -> copyTypeParameters(template, method) { method, typeParameterList ->
method.addAfter(typeParameterList, method.getModifierList()) method.addAfter(typeParameterList, method.getModifierList())
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.idea.refactoring.memberInfo package org.jetbrains.kotlin.idea.refactoring.memberInfo
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.classMembers.AbstractMemberInfoStorage import com.intellij.refactoring.classMembers.AbstractMemberInfoStorage
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -31,9 +33,9 @@ import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import java.util.ArrayList import java.util.ArrayList
public class KotlinMemberInfoStorage( public class KotlinMemberInfoStorage(
classOrObject: JetClassOrObject, classOrObject: PsiNamedElement,
filter: (JetNamedDeclaration) -> Boolean = { true } filter: (JetNamedDeclaration) -> Boolean = { true }
): AbstractMemberInfoStorage<JetNamedDeclaration, JetClassOrObject, KotlinMemberInfo>(classOrObject, filter) { ): AbstractMemberInfoStorage<JetNamedDeclaration, PsiNamedElement, KotlinMemberInfo>(classOrObject, filter) {
override fun memberConflict(member1: JetNamedDeclaration, member: JetNamedDeclaration): Boolean { override fun memberConflict(member1: JetNamedDeclaration, member: JetNamedDeclaration): Boolean {
val descriptor1 = member1.resolveToDescriptor() val descriptor1 = member1.resolveToDescriptor()
val descriptor = member.resolveToDescriptor() val descriptor = member.resolveToDescriptor()
@@ -49,24 +51,27 @@ public class KotlinMemberInfoStorage(
} }
} }
override fun buildSubClassesMap(aClass: JetClassOrObject) { override fun buildSubClassesMap(aClass: PsiNamedElement) {
val classDescriptor = aClass.resolveToDescriptor() as ClassDescriptor val classDescriptor = aClass.getClassDescriptorIfAny() ?: return
val classType = classDescriptor.defaultType val classType = classDescriptor.defaultType
for (supertype in classType.immediateSupertypes()) { for (supertype in classType.immediateSupertypes()) {
val superClass = supertype.constructor.declarationDescriptor?.source?.getPsi() as? JetClassOrObject val superClass = supertype.constructor.declarationDescriptor?.source?.getPsi()
?: continue if (superClass is JetClass || superClass is PsiClass) {
getSubclasses(superClass).add(aClass) getSubclasses(superClass as PsiNamedElement).add(aClass)
buildSubClassesMap(superClass) buildSubClassesMap(superClass)
}
} }
} }
override fun isInheritor(baseClass: JetClassOrObject, aClass: JetClassOrObject): Boolean { override fun isInheritor(baseClass: PsiNamedElement, aClass: PsiNamedElement): Boolean {
val baseDescriptor = baseClass.resolveToDescriptor() as ClassDescriptor val baseDescriptor = baseClass.getClassDescriptorIfAny() ?: return false
val currentDescriptor = aClass.resolveToDescriptor() as ClassDescriptor val currentDescriptor = aClass.getClassDescriptorIfAny() ?: return false
return DescriptorUtils.isSubclass(currentDescriptor, baseDescriptor) return DescriptorUtils.isSubclass(currentDescriptor, baseDescriptor)
} }
override fun extractClassMembers(aClass: JetClassOrObject, temp: ArrayList<KotlinMemberInfo>) { override fun extractClassMembers(aClass: PsiNamedElement, temp: ArrayList<KotlinMemberInfo>) {
if (aClass !is JetClassOrObject) return
val context = aClass.analyze() val context = aClass.analyze()
aClass.declarations aClass.declarations
.filter { it is JetNamedDeclaration .filter { it is JetNamedDeclaration
@@ -17,17 +17,17 @@
package org.jetbrains.kotlin.idea.refactoring.memberInfo; package org.jetbrains.kotlin.idea.refactoring.memberInfo;
import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.Iconable;
import com.intellij.psi.PsiNamedElement;
import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.ListCellRendererWrapper;
import org.jetbrains.kotlin.psi.JetClassOrObject;
import javax.swing.*; import javax.swing.*;
public class JetClassOrObjectListCellRenderer extends ListCellRendererWrapper<JetClassOrObject> { public class KotlinOrJavaClassCellRenderer extends ListCellRendererWrapper<PsiNamedElement> {
@Override @Override
public void customize(JList list, JetClassOrObject value, int index, boolean selected, boolean hasFocus) { public void customize(JList list, PsiNamedElement value, int index, boolean selected, boolean hasFocus) {
if (value == null) return; if (value == null) return;
setText(MemberInfoPackage.qualifiedNameForRendering(value)); setText(MemberInfoPackage.qualifiedClassNameForRendering(value));
Icon icon = value.getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS); Icon icon = value.getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS);
if (icon != null) { if (icon != null) {
setIcon(icon); setIcon(icon);
@@ -1,21 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.memberInfo
import org.jetbrains.kotlin.psi.JetClassOrObject
public fun JetClassOrObject.qualifiedNameForRendering(): String = fqName?.asString() ?: name ?: "[Anonymous]"
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.memberInfo
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMember
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiNamedElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
fun PsiNamedElement.getClassDescriptorIfAny(resolutionFacade: ResolutionFacade? = null): ClassDescriptor? {
return when (this) {
is JetClass -> (resolutionFacade ?: getResolutionFacade()).resolveToDescriptor(this) as ClassDescriptor
is PsiClass -> getJavaClassDescriptor()
else -> null
}
}
fun PsiNamedElement.isAbstractMember(): Boolean {
return when(this) {
is JetNamedDeclaration -> hasModifier(JetTokens.ABSTRACT_KEYWORD)
is PsiMember -> hasModifierProperty(PsiModifier.ABSTRACT)
else -> false
}
}
// Applies to JetClassOrObject and PsiClass
public fun PsiNamedElement.qualifiedClassNameForRendering(): String {
val fqName = when (this) {
is JetClassOrObject -> fqName?.asString()
is PsiClass -> qualifiedName
else -> throw AssertionError("Not a class: ${getElementTextWithContext()}")
}
return fqName ?: name ?: "[Anonymous]"
}
@@ -16,11 +16,13 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.psi.PsiNamedElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.getResolutionScope import org.jetbrains.kotlin.idea.core.getResolutionScope
import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.idea.refactoring.memberInfo.getClassDescriptorIfAny
import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -34,7 +36,7 @@ import org.jetbrains.kotlin.utils.keysToMap
import java.util.LinkedHashMap import java.util.LinkedHashMap
class KotlinPullUpData(val sourceClass: JetClassOrObject, class KotlinPullUpData(val sourceClass: JetClassOrObject,
val targetClass: JetClass, val targetClass: PsiNamedElement,
val membersToMove: Collection<JetNamedDeclaration>) { val membersToMove: Collection<JetNamedDeclaration>) {
val resolutionFacade = sourceClass.getResolutionFacade() val resolutionFacade = sourceClass.getResolutionFacade()
@@ -44,7 +46,7 @@ class KotlinPullUpData(val sourceClass: JetClassOrObject,
val memberDescriptors = membersToMove.keysToMap { sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]!! } val memberDescriptors = membersToMove.keysToMap { sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]!! }
val targetClassDescriptor = resolutionFacade.resolveToDescriptor(targetClass) as ClassDescriptor val targetClassDescriptor = targetClass.getClassDescriptorIfAny(resolutionFacade)!!
val typeParametersInSourceClassContext by lazy { val typeParametersInSourceClassContext by lazy {
sourceClassDescriptor.typeConstructor.parameters + sourceClass.getResolutionScope(sourceClassContext, resolutionFacade) sourceClassDescriptor.typeConstructor.parameters + sourceClass.getResolutionScope(sourceClassContext, resolutionFacade)
@@ -71,4 +73,6 @@ class KotlinPullUpData(val sourceClass: JetClassOrObject,
TypeSubstitutor.create(substitution) TypeSubstitutor.create(substitution)
} }
val isInterfaceTarget: Boolean = targetClassDescriptor.kind == ClassKind.INTERFACE
} }
@@ -18,7 +18,9 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.ui.DialogWrapper
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel import com.intellij.refactoring.classMembers.AbstractMemberInfoModel
import com.intellij.refactoring.memberPullUp.PullUpProcessor import com.intellij.refactoring.memberPullUp.PullUpProcessor
@@ -30,7 +32,7 @@ import org.jetbrains.kotlin.psi.*
public class KotlinPullUpDialog( public class KotlinPullUpDialog(
project: Project, project: Project,
private val classOrObject: JetClassOrObject, private val classOrObject: JetClassOrObject,
superClasses: List<JetClass>, superClasses: List<PsiNamedElement>,
memberInfoStorage: KotlinMemberInfoStorage memberInfoStorage: KotlinMemberInfoStorage
) : KotlinPullUpDialogBase( ) : KotlinPullUpDialogBase(
project, classOrObject, superClasses, memberInfoStorage, PULL_MEMBERS_UP project, classOrObject, superClasses, memberInfoStorage, PULL_MEMBERS_UP
@@ -53,6 +55,7 @@ public class KotlinPullUpDialog(
*/ */
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean { override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
val superClass = superClass ?: return false val superClass = superClass ?: return false
if (superClass is PsiClass) return false
if (!superClass.isInterface()) return true if (!superClass.isInterface()) return true
val member = memberInfo.member val member = memberInfo.member
@@ -60,20 +63,24 @@ public class KotlinPullUpDialog(
} }
override fun isAbstractWhenDisabled(memberInfo: KotlinMemberInfo): Boolean { override fun isAbstractWhenDisabled(memberInfo: KotlinMemberInfo): Boolean {
return memberInfo.member is JetProperty val member = memberInfo.member
return (member is JetProperty && superClass !is PsiClass) || (member is JetNamedFunction && superClass is PsiClass)
} }
override fun isMemberEnabled(memberInfo: KotlinMemberInfo): Boolean { override fun isMemberEnabled(memberInfo: KotlinMemberInfo): Boolean {
val superClass = superClass ?: return false val superClass = superClass ?: return false
val member = memberInfo.member
if (superClass is PsiClass && !member.canMoveMemberToJavaClass(superClass)) return false
if (memberInfo in memberInfoStorage.getDuplicatedMemberInfos(superClass)) return false if (memberInfo in memberInfoStorage.getDuplicatedMemberInfos(superClass)) return false
if (memberInfo.member in memberInfoStorage.getExtending(superClass)) return false if (member in memberInfoStorage.getExtending(superClass)) return false
return true return true
} }
} }
protected val memberInfoStorage: KotlinMemberInfoStorage get() = myMemberInfoStorage protected val memberInfoStorage: KotlinMemberInfoStorage get() = myMemberInfoStorage
protected val sourceClass: JetClassOrObject get() = myClass protected val sourceClass: JetClassOrObject get() = myClass as JetClassOrObject
override fun getDimensionServiceKey() = "#" + javaClass.name override fun getDimensionServiceKey() = "#" + javaClass.name
@@ -96,10 +103,11 @@ public class KotlinPullUpDialog(
companion object { companion object {
fun createProcessor(sourceClass: JetClassOrObject, fun createProcessor(sourceClass: JetClassOrObject,
targetClass: JetClass, targetClass: PsiNamedElement,
memberInfos: List<KotlinMemberInfo>): PullUpProcessor { memberInfos: List<KotlinMemberInfo>): PullUpProcessor {
val targetPsiClass = targetClass as? PsiClass ?: (targetClass as JetClass).toLightClass()
return PullUpProcessor(sourceClass.toLightClass(), return PullUpProcessor(sourceClass.toLightClass(),
targetClass.toLightClass(), targetPsiClass,
memberInfos.map { it.toJavaMemberInfo() }.filterNotNull().toTypedArray(), memberInfos.map { it.toJavaMemberInfo() }.filterNotNull().toTypedArray(),
DocCommentPolicy<PsiComment>(JavaRefactoringSettings.getInstance().PULL_UP_MEMBERS_JAVADOC)) DocCommentPolicy<PsiComment>(JavaRefactoringSettings.getInstance().PULL_UP_MEMBERS_JAVADOC))
} }
@@ -17,9 +17,10 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp; package org.jetbrains.kotlin.idea.refactoring.pullUp;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiNamedElement;
import com.intellij.refactoring.memberPullUp.PullUpDialogBase; import com.intellij.refactoring.memberPullUp.PullUpDialogBase;
import com.intellij.refactoring.ui.AbstractMemberSelectionTable; import com.intellij.refactoring.ui.AbstractMemberSelectionTable;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.JetClassOrObjectListCellRenderer; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinOrJavaClassCellRenderer;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage;
import org.jetbrains.kotlin.psi.JetClassOrObject; import org.jetbrains.kotlin.psi.JetClassOrObject;
@@ -32,11 +33,11 @@ import java.util.List;
// TODO: This is workaround which allows KotlinPullUpDialog to be compiled against both Java 6 and 8 // TODO: This is workaround which allows KotlinPullUpDialog to be compiled against both Java 6 and 8
public abstract class KotlinPullUpDialogBase extends public abstract class KotlinPullUpDialogBase extends
PullUpDialogBase<KotlinMemberInfoStorage, KotlinMemberInfo, JetNamedDeclaration, JetClassOrObject> { PullUpDialogBase<KotlinMemberInfoStorage, KotlinMemberInfo, JetNamedDeclaration, PsiNamedElement> {
protected KotlinPullUpDialogBase( protected KotlinPullUpDialogBase(
Project project, Project project,
JetClassOrObject object, JetClassOrObject object,
List<JetClassOrObject> superClasses, List<PsiNamedElement> superClasses,
KotlinMemberInfoStorage memberInfoStorage, KotlinMemberInfoStorage memberInfoStorage,
String title String title
) { ) {
@@ -46,7 +47,7 @@ public abstract class KotlinPullUpDialogBase extends
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected void initClassCombo(JComboBox classCombo) { protected void initClassCombo(JComboBox classCombo) {
classCombo.setRenderer(new JetClassOrObjectListCellRenderer()); classCombo.setRenderer(new KotlinOrJavaClassCellRenderer());
classCombo.addItemListener( classCombo.addItemListener(
new ItemListener() { new ItemListener() {
@Override @Override
@@ -22,8 +22,10 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.HelpID import com.intellij.refactoring.HelpID
import com.intellij.refactoring.RefactoringActionHandler import com.intellij.refactoring.RefactoringActionHandler
import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.RefactoringBundle
@@ -35,7 +37,7 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedNameForRendering import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -49,7 +51,7 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
interface TestHelper { interface TestHelper {
fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo> fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo>
fun chooseSuperClass(superClasses: List<JetClass>): JetClass fun chooseSuperClass(superClasses: List<PsiNamedElement>): PsiNamedElement
} }
private fun reportWrongPosition(project: Project, editor: Editor?) { private fun reportWrongPosition(project: Project, editor: Editor?) {
@@ -69,7 +71,7 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
private fun reportNoSuperClasses(project: Project, editor: Editor?, classOrObject: JetClassOrObject) { private fun reportNoSuperClasses(project: Project, editor: Editor?, classOrObject: JetClassOrObject) {
val message = RefactoringBundle.getCannotRefactorMessage( val message = RefactoringBundle.getCannotRefactorMessage(
RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project", RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project",
classOrObject.qualifiedNameForRendering()) classOrObject.qualifiedClassNameForRendering())
) )
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP) CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
} }
@@ -125,10 +127,11 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
.map { .map {
val descriptor = it.constructor.declarationDescriptor val descriptor = it.constructor.declarationDescriptor
val declaration = descriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) } val declaration = descriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
if (declaration is JetClass && declaration.canRefactor()) declaration else null if ((declaration is JetClass || declaration is PsiClass)
&& declaration.canRefactor()) declaration as PsiNamedElement else null
} }
.filterNotNull() .filterNotNull()
.toSortedListBy { it.qualifiedNameForRendering() } .toSortedListBy { it.qualifiedClassNameForRendering() }
if (superClasses.isEmpty()) { if (superClasses.isEmpty()) {
val containingClass = classOrObject.getStrictParentOfType<JetClassOrObject>() val containingClass = classOrObject.getStrictParentOfType<JetClassOrObject>()
@@ -18,7 +18,9 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.impl.light.LightField import com.intellij.psi.impl.light.LightField
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.classMembers.MemberInfoBase import com.intellij.refactoring.classMembers.MemberInfoBase
@@ -28,10 +30,13 @@ import com.intellij.refactoring.util.RefactoringUtil
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.JetLanguage import org.jetbrains.kotlin.idea.JetLanguage
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.core.refactoring.createJavaField
import org.jetbrains.kotlin.idea.core.refactoring.createJavaMethod
import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier
@@ -175,7 +180,7 @@ class KotlinPullUpHelper(
} }
} }
commonInitializer?.accept(visitor) commonInitializer?.accept(visitor)
if (targetConstructor == data.targetClass.getPrimaryConstructor() ?: data.targetClass) { if (targetConstructor == (data.targetClass as? JetClass)?.getPrimaryConstructor() ?: data.targetClass) {
property.initializer?.accept(visitor) property.initializer?.accept(visitor)
} }
@@ -192,7 +197,7 @@ class KotlinPullUpHelper(
} }
private val targetToSourceConstructors = LinkedHashMap<JetElement, MutableList<JetElement>>().let { result -> private val targetToSourceConstructors = LinkedHashMap<JetElement, MutableList<JetElement>>().let { result ->
if (!data.targetClass.isInterface()) { if (!data.isInterfaceTarget && data.targetClass is JetClass) {
result[data.targetClass.getPrimaryConstructor() ?: data.targetClass] = ArrayList<JetElement>() result[data.targetClass.getPrimaryConstructor() ?: data.targetClass] = ArrayList<JetElement>()
data.sourceClass.accept( data.sourceClass.accept(
object : JetTreeVisitorVoid() { object : JetTreeVisitorVoid() {
@@ -287,7 +292,7 @@ class KotlinPullUpHelper(
override fun setCorrectVisibility(info: MemberInfoBase<PsiMember>) { override fun setCorrectVisibility(info: MemberInfoBase<PsiMember>) {
val member = info.member.namedUnwrappedElement as? JetNamedDeclaration ?: return val member = info.member.namedUnwrappedElement as? JetNamedDeclaration ?: return
if (data.targetClass.isInterface()) { if (data.isInterfaceTarget) {
member.removeModifier(JetTokens.PUBLIC_KEYWORD) member.removeModifier(JetTokens.PUBLIC_KEYWORD)
return return
} }
@@ -332,7 +337,7 @@ class KotlinPullUpHelper(
} }
private fun makeAbstract(sourceMember: JetCallableDeclaration, targetMember: JetCallableDeclaration) { private fun makeAbstract(sourceMember: JetCallableDeclaration, targetMember: JetCallableDeclaration) {
if (!data.targetClass.isInterface()) { if (!data.isInterfaceTarget) {
targetMember.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD) targetMember.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
} }
@@ -373,6 +378,8 @@ class KotlinPullUpHelper(
} }
private fun addMemberToTarget(targetMember: JetNamedDeclaration): JetNamedDeclaration { private fun addMemberToTarget(targetMember: JetNamedDeclaration): JetNamedDeclaration {
data.targetClass as JetClass
val anchor = data.targetClass.declarations.filterIsInstance(targetMember.javaClass).lastOrNull() val anchor = data.targetClass.declarations.filterIsInstance(targetMember.javaClass).lastOrNull()
val movedMember = when { val movedMember = when {
anchor == null && targetMember is JetProperty -> data.targetClass.addDeclarationBefore(targetMember, null) anchor == null && targetMember is JetProperty -> data.targetClass.addDeclarationBefore(targetMember, null)
@@ -509,11 +516,8 @@ class KotlinPullUpHelper(
addAfter(JetPsiFactory(this).createWhiteSpace(), modifierList) addAfter(JetPsiFactory(this).createWhiteSpace(), modifierList)
} }
private fun moveSuperInterface(member: JetClass) { private fun moveSuperInterface(member: JetClass, substitutor: PsiSubstitutor) {
val psiFactory = JetPsiFactory(member)
val classDescriptor = data.memberDescriptors[member] as? ClassDescriptor ?: return val classDescriptor = data.memberDescriptors[member] as? ClassDescriptor ?: return
val currentSpecifier = val currentSpecifier =
data.sourceClass.getDelegationSpecifiers() data.sourceClass.getDelegationSpecifiers()
.filterIsInstance<JetDelegatorToSuperClass>() .filterIsInstance<JetDelegatorToSuperClass>()
@@ -521,48 +525,131 @@ class KotlinPullUpHelper(
val referencedType = data.sourceClassContext[BindingContext.TYPE, it.typeReference] val referencedType = data.sourceClassContext[BindingContext.TYPE, it.typeReference]
referencedType?.constructor?.declarationDescriptor == classDescriptor referencedType?.constructor?.declarationDescriptor == classDescriptor
} ?: return } ?: return
data.sourceClass.removeDelegationSpecifier(currentSpecifier) when (data.targetClass) {
is JetClass -> {
data.sourceClass.removeDelegationSpecifier(currentSpecifier)
if (DescriptorUtils.isSubclass(data.targetClassDescriptor, classDescriptor)) return
val referencedType = data.sourceClassContext[BindingContext.TYPE, currentSpecifier.typeReference]!!
val typeInTargetClass = data.sourceToTargetClassSubstitutor.substitute(referencedType, Variance.INVARIANT)
if (!(typeInTargetClass != null && !typeInTargetClass.isError)) return
if (!DescriptorUtils.isSubclass(data.targetClassDescriptor, classDescriptor)) {
val referencedType = data.sourceClassContext[BindingContext.TYPE, currentSpecifier.typeReference]!!
val typeInTargetClass = data.sourceToTargetClassSubstitutor.substitute(referencedType, Variance.INVARIANT)
if (typeInTargetClass != null && !typeInTargetClass.isError) {
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass) val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass)
data.targetClass.addDelegationSpecifier(psiFactory.createDelegatorToSuperClass(renderedType)).addToShorteningWaitSet() val newSpecifier = JetPsiFactory(member).createDelegatorToSuperClass(renderedType)
data.targetClass.addDelegationSpecifier(newSpecifier).addToShorteningWaitSet()
}
is PsiClass -> {
val elementFactory = JavaPsiFacade.getElementFactory(member.project)
val sourcePsiClass = data.sourceClass.toLightClass() ?: return
val superRef = sourcePsiClass.implementsList
?.referenceElements
?.firstOrNull { it.resolve()?.unwrapped == member }
?: return
val superTypeForTarget = substitutor.substitute(elementFactory.createType(superRef))
data.sourceClass.removeDelegationSpecifier(currentSpecifier)
if (DescriptorUtils.isSubclass(data.targetClassDescriptor, classDescriptor)) return
val refList = if (data.isInterfaceTarget) data.targetClass.extendsList else data.targetClass.implementsList
refList?.add(elementFactory.createReferenceFromText(superTypeForTarget.canonicalText, null))
} }
} }
return return
} }
private fun removeOriginalMemberOrAddOverride(member: JetCallableDeclaration) {
if (member.hasModifier(JetTokens.ABSTRACT_KEYWORD)) {
member.delete()
}
else if (!member.hasModifier(JetTokens.OVERRIDE_KEYWORD)) {
member.addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
}
}
private fun moveToJavaClass(member: JetNamedDeclaration, substitutor: PsiSubstitutor) {
if (!(data.targetClass is PsiClass && member.canMoveMemberToJavaClass(data.targetClass))) return
val project = member.project
val lightMethod = member.getRepresentativeLightMethod() ?: return
val elementFactory = JavaPsiFacade.getElementFactory(project)
val movedMember: PsiMember = when (member) {
is JetProperty -> {
val newType = substitutor.substitute(lightMethod.returnType)
val newField = createJavaField(member, data.targetClass)
newField.typeElement?.replace(elementFactory.createTypeElement(newType))
member.delete()
newField
}
is JetNamedFunction -> {
val newReturnType = substitutor.substitute(lightMethod.returnType)
val newParameterTypes = lightMethod.parameterList.parameters.map { substitutor.substitute(it.type) }
val objectType = PsiType.getJavaLangObject(PsiManager.getInstance(project), GlobalSearchScope.allScope(project))
val newTypeParameterBounds = lightMethod.typeParameters.map {
it.superTypes.map { substitutor.substitute(it) as? PsiClassType ?: objectType }
}
val newMethod = createJavaMethod(member, data.targetClass)
RefactoringUtil.makeMethodAbstract(data.targetClass, newMethod)
newMethod.returnTypeElement?.replace(elementFactory.createTypeElement(newReturnType))
newMethod.parameterList.parameters.forEachIndexed { i, parameter ->
parameter.typeElement?.replace(elementFactory.createTypeElement(newParameterTypes[i]))
}
newMethod.typeParameters.forEachIndexed { i, typeParameter ->
typeParameter.extendsList.referenceElements.forEachIndexed { j, referenceElement ->
referenceElement.replace(elementFactory.createReferenceElementByType(newTypeParameterBounds[i][j]))
}
}
removeOriginalMemberOrAddOverride(member)
if (!data.isInterfaceTarget && !data.targetClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
data.targetClass.modifierList?.setModifierProperty(PsiModifier.ABSTRACT, true)
}
newMethod
}
else -> return
}
JavaCodeStyleManager.getInstance(project).shortenClassReferences(movedMember)
}
override fun move(info: MemberInfoBase<PsiMember>, substitutor: PsiSubstitutor) { override fun move(info: MemberInfoBase<PsiMember>, substitutor: PsiSubstitutor) {
val member = info.member.namedUnwrappedElement as? JetNamedDeclaration ?: return val member = info.member.namedUnwrappedElement as? JetNamedDeclaration ?: return
if (member is JetClass && info.overrides != null) { if (member is JetClass && info.overrides != null) {
moveSuperInterface(member) moveSuperInterface(member, substitutor)
return
}
if (data.targetClass is PsiClass) {
moveToJavaClass(member, substitutor)
return return
} }
val markedElements = markElements(member) val markedElements = markElements(member)
val memberCopy = member.copy() as JetNamedDeclaration val memberCopy = member.copy() as JetNamedDeclaration
fun moveClassMember(member: JetClassOrObject, memberCopy: JetClassOrObject): JetClassOrObject { fun moveClassOrObject(member: JetClassOrObject, memberCopy: JetClassOrObject): JetClassOrObject {
if (data.targetClass.isInterface()) { if (data.isInterfaceTarget) {
memberCopy.removeModifier(JetTokens.INNER_KEYWORD) memberCopy.removeModifier(JetTokens.INNER_KEYWORD)
} }
val movedMember = addMemberToTarget(memberCopy) as JetClassOrObject val movedMember = addMemberToTarget(memberCopy) as JetClassOrObject
member.delete() member.delete()
return movedMember return movedMember
} }
fun moveCallableMember(member: JetCallableDeclaration, memberCopy: JetCallableDeclaration): JetCallableDeclaration { fun moveCallableMember(member: JetCallableDeclaration, memberCopy: JetCallableDeclaration): JetCallableDeclaration {
data.targetClass as JetClass
val movedMember: JetCallableDeclaration val movedMember: JetCallableDeclaration
val clashingSuper = fixOverrideAndGetClashingSuper(member, memberCopy) val clashingSuper = fixOverrideAndGetClashingSuper(member, memberCopy)
val originalIsAbstract = member.hasModifier(JetTokens.ABSTRACT_KEYWORD) val originalIsAbstract = member.hasModifier(JetTokens.ABSTRACT_KEYWORD)
val toAbstract = when { val toAbstract = when {
info.isToAbstract -> true info.isToAbstract -> true
!data.targetClass.isInterface() -> false !data.isInterfaceTarget -> false
member is JetProperty -> member.mustBeAbstractInInterface() member is JetProperty -> member.mustBeAbstractInInterface()
else -> false else -> false
} }
@@ -575,23 +662,19 @@ class KotlinPullUpHelper(
if (member.typeReference == null) { if (member.typeReference == null) {
movedMember.typeReference?.addToShorteningWaitSet() movedMember.typeReference?.addToShorteningWaitSet()
} }
if (originalIsAbstract) {
member.delete() removeOriginalMemberOrAddOverride(member)
}
else if (!member.hasModifier(JetTokens.OVERRIDE_KEYWORD)) {
member.addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
}
} }
else { else {
movedMember = doAddCallableMember(memberCopy, clashingSuper) movedMember = doAddCallableMember(memberCopy, clashingSuper)
member.delete() member.delete()
} }
if (originalIsAbstract && data.targetClass.isInterface()) { if (originalIsAbstract && data.isInterfaceTarget) {
movedMember.removeModifier(JetTokens.ABSTRACT_KEYWORD) movedMember.removeModifier(JetTokens.ABSTRACT_KEYWORD)
} }
if (!data.targetClass.isInterface() if (!data.isInterfaceTarget
&& !data.targetClass.hasModifier(JetTokens.ABSTRACT_KEYWORD) && !data.targetClass.hasModifier(JetTokens.ABSTRACT_KEYWORD)
&& (movedMember.hasModifier(JetTokens.ABSTRACT_KEYWORD))) { && (movedMember.hasModifier(JetTokens.ABSTRACT_KEYWORD))) {
data.targetClass.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD) data.targetClass.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
@@ -602,7 +685,7 @@ class KotlinPullUpHelper(
try { try {
val movedMember = when (member) { val movedMember = when (member) {
is JetCallableDeclaration -> moveCallableMember(member, memberCopy as JetCallableDeclaration) is JetCallableDeclaration -> moveCallableMember(member, memberCopy as JetCallableDeclaration)
is JetClassOrObject -> moveClassMember(member, memberCopy as JetClassOrObject) is JetClassOrObject -> moveClassOrObject(member, memberCopy as JetClassOrObject)
else -> return else -> return
} }
@@ -16,12 +16,12 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.memberPullUp.PullUpData import com.intellij.refactoring.memberPullUp.PullUpData
import com.intellij.refactoring.memberPullUp.PullUpHelper import com.intellij.refactoring.memberPullUp.PullUpHelper
import com.intellij.refactoring.memberPullUp.PullUpHelperFactory import com.intellij.refactoring.memberPullUp.PullUpHelperFactory
import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
@@ -30,7 +30,7 @@ public class KotlinPullUpHelperFactory : PullUpHelperFactory {
private fun PullUpData.toKotlinPullUpData(): KotlinPullUpData? { private fun PullUpData.toKotlinPullUpData(): KotlinPullUpData? {
if (!sourceClass.isInheritor(targetClass, true)) return null if (!sourceClass.isInheritor(targetClass, true)) return null
val sourceClass = sourceClass.unwrapped as? JetClassOrObject ?: return null val sourceClass = sourceClass.unwrapped as? JetClassOrObject ?: return null
val targetClass = targetClass.unwrapped as? JetClass ?: return null val targetClass = targetClass.unwrapped as? PsiNamedElement ?: return null
val membersToMove = membersToMove val membersToMove = membersToMove
.map { it.namedUnwrappedElement as? JetNamedDeclaration } .map { it.namedUnwrappedElement as? JetNamedDeclaration }
.filterNotNull() .filterNotNull()
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.RefactoringBundle
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
@@ -42,13 +43,15 @@ import org.jetbrains.kotlin.util.findCallableMemberBySignature
fun checkConflicts(project: Project, fun checkConflicts(project: Project,
sourceClass: JetClassOrObject, sourceClass: JetClassOrObject,
targetClass: JetClass, targetClass: PsiNamedElement,
memberInfos: List<KotlinMemberInfo>, memberInfos: List<KotlinMemberInfo>,
onShowConflicts: () -> Unit = {}, onShowConflicts: () -> Unit = {},
onAccept: () -> Unit) { onAccept: () -> Unit) {
val conflicts = MultiMap<PsiElement, String>() val conflicts = MultiMap<PsiElement, String>()
val pullUpData = KotlinPullUpData(sourceClass, targetClass, memberInfos.map { it.member }) val pullUpData = KotlinPullUpData(sourceClass,
targetClass,
memberInfos.map { it.member }.filterNotNull())
with(pullUpData) { with(pullUpData) {
for (memberInfo in memberInfos) { for (memberInfo in memberInfos) {
@@ -73,7 +76,6 @@ private val CALLABLE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_I
private fun DeclarationDescriptor.renderForConflicts(): String { private fun DeclarationDescriptor.renderForConflicts(): String {
return when (this) { return when (this) {
// todo: objects
is ClassDescriptor -> "${DescriptorRenderer.getClassKindPrefix(this)} ${IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(this)}" is ClassDescriptor -> "${DescriptorRenderer.getClassKindPrefix(this)} ${IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(this)}"
is FunctionDescriptor -> "function '${CALLABLE_RENDERER.render(this)}'" is FunctionDescriptor -> "function '${CALLABLE_RENDERER.render(this)}'"
is PropertyDescriptor -> "property '${CALLABLE_RENDERER.render(this)}'" is PropertyDescriptor -> "property '${CALLABLE_RENDERER.render(this)}'"
@@ -135,7 +137,7 @@ private fun KotlinPullUpData.checkInnerClassToInterface(
member: JetNamedDeclaration, member: JetNamedDeclaration,
memberDescriptor: DeclarationDescriptor, memberDescriptor: DeclarationDescriptor,
conflicts: MultiMap<PsiElement, String>) { conflicts: MultiMap<PsiElement, String>) {
if (targetClass.isInterface() && memberDescriptor is ClassDescriptor && memberDescriptor.isInner) { if (isInterfaceTarget && memberDescriptor is ClassDescriptor && memberDescriptor.isInner) {
val message = "${memberDescriptor.renderForConflicts()} is an inner class. It can not be moved to the interface" val message = "${memberDescriptor.renderForConflicts()} is an inner class. It can not be moved to the interface"
conflicts.putValue(member, message.capitalize()) conflicts.putValue(member, message.capitalize())
} }
@@ -16,7 +16,24 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.psi.PsiClass
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.JetNamedFunction
import org.jetbrains.kotlin.psi.JetProperty import org.jetbrains.kotlin.psi.JetProperty
fun JetProperty.mustBeAbstractInInterface() = fun JetProperty.mustBeAbstractInInterface() =
hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && accessors.isEmpty()) hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && accessors.isEmpty())
fun JetNamedDeclaration.canMoveMemberToJavaClass(targetClass: PsiClass): Boolean {
return when (this) {
is JetProperty -> {
if (targetClass.isInterface) return false
if (hasModifier(JetTokens.OPEN_KEYWORD) || hasModifier(JetTokens.ABSTRACT_KEYWORD)) return false
if (accessors.isNotEmpty() || delegateExpression != null) return false
true
}
is JetNamedFunction -> true
else -> false
}
}
@@ -0,0 +1,3 @@
class A {
}
@@ -0,0 +1,10 @@
import org.jetbrains.annotations.NotNull;
abstract class A {
public final int x;
abstract boolean foo(int n);
abstract void bar(@NotNull String s);
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
abstract class <caret>B: A() {
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true"}
abstract val t: Int
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
abstract fun bar(s: String)
// INFO: {"checked": "true"}
inner class X {
}
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,21 @@
// WITH_RUNTIME
abstract class B: A() {
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true"}
abstract val t: Int
// INFO: {"checked": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
inner class X {
}
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,3 @@
class A {
}
@@ -0,0 +1,10 @@
import org.jetbrains.annotations.NotNull;
abstract class A {
public final int x;
abstract boolean foo(int n);
abstract void bar(@NotNull String s);
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
abstract class <caret>B: A() {
// INFO: {"checked": "true", "toAbstract": "true"}
val x = 1
// INFO: {"checked": "true", "toAbstract": "true"}
val y: Int get() = 2
// INFO: {"checked": "true", "toAbstract": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true", "toAbstract": "true"}
abstract val t: Int
// INFO: {"checked": "true", "toAbstract": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true", "toAbstract": "true"}
abstract fun bar(s: String)
// INFO: {"checked": "true", "toAbstract": "true"}
inner class X {
}
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
@@ -0,0 +1,21 @@
// WITH_RUNTIME
abstract class B: A() {
// INFO: {"checked": "true", "toAbstract": "true"}
val y: Int get() = 2
// INFO: {"checked": "true", "toAbstract": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true", "toAbstract": "true"}
abstract val t: Int
// INFO: {"checked": "true", "toAbstract": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true", "toAbstract": "true"}
inner class X {
}
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
@@ -0,0 +1,3 @@
class A<T extends I, U extends I, V> {
}
@@ -0,0 +1,21 @@
import org.jetbrains.annotations.NotNull;
abstract class A<T extends I, U extends I, V> {
@NotNull
public final T foo1;
@NotNull
public final Z<T> foo2;
public final Object foo3;
@NotNull
public final Z foo4;
@NotNull
public final T foo5;
@NotNull
public final Z<T> foo6;
public final Object foo7;
@NotNull
public final Z foo8;
abstract <S extends T> void foo(@NotNull T x1, @NotNull Z<T> x2, Object y1, @NotNull Z y2, @NotNull I w1, @NotNull Z w2, @NotNull S s1, @NotNull Z<S> s2);
}
@@ -0,0 +1,30 @@
// WITH_RUNTIME
interface I
interface Z<T>
class C<W: I> {
inner class <caret>B<X: I, Y>(x: X, y: Y): A<X, I, Z<Y>>() {
// INFO: {"checked": "true"}
fun foo<S : X>(x1: X, x2: Z<X>, y1: Y, y2: Z<Y>, w1: W, w2: Z<W>, s1: S, s2: Z<S>) {
}
// INFO: {"checked": "true"}
val foo1: X
// INFO: {"checked": "true"}
val foo2: Z<X>
// INFO: {"checked": "true"}
val foo3: Y
// INFO: {"checked": "true"}
val foo4: Z<Y>
// INFO: {"checked": "true"}
val foo5 = x
// INFO: {"checked": "true"}
val foo6 = object: Z<X> {}
// INFO: {"checked": "true"}
val foo7 = y
// INFO: {"checked": "true"}
val foo8 = object: Z<Y> {}
}
}
@@ -0,0 +1,14 @@
// WITH_RUNTIME
interface I
interface Z<T>
class C<W: I> {
inner class B<X: I, Y>(x: X, y: Y): A<X, I, Z<Y>>() {
// INFO: {"checked": "true"}
override fun foo<S : X>(x1: X, x2: Z<X>, y1: Y, y2: Z<Y>, w1: W, w2: Z<W>, s1: S, s2: Z<S>) {
}
}
}
@@ -0,0 +1,3 @@
interface I {
}
@@ -0,0 +1,8 @@
import org.jetbrains.annotations.NotNull;
interface I {
boolean foo(int n);
void bar(@NotNull String s);
}
@@ -0,0 +1,20 @@
// WITH_RUNTIME
abstract class <caret>B: I {
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true"}
abstract val t: Int
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
abstract fun bar(s: String)
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,18 @@
// WITH_RUNTIME
abstract class B: I {
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true"}
abstract val t: Int
// INFO: {"checked": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,5 @@
class T {
static class A {
}
}
@@ -0,0 +1,12 @@
import org.jetbrains.annotations.NotNull;
class T {
static abstract class A {
public final int x;
abstract boolean foo(int n);
abstract void bar(@NotNull String s);
}
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
abstract class <caret>B: T.A() {
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true"}
abstract val t: Int
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
abstract fun bar(s: String)
// INFO: {"checked": "true"}
inner class X {
}
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,21 @@
// WITH_RUNTIME
abstract class B: T.A() {
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
val z: Int by lazy { 3 }
// INFO: {"checked": "true"}
abstract val t: Int
// INFO: {"checked": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
inner class X {
}
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,3 @@
class A implements Z {
}
@@ -0,0 +1,3 @@
class A implements Z, X {
}
@@ -0,0 +1,10 @@
// 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,10 @@
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class B: A(), Y
@@ -0,0 +1,3 @@
interface I extends Z {
}
@@ -0,0 +1,3 @@
interface I extends Z, X {
}
@@ -0,0 +1,10 @@
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class <caret>B: I, X, Y, Z
@@ -0,0 +1,10 @@
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "false"}
interface Y
// INFO: {"checked": "true"}
interface Z
class B: I, Y
@@ -0,0 +1,3 @@
class A<T> implements Z<T> {
}
@@ -0,0 +1,3 @@
class A<T> implements Z<T>, X, Y<T> {
}
@@ -0,0 +1,10 @@
// INFO: {"checked": "true"}
interface X<T, U>
// INFO: {"checked": "true"}
interface Y<V : Any>
// INFO: {"checked": "true"}
interface Z<T>
class <caret>B<S : Any, U : S, V>: A<S>(), X<U, V>, Y<S>, Z<U>
@@ -0,0 +1,10 @@
// INFO: {"checked": "true"}
interface X<T, U>
// INFO: {"checked": "true"}
interface Y<V : Any>
// INFO: {"checked": "true"}
interface Z<T>
class B<S : Any, U : S, V>: A<S>()
@@ -0,0 +1,3 @@
interface I {
}
@@ -0,0 +1,4 @@
interface I {
int foo();
}
@@ -0,0 +1,4 @@
class <caret>A: I {
// INFO: {"checked": "true"}
public fun foo(): Int = 1
}
@@ -0,0 +1,4 @@
class A: I {
// INFO: {"checked": "true"}
override fun foo(): Int = 1
}
@@ -0,0 +1,3 @@
class A {
}
@@ -0,0 +1,4 @@
abstract class A {
protected abstract int foo();
}
@@ -0,0 +1,6 @@
class <caret>B: A() {
// INFO: {"checked": "true"}
private fun foo(): Int = 1
fun bar() = foo() + 1
}
@@ -0,0 +1,6 @@
class B: A() {
// INFO: {"checked": "true"}
protected override fun foo(): Int = 1
fun bar() = foo() + 1
}
@@ -18,15 +18,17 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.google.gson.JsonParser import com.google.gson.JsonParser
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiNamedElement
import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.psi.JetElement
import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
@@ -57,6 +59,11 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase()
fixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}" fixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}"
val mainFileName = mainFile.getName() val mainFileName = mainFile.getName()
val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName)
val extraFiles = mainFile.getParentFile().listFiles { file, name ->
name != mainFileName && name.startsWith("$mainFileBaseName.") && (name.endsWith(".kt") || name.endsWith(".java"))
}
val extraFilesToPsi = extraFiles.toMap { fixture.configureByFile(it.getName()) }
val file = fixture.configureByFile(mainFileName) as JetFile val file = fixture.configureByFile(mainFileName) as JetFile
val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// WITH_RUNTIME") != null val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// WITH_RUNTIME") != null
@@ -82,9 +89,9 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase()
return members.filter { it.isChecked } return members.filter { it.isChecked }
} }
override fun chooseSuperClass(superClasses: List<JetClass>): JetClass { override fun chooseSuperClass(superClasses: List<PsiNamedElement>): PsiNamedElement {
if (targetClassName != null) { if (targetClassName != null) {
return superClasses.single { it.fqName?.asString() ?: it.name == targetClassName } return superClasses.single { it.qualifiedClassNameForRendering() == targetClassName }
} }
return superClasses.first() return superClasses.first()
} }
@@ -95,6 +102,9 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase()
assert(!conflictFile.exists()) { "Conflict file $conflictFile should not exist" } assert(!conflictFile.exists()) { "Conflict file $conflictFile should not exist" }
JetTestUtils.assertEqualsToFile(afterFile, file.text!!) JetTestUtils.assertEqualsToFile(afterFile, file.text!!)
for ((extraPsiFile, extraFile) in extraFilesToPsi) {
JetTestUtils.assertEqualsToFile(File("${extraFile.getPath()}.after"), extraPsiFile.getText())
}
} }
catch(e: Exception) { catch(e: Exception) {
val message = when (e) { val message = when (e) {
@@ -35,6 +35,66 @@ public class PullUpTestGenerated extends AbstractPullUpTest {
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/pullUp"), Pattern.compile("^(.+)\\.kt$")); JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/pullUp"), Pattern.compile("^(.+)\\.kt$"));
} }
@TestMetadata("k2j/fromClassToClass.kt")
public void testK2j_FromClassToClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/fromClassToClass.kt");
doTest(fileName);
}
@TestMetadata("k2j/fromClassToClassAndMakeAbstract.kt")
public void testK2j_FromClassToClassAndMakeAbstract() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.kt");
doTest(fileName);
}
@TestMetadata("k2j/fromClassToClassWithGenerics.kt")
public void testK2j_FromClassToClassWithGenerics() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.kt");
doTest(fileName);
}
@TestMetadata("k2j/fromClassToInterface.kt")
public void testK2j_FromClassToInterface() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/fromClassToInterface.kt");
doTest(fileName);
}
@TestMetadata("k2j/fromClassToNestedClass.kt")
public void testK2j_FromClassToNestedClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.kt");
doTest(fileName);
}
@TestMetadata("k2j/moveSuperInterfacesToClass.kt")
public void testK2j_MoveSuperInterfacesToClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.kt");
doTest(fileName);
}
@TestMetadata("k2j/moveSuperInterfacesToInterface.kt")
public void testK2j_MoveSuperInterfacesToInterface() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.kt");
doTest(fileName);
}
@TestMetadata("k2j/moveSuperInterfacesWithGenerics.kt")
public void testK2j_MoveSuperInterfacesWithGenerics() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.kt");
doTest(fileName);
}
@TestMetadata("k2j/publicToInterface.kt")
public void testK2j_PublicToInterface() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/publicToInterface.kt");
doTest(fileName);
}
@TestMetadata("k2j/usedPrivateToClass.kt")
public void testK2j_UsedPrivateToClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.kt");
doTest(fileName);
}
@TestMetadata("k2k/accidentalOverrides.kt") @TestMetadata("k2k/accidentalOverrides.kt")
public void testK2k_AccidentalOverrides() throws Exception { public void testK2k_AccidentalOverrides() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2k/accidentalOverrides.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2k/accidentalOverrides.kt");