diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt index d4332d58ade..81ae4ea3f66 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt @@ -462,6 +462,9 @@ public fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMeth val method = targetClass.add(methodToAdd) as PsiMethod copyModifierListItems(template.getModifierList(), method.getModifierList()) + if (targetClass.isInterface()) { + method.getModifierList().setModifierProperty(PsiModifier.FINAL, false) + } copyTypeParameters(template, method) { method, typeParameterList -> method.addAfter(typeParameterList, method.getModifierList()) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/KotlinMemberInfoStorage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/KotlinMemberInfoStorage.kt index 71cf9fe33a6..03f1598b5ba 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/KotlinMemberInfoStorage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/KotlinMemberInfoStorage.kt @@ -16,6 +16,8 @@ package org.jetbrains.kotlin.idea.refactoring.memberInfo +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiNamedElement import com.intellij.refactoring.classMembers.AbstractMemberInfoStorage import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor @@ -31,9 +33,9 @@ import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import java.util.ArrayList public class KotlinMemberInfoStorage( - classOrObject: JetClassOrObject, + classOrObject: PsiNamedElement, filter: (JetNamedDeclaration) -> Boolean = { true } -): AbstractMemberInfoStorage(classOrObject, filter) { +): AbstractMemberInfoStorage(classOrObject, filter) { override fun memberConflict(member1: JetNamedDeclaration, member: JetNamedDeclaration): Boolean { val descriptor1 = member1.resolveToDescriptor() val descriptor = member.resolveToDescriptor() @@ -49,24 +51,27 @@ public class KotlinMemberInfoStorage( } } - override fun buildSubClassesMap(aClass: JetClassOrObject) { - val classDescriptor = aClass.resolveToDescriptor() as ClassDescriptor + override fun buildSubClassesMap(aClass: PsiNamedElement) { + val classDescriptor = aClass.getClassDescriptorIfAny() ?: return val classType = classDescriptor.defaultType for (supertype in classType.immediateSupertypes()) { - val superClass = supertype.constructor.declarationDescriptor?.source?.getPsi() as? JetClassOrObject - ?: continue - getSubclasses(superClass).add(aClass) - buildSubClassesMap(superClass) + val superClass = supertype.constructor.declarationDescriptor?.source?.getPsi() + if (superClass is JetClass || superClass is PsiClass) { + getSubclasses(superClass as PsiNamedElement).add(aClass) + buildSubClassesMap(superClass) + } } } - override fun isInheritor(baseClass: JetClassOrObject, aClass: JetClassOrObject): Boolean { - val baseDescriptor = baseClass.resolveToDescriptor() as ClassDescriptor - val currentDescriptor = aClass.resolveToDescriptor() as ClassDescriptor + override fun isInheritor(baseClass: PsiNamedElement, aClass: PsiNamedElement): Boolean { + val baseDescriptor = baseClass.getClassDescriptorIfAny() ?: return false + val currentDescriptor = aClass.getClassDescriptorIfAny() ?: return false return DescriptorUtils.isSubclass(currentDescriptor, baseDescriptor) } - override fun extractClassMembers(aClass: JetClassOrObject, temp: ArrayList) { + override fun extractClassMembers(aClass: PsiNamedElement, temp: ArrayList) { + if (aClass !is JetClassOrObject) return + val context = aClass.analyze() aClass.declarations .filter { it is JetNamedDeclaration diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/JetClassOrObjectListCellRenderer.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/KotlinOrJavaClassCellRenderer.java similarity index 75% rename from idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/JetClassOrObjectListCellRenderer.java rename to idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/KotlinOrJavaClassCellRenderer.java index 7cc6acc6618..ccb3248696f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/JetClassOrObjectListCellRenderer.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/KotlinOrJavaClassCellRenderer.java @@ -17,17 +17,17 @@ package org.jetbrains.kotlin.idea.refactoring.memberInfo; import com.intellij.openapi.util.Iconable; +import com.intellij.psi.PsiNamedElement; import com.intellij.ui.ListCellRendererWrapper; -import org.jetbrains.kotlin.psi.JetClassOrObject; import javax.swing.*; -public class JetClassOrObjectListCellRenderer extends ListCellRendererWrapper { +public class KotlinOrJavaClassCellRenderer extends ListCellRendererWrapper { @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; - setText(MemberInfoPackage.qualifiedNameForRendering(value)); + setText(MemberInfoPackage.qualifiedClassNameForRendering(value)); Icon icon = value.getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS); if (icon != null) { setIcon(icon); diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/Utils.kt deleted file mode 100644 index af1c30a3caf..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/Utils.kt +++ /dev/null @@ -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]" \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/memberInfoUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/memberInfoUtils.kt new file mode 100644 index 00000000000..3618def427e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/memberInfo/memberInfoUtils.kt @@ -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]" +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpData.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpData.kt index 3eddc86e306..7eb012b08ad 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpData.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpData.kt @@ -16,11 +16,13 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp +import com.intellij.psi.PsiNamedElement import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade 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.JetNamedDeclaration import org.jetbrains.kotlin.resolve.BindingContext @@ -34,7 +36,7 @@ import org.jetbrains.kotlin.utils.keysToMap import java.util.LinkedHashMap class KotlinPullUpData(val sourceClass: JetClassOrObject, - val targetClass: JetClass, + val targetClass: PsiNamedElement, val membersToMove: Collection) { val resolutionFacade = sourceClass.getResolutionFacade() @@ -44,7 +46,7 @@ class KotlinPullUpData(val sourceClass: JetClassOrObject, 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 { sourceClassDescriptor.typeConstructor.parameters + sourceClass.getResolutionScope(sourceClassContext, resolutionFacade) @@ -71,4 +73,6 @@ class KotlinPullUpData(val sourceClass: JetClassOrObject, TypeSubstitutor.create(substitution) } + + val isInterfaceTarget: Boolean = targetClassDescriptor.kind == ClassKind.INTERFACE } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialog.kt index bcd8179877b..ad60f48f4cd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialog.kt @@ -18,7 +18,9 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogWrapper +import com.intellij.psi.PsiClass import com.intellij.psi.PsiComment +import com.intellij.psi.PsiNamedElement import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.classMembers.AbstractMemberInfoModel import com.intellij.refactoring.memberPullUp.PullUpProcessor @@ -30,7 +32,7 @@ import org.jetbrains.kotlin.psi.* public class KotlinPullUpDialog( project: Project, private val classOrObject: JetClassOrObject, - superClasses: List, + superClasses: List, memberInfoStorage: KotlinMemberInfoStorage ) : KotlinPullUpDialogBase( project, classOrObject, superClasses, memberInfoStorage, PULL_MEMBERS_UP @@ -53,6 +55,7 @@ public class KotlinPullUpDialog( */ override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean { val superClass = superClass ?: return false + if (superClass is PsiClass) return false if (!superClass.isInterface()) return true val member = memberInfo.member @@ -60,20 +63,24 @@ public class KotlinPullUpDialog( } 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 { 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.member in memberInfoStorage.getExtending(superClass)) return false + if (member in memberInfoStorage.getExtending(superClass)) return false return true } } 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 @@ -96,10 +103,11 @@ public class KotlinPullUpDialog( companion object { fun createProcessor(sourceClass: JetClassOrObject, - targetClass: JetClass, + targetClass: PsiNamedElement, memberInfos: List): PullUpProcessor { + val targetPsiClass = targetClass as? PsiClass ?: (targetClass as JetClass).toLightClass() return PullUpProcessor(sourceClass.toLightClass(), - targetClass.toLightClass(), + targetPsiClass, memberInfos.map { it.toJavaMemberInfo() }.filterNotNull().toTypedArray(), DocCommentPolicy(JavaRefactoringSettings.getInstance().PULL_UP_MEMBERS_JAVADOC)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialogBase.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialogBase.java index 01f8081ace9..a112e34715d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialogBase.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpDialogBase.java @@ -17,9 +17,10 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiNamedElement; import com.intellij.refactoring.memberPullUp.PullUpDialogBase; 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.KotlinMemberInfoStorage; 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 public abstract class KotlinPullUpDialogBase extends - PullUpDialogBase { + PullUpDialogBase { protected KotlinPullUpDialogBase( Project project, JetClassOrObject object, - List superClasses, + List superClasses, KotlinMemberInfoStorage memberInfoStorage, String title ) { @@ -46,7 +47,7 @@ public abstract class KotlinPullUpDialogBase extends @SuppressWarnings("unchecked") @Override protected void initClassCombo(JComboBox classCombo) { - classCombo.setRenderer(new JetClassOrObjectListCellRenderer()); + classCombo.setRenderer(new KotlinOrJavaClassCellRenderer()); classCombo.addItemListener( new ItemListener() { @Override diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt index cda66d32fa7..866a55bd4ab 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHandler.kt @@ -22,8 +22,10 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.ScrollType import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile +import com.intellij.psi.PsiNamedElement import com.intellij.refactoring.HelpID import com.intellij.refactoring.RefactoringActionHandler 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.refactoring.memberInfo.KotlinMemberInfo 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.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf @@ -49,7 +51,7 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler { interface TestHelper { fun adjustMembers(members: List): List - fun chooseSuperClass(superClasses: List): JetClass + fun chooseSuperClass(superClasses: List): PsiNamedElement } 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) { val message = RefactoringBundle.getCannotRefactorMessage( 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) } @@ -125,10 +127,11 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler { .map { val descriptor = it.constructor.declarationDescriptor 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() - .toSortedListBy { it.qualifiedNameForRendering() } + .toSortedListBy { it.qualifiedClassNameForRendering() } if (superClasses.isEmpty()) { val containingClass = classOrObject.getStrictParentOfType() diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt index c859cb9e10e..c82953da037 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt @@ -18,7 +18,9 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp import com.intellij.openapi.util.Key import com.intellij.psi.* +import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.impl.light.LightField +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.searches.ReferencesSearch 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.namedUnwrappedElement import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.JetLanguage 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.imports.importableFqName import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier @@ -175,7 +180,7 @@ class KotlinPullUpHelper( } } commonInitializer?.accept(visitor) - if (targetConstructor == data.targetClass.getPrimaryConstructor() ?: data.targetClass) { + if (targetConstructor == (data.targetClass as? JetClass)?.getPrimaryConstructor() ?: data.targetClass) { property.initializer?.accept(visitor) } @@ -192,7 +197,7 @@ class KotlinPullUpHelper( } private val targetToSourceConstructors = LinkedHashMap>().let { result -> - if (!data.targetClass.isInterface()) { + if (!data.isInterfaceTarget && data.targetClass is JetClass) { result[data.targetClass.getPrimaryConstructor() ?: data.targetClass] = ArrayList() data.sourceClass.accept( object : JetTreeVisitorVoid() { @@ -287,7 +292,7 @@ class KotlinPullUpHelper( override fun setCorrectVisibility(info: MemberInfoBase) { val member = info.member.namedUnwrappedElement as? JetNamedDeclaration ?: return - if (data.targetClass.isInterface()) { + if (data.isInterfaceTarget) { member.removeModifier(JetTokens.PUBLIC_KEYWORD) return } @@ -332,7 +337,7 @@ class KotlinPullUpHelper( } private fun makeAbstract(sourceMember: JetCallableDeclaration, targetMember: JetCallableDeclaration) { - if (!data.targetClass.isInterface()) { + if (!data.isInterfaceTarget) { targetMember.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD) } @@ -373,6 +378,8 @@ class KotlinPullUpHelper( } private fun addMemberToTarget(targetMember: JetNamedDeclaration): JetNamedDeclaration { + data.targetClass as JetClass + val anchor = data.targetClass.declarations.filterIsInstance(targetMember.javaClass).lastOrNull() val movedMember = when { anchor == null && targetMember is JetProperty -> data.targetClass.addDeclarationBefore(targetMember, null) @@ -509,11 +516,8 @@ class KotlinPullUpHelper( addAfter(JetPsiFactory(this).createWhiteSpace(), modifierList) } - private fun moveSuperInterface(member: JetClass) { - val psiFactory = JetPsiFactory(member) - + private fun moveSuperInterface(member: JetClass, substitutor: PsiSubstitutor) { val classDescriptor = data.memberDescriptors[member] as? ClassDescriptor ?: return - val currentSpecifier = data.sourceClass.getDelegationSpecifiers() .filterIsInstance() @@ -521,48 +525,131 @@ class KotlinPullUpHelper( val referencedType = data.sourceClassContext[BindingContext.TYPE, it.typeReference] referencedType?.constructor?.declarationDescriptor == classDescriptor } ?: 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) - 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 } + 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, substitutor: PsiSubstitutor) { val member = info.member.namedUnwrappedElement as? JetNamedDeclaration ?: return if (member is JetClass && info.overrides != null) { - moveSuperInterface(member) + moveSuperInterface(member, substitutor) + return + } + + if (data.targetClass is PsiClass) { + moveToJavaClass(member, substitutor) return } val markedElements = markElements(member) val memberCopy = member.copy() as JetNamedDeclaration - fun moveClassMember(member: JetClassOrObject, memberCopy: JetClassOrObject): JetClassOrObject { - if (data.targetClass.isInterface()) { + fun moveClassOrObject(member: JetClassOrObject, memberCopy: JetClassOrObject): JetClassOrObject { + if (data.isInterfaceTarget) { memberCopy.removeModifier(JetTokens.INNER_KEYWORD) } + val movedMember = addMemberToTarget(memberCopy) as JetClassOrObject member.delete() return movedMember } fun moveCallableMember(member: JetCallableDeclaration, memberCopy: JetCallableDeclaration): JetCallableDeclaration { + data.targetClass as JetClass + val movedMember: JetCallableDeclaration val clashingSuper = fixOverrideAndGetClashingSuper(member, memberCopy) val originalIsAbstract = member.hasModifier(JetTokens.ABSTRACT_KEYWORD) val toAbstract = when { info.isToAbstract -> true - !data.targetClass.isInterface() -> false + !data.isInterfaceTarget -> false member is JetProperty -> member.mustBeAbstractInInterface() else -> false } @@ -575,23 +662,19 @@ class KotlinPullUpHelper( if (member.typeReference == null) { movedMember.typeReference?.addToShorteningWaitSet() } - if (originalIsAbstract) { - member.delete() - } - else if (!member.hasModifier(JetTokens.OVERRIDE_KEYWORD)) { - member.addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD) - } + + removeOriginalMemberOrAddOverride(member) } else { movedMember = doAddCallableMember(memberCopy, clashingSuper) member.delete() } - if (originalIsAbstract && data.targetClass.isInterface()) { + if (originalIsAbstract && data.isInterfaceTarget) { movedMember.removeModifier(JetTokens.ABSTRACT_KEYWORD) } - if (!data.targetClass.isInterface() + if (!data.isInterfaceTarget && !data.targetClass.hasModifier(JetTokens.ABSTRACT_KEYWORD) && (movedMember.hasModifier(JetTokens.ABSTRACT_KEYWORD))) { data.targetClass.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD) @@ -602,7 +685,7 @@ class KotlinPullUpHelper( try { val movedMember = when (member) { is JetCallableDeclaration -> moveCallableMember(member, memberCopy as JetCallableDeclaration) - is JetClassOrObject -> moveClassMember(member, memberCopy as JetClassOrObject) + is JetClassOrObject -> moveClassOrObject(member, memberCopy as JetClassOrObject) else -> return } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelperFactory.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelperFactory.kt index 27033037d7b..a59f1431efd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelperFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelperFactory.kt @@ -16,12 +16,12 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp +import com.intellij.psi.PsiNamedElement import com.intellij.refactoring.memberPullUp.PullUpData import com.intellij.refactoring.memberPullUp.PullUpHelper import com.intellij.refactoring.memberPullUp.PullUpHelperFactory import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.unwrapped -import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.JetClassOrObject import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.psi.psiUtil.startOffset @@ -30,7 +30,7 @@ public class KotlinPullUpHelperFactory : PullUpHelperFactory { private fun PullUpData.toKotlinPullUpData(): KotlinPullUpData? { if (!sourceClass.isInheritor(targetClass, true)) 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 .map { it.namedUnwrappedElement as? JetNamedDeclaration } .filterNotNull() diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpConflictsUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpConflictsUtils.kt index ac230fd9b91..20c7b9ee564 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpConflictsUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpConflictsUtils.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNamedElement import com.intellij.refactoring.RefactoringBundle import com.intellij.util.containers.MultiMap import org.jetbrains.kotlin.asJava.unwrapped @@ -42,13 +43,15 @@ import org.jetbrains.kotlin.util.findCallableMemberBySignature fun checkConflicts(project: Project, sourceClass: JetClassOrObject, - targetClass: JetClass, + targetClass: PsiNamedElement, memberInfos: List, onShowConflicts: () -> Unit = {}, onAccept: () -> Unit) { val conflicts = MultiMap() - val pullUpData = KotlinPullUpData(sourceClass, targetClass, memberInfos.map { it.member }) + val pullUpData = KotlinPullUpData(sourceClass, + targetClass, + memberInfos.map { it.member }.filterNotNull()) with(pullUpData) { for (memberInfo in memberInfos) { @@ -73,7 +76,6 @@ private val CALLABLE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_I private fun DeclarationDescriptor.renderForConflicts(): String { return when (this) { - // todo: objects is ClassDescriptor -> "${DescriptorRenderer.getClassKindPrefix(this)} ${IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(this)}" is FunctionDescriptor -> "function '${CALLABLE_RENDERER.render(this)}'" is PropertyDescriptor -> "property '${CALLABLE_RENDERER.render(this)}'" @@ -135,7 +137,7 @@ private fun KotlinPullUpData.checkInnerClassToInterface( member: JetNamedDeclaration, memberDescriptor: DeclarationDescriptor, conflicts: MultiMap) { - 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" conflicts.putValue(member, message.capitalize()) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt index 830e0241c47..ab67f6b61c7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/pullUpUtils.kt @@ -16,7 +16,24 @@ 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 fun JetProperty.mustBeAbstractInInterface() = 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 + } +} diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClass.1.java b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.1.java new file mode 100644 index 00000000000..1b4568a8f26 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.1.java @@ -0,0 +1,3 @@ +class A { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClass.1.java.after b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.1.java.after new file mode 100644 index 00000000000..5113d36bf6b --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.1.java.after @@ -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); +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClass.kt b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.kt new file mode 100644 index 00000000000..123e0bad392 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME +abstract class 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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClass.kt.after b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.kt.after new file mode 100644 index 00000000000..562f4e3f123 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClass.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.1.java b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.1.java new file mode 100644 index 00000000000..1b4568a8f26 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.1.java @@ -0,0 +1,3 @@ +class A { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.1.java.after b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.1.java.after new file mode 100644 index 00000000000..5113d36bf6b --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.1.java.after @@ -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); +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.kt b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.kt new file mode 100644 index 00000000000..5294fd29b6c --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME +abstract class 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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.kt.after b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.kt.after new file mode 100644 index 00000000000..3063312d3ea --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassAndMakeAbstract.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.1.java b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.1.java new file mode 100644 index 00000000000..f98169d7d80 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.1.java @@ -0,0 +1,3 @@ +class A { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.1.java.after b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.1.java.after new file mode 100644 index 00000000000..8904e880953 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.1.java.after @@ -0,0 +1,21 @@ +import org.jetbrains.annotations.NotNull; + +abstract class A { + + @NotNull + public final T foo1; + @NotNull + public final Z foo2; + public final Object foo3; + @NotNull + public final Z foo4; + @NotNull + public final T foo5; + @NotNull + public final Z foo6; + public final Object foo7; + @NotNull + public final Z foo8; + + abstract void foo(@NotNull T x1, @NotNull Z x2, Object y1, @NotNull Z y2, @NotNull I w1, @NotNull Z w2, @NotNull S s1, @NotNull Z s2); +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.kt b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.kt new file mode 100644 index 00000000000..0e735e241db --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.kt @@ -0,0 +1,30 @@ +// WITH_RUNTIME +interface I + +interface Z + +class C { + inner class B(x: X, y: Y): A>() { + // INFO: {"checked": "true"} + fun foo(x1: X, x2: Z, y1: Y, y2: Z, w1: W, w2: Z, s1: S, s2: Z) { + + } + // INFO: {"checked": "true"} + val foo1: X + // INFO: {"checked": "true"} + val foo2: Z + // INFO: {"checked": "true"} + val foo3: Y + // INFO: {"checked": "true"} + val foo4: Z + + // INFO: {"checked": "true"} + val foo5 = x + // INFO: {"checked": "true"} + val foo6 = object: Z {} + // INFO: {"checked": "true"} + val foo7 = y + // INFO: {"checked": "true"} + val foo8 = object: Z {} + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.kt.after b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.kt.after new file mode 100644 index 00000000000..4a64013a156 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToClassWithGenerics.kt.after @@ -0,0 +1,14 @@ +// WITH_RUNTIME +interface I + +interface Z + +class C { + inner class B(x: X, y: Y): A>() { + // INFO: {"checked": "true"} + override fun foo(x1: X, x2: Z, y1: Y, y2: Z, w1: W, w2: Z, s1: S, s2: Z) { + + } + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.1.java b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.1.java new file mode 100644 index 00000000000..59b90a4c96a --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.1.java @@ -0,0 +1,3 @@ +interface I { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.1.java.after b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.1.java.after new file mode 100644 index 00000000000..62ab49448a7 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.1.java.after @@ -0,0 +1,8 @@ +import org.jetbrains.annotations.NotNull; + +interface I { + + boolean foo(int n); + + void bar(@NotNull String s); +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.kt b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.kt new file mode 100644 index 00000000000..73bd1d3b8b8 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.kt @@ -0,0 +1,20 @@ +// 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"} + fun foo(n: Int): Boolean = n > 0 + // INFO: {"checked": "true"} + abstract fun bar(s: String) + + // INFO: {"checked": "true"} + class Y { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.kt.after b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.kt.after new file mode 100644 index 00000000000..464bacfba97 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToInterface.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.1.java b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.1.java new file mode 100644 index 00000000000..c164fb459cc --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.1.java @@ -0,0 +1,5 @@ +class T { + static class A { + + } +} diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.1.java.after b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.1.java.after new file mode 100644 index 00000000000..b3e7128c21d --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.1.java.after @@ -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); + } +} diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.kt b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.kt new file mode 100644 index 00000000000..919f66d377f --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.kt @@ -0,0 +1,25 @@ +// WITH_RUNTIME +abstract class 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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.kt.after b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.kt.after new file mode 100644 index 00000000000..104444d5dd7 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/fromClassToNestedClass.kt.after @@ -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 { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.1.java b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.1.java new file mode 100644 index 00000000000..085f6131db3 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.1.java @@ -0,0 +1,3 @@ +class A implements Z { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.1.java.after b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.1.java.after new file mode 100644 index 00000000000..cfc6c5665c3 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.1.java.after @@ -0,0 +1,3 @@ +class A implements Z, X { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.kt b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.kt new file mode 100644 index 00000000000..4aa7c600229 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.kt @@ -0,0 +1,10 @@ +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "false"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +class B: A(), X, Y, Z \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.kt.after b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.kt.after new file mode 100644 index 00000000000..b9cab1379a1 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToClass.kt.after @@ -0,0 +1,10 @@ +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "false"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +class B: A(), Y \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.1.java b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.1.java new file mode 100644 index 00000000000..c4cda5ced3f --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.1.java @@ -0,0 +1,3 @@ +interface I extends Z { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.1.java.after b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.1.java.after new file mode 100644 index 00000000000..acf6e0d4b69 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.1.java.after @@ -0,0 +1,3 @@ +interface I extends Z, X { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.kt b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.kt new file mode 100644 index 00000000000..8410146ba90 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.kt @@ -0,0 +1,10 @@ +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "false"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +class B: I, X, Y, Z \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.kt.after b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.kt.after new file mode 100644 index 00000000000..a8a46706c82 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesToInterface.kt.after @@ -0,0 +1,10 @@ +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "false"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +class B: I, Y \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.1.java b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.1.java new file mode 100644 index 00000000000..db7ffe7869a --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.1.java @@ -0,0 +1,3 @@ +class A implements Z { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.1.java.after b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.1.java.after new file mode 100644 index 00000000000..efab1643bff --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.1.java.after @@ -0,0 +1,3 @@ +class A implements Z, X, Y { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.kt b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.kt new file mode 100644 index 00000000000..a9554d2fc0c --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.kt @@ -0,0 +1,10 @@ +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "true"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +class B: A(), X, Y, Z \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.kt.after b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.kt.after new file mode 100644 index 00000000000..c536912aeed --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/moveSuperInterfacesWithGenerics.kt.after @@ -0,0 +1,10 @@ +// INFO: {"checked": "true"} +interface X + +// INFO: {"checked": "true"} +interface Y + +// INFO: {"checked": "true"} +interface Z + +class B: A() \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/publicToInterface.1.java b/idea/testData/refactoring/pullUp/k2j/publicToInterface.1.java new file mode 100644 index 00000000000..91d101c372c --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/publicToInterface.1.java @@ -0,0 +1,3 @@ +interface I { + +} diff --git a/idea/testData/refactoring/pullUp/k2j/publicToInterface.1.java.after b/idea/testData/refactoring/pullUp/k2j/publicToInterface.1.java.after new file mode 100644 index 00000000000..824b42cf600 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/publicToInterface.1.java.after @@ -0,0 +1,4 @@ +interface I { + + int foo(); +} diff --git a/idea/testData/refactoring/pullUp/k2j/publicToInterface.kt b/idea/testData/refactoring/pullUp/k2j/publicToInterface.kt new file mode 100644 index 00000000000..00ce24e37b6 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/publicToInterface.kt @@ -0,0 +1,4 @@ +class A: I { + // INFO: {"checked": "true"} + public fun foo(): Int = 1 +} diff --git a/idea/testData/refactoring/pullUp/k2j/publicToInterface.kt.after b/idea/testData/refactoring/pullUp/k2j/publicToInterface.kt.after new file mode 100644 index 00000000000..29b145ba86a --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/publicToInterface.kt.after @@ -0,0 +1,4 @@ +class A: I { + // INFO: {"checked": "true"} + override fun foo(): Int = 1 +} diff --git a/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.1.java b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.1.java new file mode 100644 index 00000000000..1b4568a8f26 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.1.java @@ -0,0 +1,3 @@ +class A { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.1.java.after b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.1.java.after new file mode 100644 index 00000000000..b488993e84e --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.1.java.after @@ -0,0 +1,4 @@ +abstract class A { + + protected abstract int foo(); +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.kt b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.kt new file mode 100644 index 00000000000..8dde2c34aba --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.kt @@ -0,0 +1,6 @@ +class B: A() { + // INFO: {"checked": "true"} + private fun foo(): Int = 1 + + fun bar() = foo() + 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.kt.after b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.kt.after new file mode 100644 index 00000000000..9cc6bbffa28 --- /dev/null +++ b/idea/testData/refactoring/pullUp/k2j/usedPrivateToClass.kt.after @@ -0,0 +1,6 @@ +class B: A() { + // INFO: {"checked": "true"} + protected override fun foo(): Int = 1 + + fun bar() = foo() + 1 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt index a3f7723e043..3a513ba9ec6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/AbstractPullUpTest.kt @@ -18,15 +18,17 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp import com.google.gson.JsonParser 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.util.CommonRefactoringUtil import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase 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.JetLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.PluginTestCaseBase -import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.NotNullableUserDataProperty @@ -57,6 +59,11 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase() fixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}" 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 addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// WITH_RUNTIME") != null @@ -82,9 +89,9 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase() return members.filter { it.isChecked } } - override fun chooseSuperClass(superClasses: List): JetClass { + override fun chooseSuperClass(superClasses: List): PsiNamedElement { if (targetClassName != null) { - return superClasses.single { it.fqName?.asString() ?: it.name == targetClassName } + return superClasses.single { it.qualifiedClassNameForRendering() == targetClassName } } return superClasses.first() } @@ -95,6 +102,9 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase() assert(!conflictFile.exists()) { "Conflict file $conflictFile should not exist" } JetTestUtils.assertEqualsToFile(afterFile, file.text!!) + for ((extraPsiFile, extraFile) in extraFilesToPsi) { + JetTestUtils.assertEqualsToFile(File("${extraFile.getPath()}.after"), extraPsiFile.getText()) + } } catch(e: Exception) { val message = when (e) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/PullUpTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/PullUpTestGenerated.java index 19e6f4d4c76..9c46ab43eb1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/PullUpTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/PullUpTestGenerated.java @@ -35,6 +35,66 @@ public class PullUpTestGenerated extends AbstractPullUpTest { 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") public void testK2k_AccidentalOverrides() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/k2k/accidentalOverrides.kt");