Push Down: Initial Support

This commit is contained in:
Alexey Sedunov
2015-07-28 21:28:39 +03:00
parent 5168ccc159
commit a66ef47887
42 changed files with 1638 additions and 356 deletions
@@ -84,6 +84,7 @@ import org.jetbrains.kotlin.idea.refactoring.inline.AbstractInlineTest
import org.jetbrains.kotlin.idea.refactoring.introduce.AbstractJetExtractionTest
import org.jetbrains.kotlin.idea.refactoring.move.AbstractJetMoveTest
import org.jetbrains.kotlin.idea.refactoring.pullUp.AbstractPullUpTest
import org.jetbrains.kotlin.idea.refactoring.pushDown.AbstractPushDownTest
import org.jetbrains.kotlin.idea.refactoring.rename.AbstractRenameTest
import org.jetbrains.kotlin.idea.resolve.*
import org.jetbrains.kotlin.idea.structureView.AbstractKotlinFileStructureTest
@@ -664,6 +665,10 @@ fun main(args: Array<String>) {
model("refactoring/pullUp/j2k", extension = "java", singleClass = true, testClassName = "J2K", testMethod = "doJavaTest")
}
testClass(javaClass<AbstractPushDownTest>()) {
model("refactoring/pushDown", extension = "kt", singleClass = true)
}
testClass(javaClass<AbstractSelectExpressionForDebuggerTest>()) {
model("debugger/selectExpression", recursive = false)
model("debugger/selectExpression/disallowMethodCalls", testMethod = "doTestWoMethodCalls")
@@ -62,6 +62,7 @@ public fun ClassDescriptor.findCallableMemberBySignature(signature: CallableMemb
val descriptorKind = if (signature is FunctionDescriptor) DescriptorKindFilter.FUNCTIONS else DescriptorKindFilter.VARIABLES
return getDefaultType().getMemberScope()
.getDescriptors(descriptorKind)
.filterIsInstance<CallableMemberDescriptor>()
.firstOrNull {
it.getContainingDeclaration() == this
&& OverridingUtil.DEFAULT.isOverridableBy(it as CallableDescriptor, signature).getResult() == OVERRIDABLE
@@ -26,7 +26,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.util.EmptyQuery
public fun HierarchySearchRequest<PsiElement>.searchInheritors(): Query<PsiClass> {
public fun HierarchySearchRequest<*>.searchInheritors(): Query<PsiClass> {
val psiClass: PsiClass? = when (originalElement) {
is JetClassOrObject -> LightClassUtil.getPsiClass(originalElement)
is PsiClass -> originalElement
@@ -0,0 +1,103 @@
/*
* 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
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.RefactoringActionHandler
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.lang.ElementsHandler
import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import java.util.HashSet
public abstract class AbstractPullPushMembersHandler(
private val refactoringName: String,
private val helpId: String,
private val wrongPositionMessage: String
) : RefactoringActionHandler, ElementsHandler {
private fun reportWrongPosition(project: Project, editor: Editor?) {
val message = RefactoringBundle.getCannotRefactorMessage(wrongPositionMessage)
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId)
}
protected fun reportWrongContext(project: Project, editor: Editor?) {
val message = RefactoringBundle.getCannotRefactorMessage(
RefactoringBundle.message("is.not.supported.in.the.current.context", refactoringName)
)
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId)
}
protected abstract fun invoke(project: Project,
editor: Editor?,
classOrObject: JetClassOrObject?,
member: JetNamedDeclaration?,
dataContext: DataContext?)
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
val offset = editor.caretModel.offset
editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
val target = (file.findElementAt(offset) ?: return).parentsWithSelf.firstOrNull {
it is JetClassOrObject || ((it is JetNamedFunction || it is JetProperty) && it.parent is JetClassBody)
}
if (target == null) {
reportWrongPosition(project, editor)
return
}
if (!target.canRefactor()) return
invoke(project, arrayOf(target), dataContext)
}
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
val element = elements.singleOrNull() ?: return
val editor = dataContext?.let { CommonDataKeys.EDITOR.getData(it) }
val (classOrObject, member) = when (element) {
is JetNamedFunction, is JetProperty -> element.getStrictParentOfType<JetClassOrObject>() to element as JetNamedDeclaration?
is JetClassOrObject -> element to null
else -> {
reportWrongPosition(project, editor)
return
}
}
invoke(project, editor, classOrObject, member, dataContext)
}
override fun isEnabledOnElements(elements: Array<out PsiElement>): Boolean {
return elements.mapTo(HashSet<PsiElement>()) {
when (it) {
is JetNamedFunction, is JetProperty ->
(it.parent as? JetClassBody)?.parent as? JetClassOrObject
is JetClassOrObject -> it
else -> null
} ?: return false
}.size() == 1
}
}
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.Kotlin
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
import org.jetbrains.kotlin.idea.refactoring.pullUp.KotlinPullUpHandler
import org.jetbrains.kotlin.idea.refactoring.pushDown.KotlinPushDownHandler
import org.jetbrains.kotlin.idea.refactoring.safeDelete.*
import org.jetbrains.kotlin.psi.*
@@ -73,4 +74,6 @@ public class KotlinRefactoringSupportProvider : RefactoringSupportProvider() {
override fun getChangeSignatureHandler() = JetChangeSignatureHandler()
override fun getPullUpHandler() = KotlinPullUpHandler()
override fun getPushDownHandler() = KotlinPushDownHandler()
}
@@ -16,37 +16,36 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
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
import com.intellij.refactoring.lang.ElementsHandler
import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.AbstractPullPushMembersHandler
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.types.typeUtil.supertypes
import java.util.HashSet
public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
public class KotlinPullUpHandler : AbstractPullPushMembersHandler(
refactoringName = PULL_MEMBERS_UP,
helpId = HelpID.MEMBERS_PULL_UP,
wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")
) {
companion object {
val PULLUP_TEST_HELPER_KEY = "PULLUP_TEST_HELPER_KEY"
val PULL_UP_TEST_HELPER_KEY = "PULL_UP_TEST_HELPER_KEY"
}
interface TestHelper {
@@ -54,20 +53,6 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
fun chooseSuperClass(superClasses: List<PsiNamedElement>): PsiNamedElement
}
private fun reportWrongPosition(project: Project, editor: Editor?) {
val message = RefactoringBundle.getCannotRefactorMessage(
RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")
)
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, "refactoring.pullMembersUp")
}
private fun reportWrongContext(project: Project, editor: Editor?) {
val message = RefactoringBundle.getCannotRefactorMessage(
RefactoringBundle.message("is.not.supported.in.the.current.context", PULL_MEMBERS_UP)
)
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
}
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",
@@ -76,41 +61,7 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
}
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
val offset = editor.caretModel.offset
editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
val target = (file.findElementAt(offset) ?: return).parentsWithSelf.firstOrNull {
it is JetClassOrObject || ((it is JetNamedFunction || it is JetProperty) && it.parent is JetClassBody)
}
if (target == null) {
reportWrongPosition(project, editor)
return
}
if (!target.canRefactor()) return
invoke(project, arrayOf(target), dataContext)
}
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
val element = elements.singleOrNull() ?: return
val editor = dataContext?.let { CommonDataKeys.EDITOR.getData(it) }
val (classOrObject, member) = when (element) {
is JetNamedFunction, is JetProperty -> element.getStrictParentOfType<JetClassOrObject>() to element as JetNamedDeclaration?
is JetClassOrObject -> element to null
else -> {
reportWrongPosition(project, editor)
return
}
}
invoke(project, editor, classOrObject, member, dataContext)
}
private fun invoke(project: Project,
override fun invoke(project: Project,
editor: Editor?,
classOrObject: JetClassOrObject?,
member: JetNamedDeclaration?,
@@ -148,7 +99,7 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
val members = memberInfoStorage.getClassMemberInfos(classOrObject)
if (ApplicationManager.getApplication().isUnitTestMode) {
val helper = dataContext?.getData(PULLUP_TEST_HELPER_KEY) as TestHelper
val helper = dataContext?.getData(PULL_UP_TEST_HELPER_KEY) as TestHelper
val selectedMembers = helper.adjustMembers(members)
val targetClass = helper.chooseSuperClass(superClasses)
checkConflicts(project, classOrObject, targetClass, selectedMembers) {
@@ -162,17 +113,6 @@ public class KotlinPullUpHandler : RefactoringActionHandler, ElementsHandler {
KotlinPullUpDialog(project, classOrObject, superClasses, memberInfoStorage).show()
}
}
override fun isEnabledOnElements(elements: Array<out PsiElement>): Boolean {
return elements.mapTo(HashSet<PsiElement>()) {
when (it) {
is JetNamedFunction, is JetProperty ->
(it.parent as? JetClassBody)?.parent as? JetClassOrObject
is JetClassOrObject -> it
else -> null
} ?: return false
}.size() == 1
}
}
val PULL_MEMBERS_UP = "Pull Members Up"
@@ -16,7 +16,6 @@
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
@@ -31,30 +30,25 @@ 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.imports.importableFqName
import org.jetbrains.kotlin.idea.refactoring.safeDelete.removeOverrideModifier
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiUnifier
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.asAssignment
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isUnit
import java.util.ArrayList
import java.util.Comparator
import java.util.LinkedHashMap
@@ -64,12 +58,6 @@ class KotlinPullUpHelper(
private val javaData: PullUpData,
private val data: KotlinPullUpData
) : PullUpHelper<MemberInfoBase<PsiMember>> {
companion object {
private var JetElement.newFqName: FqName? by CopyableUserDataProperty(Key.create("NEW_FQ_NAME"))
private var JetElement.replaceWithTargetThis: Boolean? by CopyableUserDataProperty(Key.create("REPLACE_WITH_TARGET_THIS"))
private var JetElement.newTypeText: String? by CopyableUserDataProperty(Key.create("NEW_TYPE_TEXT"))
}
private fun JetExpression.isMovable(): Boolean {
return accept(
object: JetVisitor<Boolean, Nothing?>() {
@@ -334,184 +322,13 @@ class KotlinPullUpHelper(
return clashingSuperDescriptor.source.getPsi() as? JetCallableDeclaration
}
private fun makeAbstract(sourceMember: JetCallableDeclaration, targetMember: JetCallableDeclaration) {
if (!data.isInterfaceTarget) {
targetMember.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
}
if (sourceMember.typeReference == null) {
var type = (data.memberDescriptors[sourceMember] as CallableMemberDescriptor).returnType
if (type == null || type.isError) {
type = KotlinBuiltIns.getInstance().nullableAnyType
}
else {
type = data.sourceToTargetClassSubstitutor.substitute(type, Variance.INVARIANT)
?: KotlinBuiltIns.getInstance().nullableAnyType
}
if (sourceMember is JetProperty || !type.isUnit()) {
val typeRef = JetPsiFactory(sourceMember).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type))
targetMember.setTypeReference(typeRef)
}
}
val deleteFrom = when (sourceMember) {
is JetProperty -> {
targetMember as JetProperty
val accessors = targetMember.accessors
targetMember.equalsToken ?: targetMember.delegate ?: accessors.firstOrNull()
}
is JetNamedFunction -> {
targetMember as JetNamedFunction
targetMember.equalsToken ?: targetMember.bodyExpression
}
else -> null
}
if (deleteFrom != null) {
targetMember.deleteChildRange(deleteFrom, targetMember.lastChild)
}
}
private fun markElements(member: JetNamedDeclaration): List<JetElement> {
val affectedElements = ArrayList<JetElement>()
member.accept(
object: JetVisitorVoid() {
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
val referenceTarget = data.sourceClassContext[BindingContext.REFERENCE_TARGET, expression.instanceReference]
if (referenceTarget == data.targetClassDescriptor) {
expression.replaceWithTargetThis = true
affectedElements.add(expression)
}
}
override fun visitElement(element: PsiElement) {
element.allChildren.forEach { it.accept(this) }
}
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
val resolvedCall = expression.getResolvedCall(data.sourceClassContext) ?: return
var receiver = resolvedCall.getExplicitReceiverValue()
if (!receiver.exists()) {
receiver = resolvedCall.extensionReceiver
}
if (!receiver.exists()) {
receiver = resolvedCall.dispatchReceiver
}
if (!receiver.exists()) return
val implicitThis = receiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (implicitThis.isCompanionObject
&& DescriptorUtils.isAncestor(data.sourceClassDescriptor, implicitThis, true)) {
val qualifierFqName = implicitThis.importableFqName ?: return
expression.newFqName = FqName("${qualifierFqName.asString()}.${expression.getReferencedName()}")
affectedElements.add(expression)
}
}
override fun visitThisExpression(expression: JetThisExpression) {
visitSuperOrThis(expression)
}
override fun visitSuperExpression(expression: JetSuperExpression) {
visitSuperOrThis(expression)
}
override fun visitTypeReference(typeReference: JetTypeReference) {
val oldType = data.sourceClassContext[BindingContext.TYPE, typeReference] ?: return
val newType = data.sourceToTargetClassSubstitutor.substitute(oldType, Variance.INVARIANT) ?: return
typeReference.newTypeText = IdeDescriptorRenderers.SOURCE_CODE.renderType(newType)
affectedElements.add(typeReference)
}
}
)
return affectedElements
}
private fun processMarkedElements(member: JetNamedDeclaration) {
val psiFactory = JetPsiFactory(member)
val targetThis = psiFactory.createExpression("this@${data.targetClassDescriptor.name.asString()}")
val shorteningOptionsForThis = ShortenReferences.Options(removeThisLabels = true, removeThis = true)
member.accept(
object: JetVisitorVoid() {
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
expression.replaceWithTargetThis?.let {
expression.replaceWithTargetThis = null
val newThisExpression = expression.replace(targetThis) as JetExpression
newThisExpression.getQualifiedExpressionForReceiverOrThis().addToShorteningWaitSet(shorteningOptionsForThis)
}
}
override fun visitElement(element: PsiElement) {
for (it in element.allChildren.toList()) {
it.accept(this)
}
}
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
expression.newFqName?.let {
expression.newFqName = null
expression.mainReference.bindToFqName(it)
}
}
override fun visitThisExpression(expression: JetThisExpression) {
visitSuperOrThis(expression)
}
override fun visitSuperExpression(expression: JetSuperExpression) {
visitSuperOrThis(expression)
}
override fun visitTypeReference(typeReference: JetTypeReference) {
typeReference.newTypeText?.let {
typeReference.newTypeText = null
(typeReference.replace(psiFactory.createType(it)) as JetElement).addToShorteningWaitSet()
}
}
}
)
}
private fun clearMarking(markedElements: List<JetElement>) {
markedElements.forEach {
it.newFqName = null
it.newTypeText = null
it.replaceWithTargetThis = null
}
}
private fun moveSuperInterface(member: JetClass, substitutor: PsiSubstitutor) {
val classDescriptor = data.memberDescriptors[member] as? ClassDescriptor ?: return
val currentSpecifier =
data.sourceClass.getDelegationSpecifiers()
.filterIsInstance<JetDelegatorToSuperClass>()
.firstOrNull {
val referencedType = data.sourceClassContext[BindingContext.TYPE, it.typeReference]
referencedType?.constructor?.declarationDescriptor == classDescriptor
} ?: return
val currentSpecifier = data.sourceClass.getDelegatorToSuperClassByDescriptor(classDescriptor, data.sourceClassContext) ?: return
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
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass)
val newSpecifier = JetPsiFactory(member).createDelegatorToSuperClass(renderedType)
data.targetClass.addDelegationSpecifier(newSpecifier).addToShorteningWaitSet()
addDelegatorToSuperClass(currentSpecifier, data.targetClass, data.targetClassDescriptor, data.sourceClassContext, data.sourceToTargetClassSubstitutor)
}
is PsiClass -> {
@@ -601,7 +418,7 @@ class KotlinPullUpHelper(
return
}
val markedElements = markElements(member)
val markedElements = markElements(member, data.sourceClassContext, data.sourceClassDescriptor, data.targetClassDescriptor)
val memberCopy = member.copy() as JetNamedDeclaration
fun moveClassOrObject(member: JetClassOrObject, memberCopy: JetClassOrObject): JetClassOrObject {
@@ -629,7 +446,7 @@ class KotlinPullUpHelper(
}
if (toAbstract) {
if (!originalIsAbstract) {
makeAbstract(member, memberCopy)
makeAbstract(memberCopy, data.memberDescriptors[member] as CallableMemberDescriptor, data.sourceToTargetClassSubstitutor, data.targetClass)
}
movedMember = doAddCallableMember(memberCopy, clashingSuper, data.targetClass)
@@ -661,7 +478,7 @@ class KotlinPullUpHelper(
else -> return
}
processMarkedElements(movedMember)
applyMarking(movedMember, data.sourceToTargetClassSubstitutor, data.targetClassDescriptor)
addMovedMember(movedMember)
}
finally {
@@ -0,0 +1,167 @@
/*
* 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.pullUp
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.allChildren
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiverOrThis
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.Variance
import java.util.*
private var JetElement.newFqName: FqName? by CopyableUserDataProperty(Key.create("NEW_FQ_NAME"))
private var JetElement.replaceWithTargetThis: Boolean? by CopyableUserDataProperty(Key.create("REPLACE_WITH_TARGET_THIS"))
private var JetElement.newTypeText: ((TypeSubstitutor) -> String?)? by CopyableUserDataProperty(Key.create("NEW_TYPE_TEXT"))
fun markElements(
declaration: JetNamedDeclaration,
context: BindingContext,
sourceClassDescriptor: ClassDescriptor, targetClassDescriptor: ClassDescriptor?
): List<JetElement> {
val affectedElements = ArrayList<JetElement>()
declaration.accept(
object : JetVisitorVoid() {
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
if (targetClassDescriptor == null) return
val referenceTarget = context[BindingContext.REFERENCE_TARGET, expression.instanceReference]
if (referenceTarget == targetClassDescriptor) {
expression.replaceWithTargetThis = true
affectedElements.add(expression)
}
}
override fun visitElement(element: PsiElement) {
element.allChildren.forEach { it.accept(this) }
}
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
val resolvedCall = expression.getResolvedCall(context) ?: return
var receiver = resolvedCall.getExplicitReceiverValue()
if (!receiver.exists()) {
receiver = resolvedCall.extensionReceiver
}
if (!receiver.exists()) {
receiver = resolvedCall.dispatchReceiver
}
if (!receiver.exists()) return
val implicitThis = receiver.type.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (implicitThis.isCompanionObject
&& DescriptorUtils.isAncestor(sourceClassDescriptor, implicitThis, true)) {
val qualifierFqName = implicitThis.importableFqName ?: return
expression.newFqName = FqName("${qualifierFqName.asString()}.${expression.getReferencedName()}")
affectedElements.add(expression)
}
}
override fun visitThisExpression(expression: JetThisExpression) {
visitSuperOrThis(expression)
}
override fun visitSuperExpression(expression: JetSuperExpression) {
visitSuperOrThis(expression)
}
override fun visitTypeReference(typeReference: JetTypeReference) {
val oldType = context[BindingContext.TYPE, typeReference] ?: return
typeReference.newTypeText = f@ { substitutor ->
substitutor.substitute(oldType, Variance.INVARIANT)?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) }
}
affectedElements.add(typeReference)
}
}
)
return affectedElements
}
fun applyMarking(
declaration: JetNamedDeclaration,
substitutor: TypeSubstitutor, targetClassDescriptor: ClassDescriptor
) {
val psiFactory = JetPsiFactory(declaration)
val targetThis = psiFactory.createExpression("this@${targetClassDescriptor.name.asString()}")
val shorteningOptionsForThis = ShortenReferences.Options(removeThisLabels = true, removeThis = true)
declaration.accept(
object : JetVisitorVoid() {
private fun visitSuperOrThis(expression: JetInstanceExpressionWithLabel) {
expression.replaceWithTargetThis?.let {
expression.replaceWithTargetThis = null
val newThisExpression = expression.replace(targetThis) as JetExpression
newThisExpression.getQualifiedExpressionForReceiverOrThis().addToShorteningWaitSet(shorteningOptionsForThis)
}
}
override fun visitElement(element: PsiElement) {
for (it in element.allChildren.toList()) {
it.accept(this)
}
}
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) {
expression.newFqName?.let {
expression.newFqName = null
expression.mainReference.bindToFqName(it)
}
}
override fun visitThisExpression(expression: JetThisExpression) {
this.visitSuperOrThis(expression)
}
override fun visitSuperExpression(expression: JetSuperExpression) {
this.visitSuperOrThis(expression)
}
override fun visitTypeReference(typeReference: JetTypeReference) {
typeReference.newTypeText?.let f@ {
typeReference.newTypeText = null
val newTypeText = it(substitutor) ?: return@f
(typeReference.replace(psiFactory.createType(newTypeText)) as JetElement).addToShorteningWaitSet()
}
}
}
)
}
fun clearMarking(markedElements: List<JetElement>) {
markedElements.forEach {
it.newFqName = null
it.newTypeText = null
it.replaceWithTargetThis = null
}
}
@@ -17,10 +17,20 @@
package org.jetbrains.kotlin.idea.refactoring.pullUp
import com.intellij.psi.PsiClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.isUnit
fun JetProperty.mustBeAbstractInInterface() =
hasInitializer() || hasDelegate() || (!hasInitializer() && !hasDelegate() && accessors.isEmpty())
@@ -74,4 +84,76 @@ fun JetClass.makeAbstract() {
if (!isInterface()) {
addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
}
}
fun JetClassOrObject.getDelegatorToSuperClassByDescriptor(
descriptor: ClassDescriptor,
context: BindingContext
): JetDelegatorToSuperClass? {
return getDelegationSpecifiers()
.filterIsInstance<JetDelegatorToSuperClass>()
.firstOrNull {
val referencedType = context[BindingContext.TYPE, it.typeReference]
referencedType?.constructor?.declarationDescriptor == descriptor
}
}
fun makeAbstract(member: JetCallableDeclaration,
originalMemberDescriptor: CallableMemberDescriptor,
substitutor: TypeSubstitutor,
targetClass: JetClass) {
if (!targetClass.isInterface()) {
member.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
}
if (member.typeReference == null) {
var type = originalMemberDescriptor.returnType
if (type == null || type.isError) {
type = KotlinBuiltIns.getInstance().nullableAnyType
}
else {
type = substitutor.substitute(type, Variance.INVARIANT) ?: KotlinBuiltIns.getInstance().nullableAnyType
}
if (member is JetProperty || !type.isUnit()) {
val typeRef = JetPsiFactory(targetClass).createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type))
member.setTypeReference(typeRef)
}
}
val deleteFrom = when (member) {
is JetProperty -> {
member.equalsToken ?: member.delegate ?: member.accessors.firstOrNull()
}
is JetNamedFunction -> {
member.equalsToken ?: member.bodyExpression
}
else -> null
}
if (deleteFrom != null) {
member.deleteChildRange(deleteFrom, member.lastChild)
}
}
fun addDelegatorToSuperClass(
delegator: JetDelegatorToSuperClass,
targetClass: JetClassOrObject,
targetClassDescriptor: ClassDescriptor,
context: BindingContext,
substitutor: TypeSubstitutor
) {
val referencedType = context[BindingContext.TYPE, delegator.typeReference]!!
val referencedClass = referencedType.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (targetClassDescriptor == referencedClass || DescriptorUtils.isDirectSubclass(targetClassDescriptor, referencedClass)) return
val typeInTargetClass = substitutor.substitute(referencedType, Variance.INVARIANT)
if (!(typeInTargetClass != null && !typeInTargetClass.isError)) return
val renderedType = IdeDescriptorRenderers.SOURCE_CODE.renderType(typeInTargetClass)
val newSpecifier = JetPsiFactory(targetClass).createDelegatorToSuperClass(renderedType)
targetClass.addDelegationSpecifier(newSpecifier).addToShorteningWaitSet()
}
@@ -0,0 +1,104 @@
/*
* 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.pushDown
import com.intellij.openapi.project.Project
import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel
import com.intellij.refactoring.classMembers.MemberInfoChange
import com.intellij.refactoring.ui.RefactoringDialog
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.JetNamedFunction
import org.jetbrains.kotlin.psi.JetProperty
import java.awt.BorderLayout
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import java.awt.Insets
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel
public class KotlinPushDownDialog(
project: Project,
private val memberInfos: List<KotlinMemberInfo>,
private val sourceClass: JetClass
) : RefactoringDialog(project, true) {
init {
title = PUSH_MEMBERS_DOWN
init()
}
private var memberInfoModel: AbstractMemberInfoModel<JetNamedDeclaration, KotlinMemberInfo>? = null
public val selectedMemberInfos: List<KotlinMemberInfo>
get() = memberInfos.filter { it.isChecked && memberInfoModel?.isMemberEnabled(it) ?: false }
override fun getDimensionServiceKey() = "#" + javaClass.name
override fun createNorthPanel(): JComponent? {
val gbConstraints = GridBagConstraints()
val panel = JPanel(GridBagLayout())
gbConstraints.insets = Insets(4, 0, 10, 8)
gbConstraints.weighty = 1.0
gbConstraints.weightx = 1.0
gbConstraints.gridy = 0
gbConstraints.gridwidth = GridBagConstraints.REMAINDER
gbConstraints.fill = GridBagConstraints.BOTH
gbConstraints.anchor = GridBagConstraints.WEST
panel.add(JLabel(RefactoringBundle.message("push.members.from.0.down.label",
sourceClass.qualifiedClassNameForRendering())), gbConstraints)
return panel
}
override fun createCenterPanel(): JComponent? {
val panel = JPanel(BorderLayout())
val memberSelectionPanel = KotlinMemberSelectionPanel(
RefactoringBundle.message("members.to.be.pushed.down.panel.title"),
memberInfos,
RefactoringBundle.message("keep.abstract.column.header"))
panel.add(memberSelectionPanel, BorderLayout.CENTER)
memberInfoModel = object : AbstractMemberInfoModel<JetNamedDeclaration, KotlinMemberInfo>() {
override fun isFixedAbstract(member: KotlinMemberInfo?) = null
override fun isAbstractEnabled(memberInfo: KotlinMemberInfo): Boolean {
val member = memberInfo.member
return member is JetNamedFunction || member is JetProperty
}
}
memberInfoModel!!.memberInfoChanged(MemberInfoChange(memberInfos))
memberSelectionPanel.table.memberInfoModel = memberInfoModel
memberSelectionPanel.table.addMemberInfoChangeListener(memberInfoModel)
return panel
}
override fun doAction() {
if (!isOKActionEnabled()) return
JavaRefactoringSettings.getInstance().PUSH_DOWN_PREVIEW_USAGES = isPreviewUsages
invokeRefactoring(KotlinPushDownProcessor(getProject(), sourceClass, selectedMemberInfos))
}
}
@@ -0,0 +1,89 @@
/*
* 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.pushDown
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import com.intellij.refactoring.HelpID
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.refactoring.util.RefactoringUIUtil
import org.jetbrains.kotlin.idea.refactoring.AbstractPullPushMembersHandler
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfoStorage
import org.jetbrains.kotlin.idea.refactoring.memberInfo.qualifiedClassNameForRendering
import org.jetbrains.kotlin.idea.refactoring.pullUp.PULL_MEMBERS_UP
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.JetObjectDeclaration
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
val PUSH_MEMBERS_DOWN = "Push Members Down"
public class KotlinPushDownHandler : AbstractPullPushMembersHandler(
refactoringName = PUSH_MEMBERS_DOWN,
helpId = HelpID.MEMBERS_PUSH_DOWN,
wrongPositionMessage = RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.push.members.from")
) {
companion object {
val PUSH_DOWN_TEST_HELPER_KEY = "PUSH_DOWN_TEST_HELPER_KEY"
}
interface TestHelper {
fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo>
}
private fun reportFinalClassOrObject(project: Project, editor: Editor?, classOrObject: JetClassOrObject) {
val message = RefactoringBundle.getCannotRefactorMessage(
"${RefactoringUIUtil.getDescription(classOrObject, false)} is final".capitalize()
)
CommonRefactoringUtil.showErrorHint(project, editor, message, PULL_MEMBERS_UP, HelpID.MEMBERS_PULL_UP)
}
override fun invoke(project: Project,
editor: Editor?,
classOrObject: JetClassOrObject?,
member: JetNamedDeclaration?,
dataContext: DataContext?) {
if (classOrObject == null) {
reportWrongContext(project, editor)
return
}
if (!(classOrObject is JetClass && classOrObject.isInheritable())) {
reportFinalClassOrObject(project, editor, classOrObject)
return
}
val members = KotlinMemberInfoStorage(classOrObject).getClassMemberInfos(classOrObject)
if (ApplicationManager.getApplication().isUnitTestMode) {
val helper = dataContext?.getData(PUSH_DOWN_TEST_HELPER_KEY) as TestHelper
val selectedMembers = helper.adjustMembers(members)
KotlinPushDownProcessor(project, classOrObject, selectedMembers).run()
}
else {
val manager = PsiManager.getInstance(project)
members.filter { manager.areElementsEquivalent(it.member, member) }.forEach { it.isChecked = true }
KotlinPushDownDialog(project, members, classOrObject).show()
}
}
}
@@ -0,0 +1,202 @@
/*
* 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.pushDown
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.listeners.RefactoringEventData
import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageViewBundle
import com.intellij.usageView.UsageViewDescriptor
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
import org.jetbrains.kotlin.idea.refactoring.pullUp.*
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.keysToMap
import java.util.ArrayList
public class KotlinPushDownProcessor(
project: Project,
private val sourceClass: JetClass,
private val membersToMove: List<KotlinMemberInfo>
) : BaseRefactoringProcessor(project) {
private val resolutionFacade = sourceClass.getResolutionFacade()
private val sourceClassContext = resolutionFacade.analyzeFullyAndGetResult(listOf(sourceClass)).bindingContext
private val sourceClassDescriptor = sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, sourceClass] as ClassDescriptor
private val memberDescriptors = membersToMove
.map { it.member }
.keysToMap { sourceClassContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]!! }
inner class UsageViewDescriptorImpl : UsageViewDescriptor {
override fun getProcessedElementsHeader() = RefactoringBundle.message("push.down.members.elements.header")
override fun getElements() = arrayOf(sourceClass)
override fun getCodeReferencesText(usagesCount: Int, filesCount: Int) =
RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount))
override fun getCommentReferencesText(usagesCount: Int, filesCount: Int) = null
}
class SubclassUsage(element: PsiElement) : UsageInfo(element)
override fun getCommandName() = PUSH_MEMBERS_DOWN
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>) = UsageViewDescriptorImpl()
override fun getBeforeData() = RefactoringEventData().apply {
addElement(sourceClass)
addElements(membersToMove.map { it.member }.toTypedArray())
}
override fun getAfterData(usages: Array<out UsageInfo>) = RefactoringEventData().apply {
addElements(usages.map { it.element as? JetClassOrObject }.filterNotNull())
}
override fun findUsages(): Array<out UsageInfo> {
return HierarchySearchRequest(sourceClass, sourceClass.useScope, false)
.searchInheritors()
.map { it.unwrapped }
.filterNotNull()
.map { SubclassUsage(it) }
.toTypedArray()
}
private fun pushDownToClass(targetClass: JetClassOrObject) {
val targetClassDescriptor = resolutionFacade.resolveToDescriptor(targetClass) as ClassDescriptor
val substitutor = getTypeSubstitutor(sourceClassDescriptor.defaultType, targetClassDescriptor.defaultType)
?: TypeSubstitutor.EMPTY
members@ for (memberInfo in membersToMove) {
val member = memberInfo.member
val memberDescriptor = memberDescriptors[member] ?: continue
val movedMember = when (member) {
is JetProperty, is JetNamedFunction -> {
memberDescriptor as CallableMemberDescriptor
val targetMemberDescriptor = memberDescriptor.substitute(substitutor)?.let {
targetClassDescriptor.findCallableMemberBySignature(it as CallableMemberDescriptor)
}
val targetMember = targetMemberDescriptor?.source?.getPsi() as? JetCallableDeclaration
targetMember?.apply {
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
}
else if (memberDescriptor.overriddenDescriptors.isEmpty()) {
removeModifier(JetTokens.OVERRIDE_KEYWORD)
}
else {
addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
}
} ?: addMemberToTarget(member, targetClass).apply {
if (sourceClassDescriptor.kind == ClassKind.INTERFACE) {
if (targetClassDescriptor.kind != ClassKind.INTERFACE && memberDescriptor.modality == Modality.ABSTRACT) {
addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD)
}
}
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
if (hasModifier(JetTokens.PRIVATE_KEYWORD)) {
addModifierWithSpace(JetTokens.PROTECTED_KEYWORD)
}
addModifierWithSpace(JetTokens.OVERRIDE_KEYWORD)
}
}
}
is JetClassOrObject -> {
if (memberInfo.overrides != null) {
sourceClass.getDelegatorToSuperClassByDescriptor(memberDescriptor as ClassDescriptor, sourceClassContext)?.let {
addDelegatorToSuperClass(it, targetClass, targetClassDescriptor, sourceClassContext, substitutor)
}
continue@members
}
else {
addMemberToTarget(member, targetClass)
}
}
else -> continue@members
}
applyMarking(movedMember, substitutor, targetClassDescriptor)
}
}
private fun removeOriginalMembers() {
for (memberInfo in membersToMove) {
val member = memberInfo.member
val memberDescriptor = memberDescriptors[member] ?: continue
when (member) {
is JetProperty, is JetNamedFunction -> {
member as JetCallableDeclaration
memberDescriptor as CallableMemberDescriptor
if (memberDescriptor.modality != Modality.ABSTRACT && memberInfo.isToAbstract) {
if (member.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
member.addModifierWithSpace(JetTokens.PROTECTED_KEYWORD)
}
makeAbstract(member, memberDescriptor, TypeSubstitutor.EMPTY, sourceClass)
member.typeReference?.addToShorteningWaitSet()
}
else {
member.delete()
}
}
is JetClassOrObject -> {
if (memberInfo.overrides != null) {
sourceClass.getDelegatorToSuperClassByDescriptor(memberDescriptor as ClassDescriptor, sourceClassContext)?.let {
sourceClass.removeDelegationSpecifier(it)
}
}
else {
member.delete()
}
}
}
}
}
override fun performRefactoring(usages: Array<out UsageInfo>) {
val markedElements = ArrayList<JetElement>()
try {
membersToMove.forEach { markedElements += markElements(it.member, sourceClassContext, sourceClassDescriptor, null) }
usages.forEach { (it.element as? JetClassOrObject)?.let { pushDownToClass(it) } }
removeOriginalMembers()
}
finally {
clearMarking(markedElements)
}
}
}
+8
View File
@@ -0,0 +1,8 @@
class <caret>A {
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
fun foo() {
}
}
@@ -0,0 +1,2 @@
Cannot perform refactoring.
Class <b><code>A</code></b> is final
@@ -0,0 +1,12 @@
class X {
open class <caret>A {
// INFO: {"checked": "true"}
fun foo(): Int = bar() + A.bar() + X.A.bar()
companion object {
fun bar() = 1
}
}
}
class B : X.A
@@ -0,0 +1,13 @@
class X {
open class A {
companion object {
fun bar() = 1
}
}
}
class B : X.A {
// INFO: {"checked": "true"}
fun foo(): Int = X.A.bar() + X.A.bar() + X.A.bar()
}
+12
View File
@@ -0,0 +1,12 @@
open class <caret>A {
// INFO: {"checked": "true", "toAbstract": "true"}
private fun foo() {
}
}
class B : A {
fun foo() {
}
}
+10
View File
@@ -0,0 +1,10 @@
open class A {
// INFO: {"checked": "true", "toAbstract": "true"}
protected abstract fun foo()
}
class B : A {
override fun foo() {
}
}
+4
View File
@@ -0,0 +1,4 @@
class A {
val x: Int = 1
}
@@ -0,0 +1,2 @@
Cannot perform refactoring.
Caret should be positioned inside a class to push members from
@@ -0,0 +1,8 @@
object <caret>O {
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
fun foo() {
}
}
@@ -0,0 +1,2 @@
Cannot perform refactoring.
Object <b><code>O</code></b> is final
+3
View File
@@ -0,0 +1,3 @@
fun <caret>test() {
}
@@ -0,0 +1,2 @@
Cannot perform refactoring.
Caret should be positioned inside a class to push members from
+34
View File
@@ -0,0 +1,34 @@
abstract class <caret>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 {
}
}
abstract class B : A() {
}
class C : A() {
override val t = 1
override fun bar(s: String) = s.length()
}
@@ -0,0 +1,55 @@
abstract class A {
}
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 {
}
}
class C : A() {
val t = 1
// INFO: {"checked": "true"}
val x = 1
// INFO: {"checked": "true"}
val y: Int get() = 2
// INFO: {"checked": "true"}
val z: Int by lazy { 3 }
fun bar(s: String) = s.length()
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
inner class X {
}
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,34 @@
abstract class <caret>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 {
}
}
abstract class B : A() {
}
class C : A() {
override val t = 1
override fun bar(s: String) = s.length()
}
@@ -0,0 +1,64 @@
abstract class A {
// INFO: {"checked": "true", "toAbstract": "true"}
abstract val x: Int
// INFO: {"checked": "true", "toAbstract": "true"}
abstract val y: Int
// INFO: {"checked": "true", "toAbstract": "true"}
abstract val z: Int
// INFO: {"checked": "true", "toAbstract": "true"}
abstract fun foo(n: Int): Boolean
}
abstract class B : A() {
// INFO: {"checked": "true", "toAbstract": "true"}
override val x = 1
// INFO: {"checked": "true", "toAbstract": "true"}
override val y: Int get() = 2
// INFO: {"checked": "true", "toAbstract": "true"}
override 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"}
abstract fun bar(s: String)
// INFO: {"checked": "true", "toAbstract": "true"}
inner class X {
}
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
class C : A() {
val t = 1
// INFO: {"checked": "true", "toAbstract": "true"}
override val x = 1
// INFO: {"checked": "true", "toAbstract": "true"}
override val y: Int get() = 2
// INFO: {"checked": "true", "toAbstract": "true"}
override val z: Int by lazy { 3 }
fun bar(s: String) = s.length()
// 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,29 @@
interface Z<T>
open class <caret>A<T> {
// INFO: {"checked": "true"}
val t1: T
// INFO: {"checked": "true"}
val t2: Z<T>
// INFO: {"checked": "true"}
fun foo<S>(t1: T, t2: Z<T>, s1: S, s2: Z<S>): Boolean = true
// INFO: {"checked": "true"}
inner class X : Z<T> {
}
// INFO: {"checked": "true"}
class Y<U> : Z<U> {
}
}
class B<S> : A<Z<S>>() {
}
class C<K> : A<B<K>>() {
}
@@ -0,0 +1,47 @@
interface Z<T>
open class A<T> {
}
class B<S> : A<Z<S>>() {
// INFO: {"checked": "true"}
val t1: Z<S>
// INFO: {"checked": "true"}
val t2: Z<Z<S>>
// INFO: {"checked": "true"}
fun foo<S>(t1: Z<S>, t2: Z<Z<S>>, s1: S, s2: Z<S>): Boolean = true
// INFO: {"checked": "true"}
inner class X : Z<Z<S>> {
}
// INFO: {"checked": "true"}
class Y<U> : Z<U> {
}
}
class C<K> : A<B<K>>() {
// INFO: {"checked": "true"}
val t1: B<K>
// INFO: {"checked": "true"}
val t2: Z<B<K>>
// INFO: {"checked": "true"}
fun foo<S>(t1: B<K>, t2: Z<B<K>>, s1: S, s2: Z<S>): Boolean = true
// INFO: {"checked": "true"}
inner class X : Z<B<K>> {
}
// INFO: {"checked": "true"}
class Y<U> : Z<U> {
}
}
@@ -0,0 +1,31 @@
interface <caret>I {
// INFO: {"checked": "true"}
val x: Int get() = 2
// INFO: {"checked": "true"}
val y: Int
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
fun bar(s: String)
// INFO: {"checked": "true"}
class Y {
}
}
abstract class A : I
class B : I {
override val y = 1
override fun bar(s: String) = s.length()
}
interface J : I
interface K : I {
override val y: Int get() = 1
override fun bar(s: String) = s.length()
}
@@ -0,0 +1,69 @@
interface I {
}
abstract class A : I {
// INFO: {"checked": "true"}
val x: Int get() = 2
// INFO: {"checked": "true"}
abstract val y: Int
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
abstract fun bar(s: String)
// INFO: {"checked": "true"}
class Y {
}
}
class B : I {
val y = 1
// INFO: {"checked": "true"}
val x: Int get() = 2
fun bar(s: String) = s.length()
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
class Y {
}
}
interface J : I {
// INFO: {"checked": "true"}
val x: Int get() = 2
// INFO: {"checked": "true"}
val y: Int
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
fun bar(s: String)
// INFO: {"checked": "true"}
class Y {
}
}
interface K : I {
val y: Int get() = 1
// INFO: {"checked": "true"}
val x: Int get() = 2
fun bar(s: String) = s.length()
// INFO: {"checked": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true"}
class Y {
}
}
@@ -0,0 +1,31 @@
interface <caret>I {
// INFO: {"checked": "true", "toAbstract": "true"}
val x: Int get() = 2
// INFO: {"checked": "true", "toAbstract": "true"}
val y: Int
// INFO: {"checked": "true", "toAbstract": "true"}
fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true", "toAbstract": "true"}
fun bar(s: String)
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
abstract class A : I
class B : I {
override val y = 1
override fun bar(s: String) = s.length()
}
interface J : I
interface K : I {
override val y: Int get() = 1
override fun bar(s: String) = s.length()
}
@@ -0,0 +1,74 @@
interface I {
// INFO: {"checked": "true", "toAbstract": "true"}
val x: Int
// INFO: {"checked": "true", "toAbstract": "true"}
fun foo(n: Int): Boolean
}
abstract class A : I {
// INFO: {"checked": "true", "toAbstract": "true"}
override val x: Int get() = 2
// INFO: {"checked": "true", "toAbstract": "true"}
abstract val y: Int
// INFO: {"checked": "true", "toAbstract": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true", "toAbstract": "true"}
abstract fun bar(s: String)
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
class B : I {
val y = 1
// INFO: {"checked": "true", "toAbstract": "true"}
override val x: Int get() = 2
fun bar(s: String) = s.length()
// INFO: {"checked": "true", "toAbstract": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
interface J : I {
// INFO: {"checked": "true", "toAbstract": "true"}
override val x: Int get() = 2
// INFO: {"checked": "true", "toAbstract": "true"}
val y: Int
// INFO: {"checked": "true", "toAbstract": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true", "toAbstract": "true"}
fun bar(s: String)
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
interface K : I {
val y: Int get() = 1
// INFO: {"checked": "true", "toAbstract": "true"}
override val x: Int get() = 2
fun bar(s: String) = s.length()
// INFO: {"checked": "true", "toAbstract": "true"}
override fun foo(n: Int): Boolean = n > 0
// INFO: {"checked": "true", "toAbstract": "true"}
class Y {
}
}
@@ -0,0 +1,12 @@
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "true"}
interface Y
// INFO: {"checked": "true"}
interface Z
open class <caret>A : X, Y, Z
class B : A(), Z
@@ -0,0 +1,12 @@
// INFO: {"checked": "true"}
interface X
// INFO: {"checked": "true"}
interface Y
// INFO: {"checked": "true"}
interface Z
open class A
class B : A(), Z, X, Y
@@ -0,0 +1,14 @@
interface I
// INFO: {"checked": "true"}
interface X<T>
// INFO: {"checked": "true"}
interface Y<T>
// INFO: {"checked": "true"}
interface Z<T>
open class <caret>A<T, U> : X<T>, Y<X<I>>, Z<Y<U>>
class B<S> : A<X<S>, Y<S>>(), Z<I>
@@ -0,0 +1,14 @@
interface I
// INFO: {"checked": "true"}
interface X<T>
// INFO: {"checked": "true"}
interface Y<T>
// INFO: {"checked": "true"}
interface Z<T>
open class A<T, U>
class B<S> : A<X<S>, Y<S>>(), Z<I>, X<X<S>>, Y<X<I>>
@@ -0,0 +1,110 @@
/*
* 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
import com.google.gson.JsonParser
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.classMembers.MemberInfoBase
import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.JetTestUtils
import org.jetbrains.kotlin.test.util.findElementsByCommentPrefix
import java.io.File
public abstract class AbstractMemberPullPushTest : JetLightCodeInsightFixtureTestCase() {
private data class ElementInfo(val checked: Boolean, val toAbstract: Boolean)
companion object {
private var PsiElement.elementInfo: ElementInfo
by NotNullableUserDataProperty(Key.create("ELEMENT_INFO"), ElementInfo(false, false))
}
override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
val fixture: JavaCodeInsightTestFixture get() = myFixture
protected override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase()
protected fun doTest(path: String, action: (mainFile: PsiFile) -> Unit) {
val mainFile = File(path)
val afterFile = File("$path.after")
val conflictFile = File("$path.messages")
fixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}"
val mainFileName = mainFile.getName()
val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName)
val extraFiles = mainFile.parentFile.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)
val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// WITH_RUNTIME") != null
if (addKotlinRuntime) {
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
try {
for ((element, info) in file.findElementsByCommentPrefix("// INFO: ")) {
val parsedInfo = JsonParser().parse(info).asJsonObject
element.elementInfo = ElementInfo(parsedInfo["checked"]?.asBoolean ?: false,
parsedInfo["toAbstract"]?.asBoolean ?: false)
}
action(file)
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.text)
}
}
catch(e: Exception) {
val message = when (e) {
is BaseRefactoringProcessor.ConflictsInTestsException -> e.messages.sort().joinToString("\n")
is CommonRefactoringUtil.RefactoringErrorHintException -> e.getMessage()!!
else -> throw e
}
JetTestUtils.assertEqualsToFile(conflictFile, message)
}
finally {
if (addKotlinRuntime) {
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
}
}
protected fun chooseMembers<T : MemberInfoBase<*>>(members: List<T>): List<T> {
members.forEach {
val info = it.member.elementInfo
it.isChecked = info.checked
it.isToAbstract = info.toAbstract
}
return members.filter { it.isChecked }
}
}
@@ -16,116 +16,29 @@
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.*
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.classMembers.MemberInfoBase
import com.intellij.refactoring.memberPullUp.PullUpConflictsUtil
import com.intellij.refactoring.memberPullUp.PullUpProcessor
import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.refactoring.util.DocCommentPolicy
import com.intellij.refactoring.util.RefactoringHierarchyUtil
import com.intellij.refactoring.util.classMembers.MemberInfoStorage
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.refactoring.AbstractMemberPullPushTest
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.JetWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.JetTestUtils
import org.jetbrains.kotlin.test.util.findElementsByCommentPrefix
import java.io.File
public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase() {
private data class ElementInfo(val checked: Boolean, val toAbstract: Boolean)
companion object {
private var PsiElement.elementInfo: ElementInfo
by NotNullableUserDataProperty(Key.create("ELEMENT_INFO"), ElementInfo(false, false))
}
override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
val fixture: JavaCodeInsightTestFixture get() = myFixture
protected override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase()
protected fun doTest(path: String, action: (mainFile: PsiFile) -> Unit) {
val mainFile = File(path)
val afterFile = File("$path.after")
val conflictFile = File("$path.messages")
fixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}"
val mainFileName = mainFile.getName()
val mainFileBaseName = FileUtil.getNameWithoutExtension(mainFileName)
val extraFiles = mainFile.parentFile.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)
val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// WITH_RUNTIME") != null
if (addKotlinRuntime) {
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
try {
for ((element, info) in file.findElementsByCommentPrefix("// INFO: ")) {
val parsedInfo = JsonParser().parse(info).asJsonObject
element.elementInfo = ElementInfo(parsedInfo["checked"]?.asBoolean ?: false,
parsedInfo["toAbstract"]?.asBoolean ?: false)
}
action(file)
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.text)
}
}
catch(e: Exception) {
val message = when (e) {
is BaseRefactoringProcessor.ConflictsInTestsException -> e.messages.sort().joinToString("\n")
is CommonRefactoringUtil.RefactoringErrorHintException -> e.getMessage()!!
else -> throw e
}
JetTestUtils.assertEqualsToFile(conflictFile, message)
}
finally {
if (addKotlinRuntime) {
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
}
}
public abstract class AbstractPullUpTest : AbstractMemberPullPushTest() {
private fun getTargetClassName(file: PsiFile) = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// TARGET_CLASS: ")
private fun chooseMembers<T : MemberInfoBase<*>>(members: List<T>): List<T> {
members.forEach {
val info = it.member.elementInfo
it.isChecked = info.checked
it.isToAbstract = info.toAbstract
}
return members.filter { it.isChecked }
}
protected fun doKotlinTest(path: String) {
doTest(path) { file ->
val targetClassName = getTargetClassName(file)
val helper = object: KotlinPullUpHandler.TestHelper {
override fun adjustMembers(members: List<KotlinMemberInfo>): List<KotlinMemberInfo> {
return chooseMembers(members)
}
override fun adjustMembers(members: List<KotlinMemberInfo>) = chooseMembers(members)
override fun chooseSuperClass(superClasses: List<PsiNamedElement>): PsiNamedElement {
if (targetClassName != null) {
@@ -135,7 +48,7 @@ public abstract class AbstractPullUpTest : JetLightCodeInsightFixtureTestCase()
}
}
KotlinPullUpHandler().invoke(getProject(), getEditor(), file) {
if (it == KotlinPullUpHandler.PULLUP_TEST_HELPER_KEY) helper else null
if (it == KotlinPullUpHandler.PULL_UP_TEST_HELPER_KEY) helper else null
}
}
}
@@ -0,0 +1,33 @@
/*
* 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.pushDown
import org.jetbrains.kotlin.idea.refactoring.AbstractMemberPullPushTest
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo
public abstract class AbstractPushDownTest : AbstractMemberPullPushTest() {
protected fun doTest(path: String) {
doTest(path) { file ->
val helper = object: KotlinPushDownHandler.TestHelper {
override fun adjustMembers(members: List<KotlinMemberInfo>) = chooseMembers(members)
}
KotlinPushDownHandler().invoke(getProject(), getEditor(), file) {
if (it == KotlinPushDownHandler.PUSH_DOWN_TEST_HELPER_KEY) helper else null
}
}
}
}
@@ -0,0 +1,115 @@
/*
* 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.pushDown;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.JetTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/refactoring/pushDown")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class PushDownTestGenerated extends AbstractPushDownTest {
public void testAllFilesPresentInPushDown() throws Exception {
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/pushDown"), Pattern.compile("^(.+)\\.kt$"));
}
@TestMetadata("finalClass.kt")
public void testFinalClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/finalClass.kt");
doTest(fileName);
}
@TestMetadata("implicitCompanionUsages.kt")
public void testImplicitCompanionUsages() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/implicitCompanionUsages.kt");
doTest(fileName);
}
@TestMetadata("liftPrivate.kt")
public void testLiftPrivate() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/liftPrivate.kt");
doTest(fileName);
}
@TestMetadata("noCaret.kt")
public void testNoCaret() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/noCaret.kt");
doTest(fileName);
}
@TestMetadata("objectDeclaration.kt")
public void testObjectDeclaration() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/objectDeclaration.kt");
doTest(fileName);
}
@TestMetadata("outsideOfClass.kt")
public void testOutsideOfClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/outsideOfClass.kt");
doTest(fileName);
}
@TestMetadata("pushClassMembers.kt")
public void testPushClassMembers() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushClassMembers.kt");
doTest(fileName);
}
@TestMetadata("pushClassMembersAndMakeAbstract.kt")
public void testPushClassMembersAndMakeAbstract() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushClassMembersAndMakeAbstract.kt");
doTest(fileName);
}
@TestMetadata("pushClassMembersWithGenerics.kt")
public void testPushClassMembersWithGenerics() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushClassMembersWithGenerics.kt");
doTest(fileName);
}
@TestMetadata("pushInterfaceMembers.kt")
public void testPushInterfaceMembers() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushInterfaceMembers.kt");
doTest(fileName);
}
@TestMetadata("pushInterfaceMembersAndMakeAbstract.kt")
public void testPushInterfaceMembersAndMakeAbstract() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushInterfaceMembersAndMakeAbstract.kt");
doTest(fileName);
}
@TestMetadata("pushSuperInterfaces.kt")
public void testPushSuperInterfaces() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushSuperInterfaces.kt");
doTest(fileName);
}
@TestMetadata("pushSuperInterfacesWithGenerics.kt")
public void testPushSuperInterfacesWithGenerics() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pushDown/pushSuperInterfacesWithGenerics.kt");
doTest(fileName);
}
}