From 1a09741c0a6454db48afd07848974715829ed367 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Mon, 20 Jul 2015 14:31:59 +0300 Subject: [PATCH] Pull Up: Move property initializers --- .../kotlin/psi/psiUtil/jetPsiUtil.kt | 2 + .../refactoring/pullUp/KotlinPullUpHelper.kt | 325 +++++++++++++++++- .../fromClassToClassWithGenerics.kt.after | 4 +- .../pullUp/initializerInConstructor.kt | 10 + .../pullUp/initializerInConstructor.kt.after | 14 + .../initializerInMultipleConstructorsEq.kt | 14 + ...itializerInMultipleConstructorsEq.kt.after | 17 + .../initializerInMultipleConstructorsNonEq.kt | 14 + ...alizerInMultipleConstructorsNonEq.kt.after | 15 + .../multipleInitializersInConstructorsEq.kt | 11 + ...tipleInitializersInConstructorsEq.kt.after | 15 + .../pullUp/noInitializationInInterface.kt | 10 + .../noInitializationInInterface.kt.after | 13 + .../pullUp/parametersInPrimaryInitializer.kt | 6 + .../parametersInPrimaryInitializer.kt.after | 7 + .../pullUp/propertyDependenceSatisfied.kt | 20 ++ .../propertyDependenceSatisfied.kt.after | 22 ++ .../pullUp/propertyDependenceUnsatisfied.kt | 20 ++ .../propertyDependenceUnsatisfied.kt.after | 20 ++ .../pullUp/PullUpTestGenerated.java | 48 +++ 20 files changed, 587 insertions(+), 20 deletions(-) create mode 100644 idea/testData/refactoring/pullUp/initializerInConstructor.kt create mode 100644 idea/testData/refactoring/pullUp/initializerInConstructor.kt.after create mode 100644 idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt create mode 100644 idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt.after create mode 100644 idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt create mode 100644 idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt.after create mode 100644 idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt create mode 100644 idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt.after create mode 100644 idea/testData/refactoring/pullUp/noInitializationInInterface.kt create mode 100644 idea/testData/refactoring/pullUp/noInitializationInInterface.kt.after create mode 100644 idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt create mode 100644 idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt.after create mode 100644 idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt create mode 100644 idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt.after create mode 100644 idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt create mode 100644 idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index eb4a8d949dc..05653359631 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -392,4 +392,6 @@ public fun JetFunctionLiteralArgument.getFunctionLiteralArgumentName(bindingCont return (resolvedCall?.getArgumentMapping(this) as? ArgumentMatch)?.valueParameter?.getName() } +public fun JetExpression.asAssignment(): JetBinaryExpression? = + if (JetPsiUtil.isAssignment(this)) this as JetBinaryExpression else null 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 51d17a3b53d..1d2083b93f1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/pullUp/KotlinPullUpHelper.kt @@ -16,22 +16,21 @@ package org.jetbrains.kotlin.idea.refactoring.pullUp +import com.intellij.lang.Language import com.intellij.openapi.util.Key -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiField -import com.intellij.psi.PsiMember -import com.intellij.psi.PsiSubstitutor +import com.intellij.psi.* +import com.intellij.psi.impl.light.LightField import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.classMembers.MemberInfoBase import com.intellij.refactoring.memberPullUp.PullUpData import com.intellij.refactoring.memberPullUp.PullUpHelper -import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod -import org.jetbrains.kotlin.asJava.namedUnwrappedElement -import org.jetbrains.kotlin.asJava.toLightClass +import com.intellij.refactoring.util.RefactoringUtil +import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.asJava.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor +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.replaced import org.jetbrains.kotlin.idea.imports.importableFqName @@ -39,23 +38,23 @@ 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.JetModifierKeywordToken import org.jetbrains.kotlin.lexer.JetTokens 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.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.psi.psiUtil.* 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 org.jetbrains.kotlin.util.findCallableMemberBySignature -import java.util.ArrayList -import java.util.LinkedHashSet +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.kotlin.utils.keysToMap +import java.util.* class KotlinPullUpHelper( private val javaData: PullUpData, @@ -67,6 +66,212 @@ class KotlinPullUpHelper( private var JetElement.newTypeText: String? by CopyableUserDataProperty(Key.create("NEW_TYPE_TEXT")) } + private fun JetExpression.isMovable(): Boolean { + return accept( + object: JetVisitor() { + override fun visitJetElement(element: JetElement, arg: Nothing?): Boolean { + return element.allChildren.all { (it as? JetElement)?.accept(this, arg) ?: true } + } + + override fun visitJetFile(file: JetFile, data: Nothing?) = false + + override fun visitSimpleNameExpression(expression: JetSimpleNameExpression, arg: Nothing?): Boolean { + val resolvedCall = expression.getResolvedCall(data.resolutionFacade.analyze(expression)) ?: return true + val receiver = (resolvedCall.getExplicitReceiverValue() as? ExpressionReceiver)?.getExpression() + if (receiver != null && receiver !is JetThisExpression && receiver !is JetSuperExpression) return true + + var descriptor: DeclarationDescriptor = resolvedCall.getResultingDescriptor() + if (descriptor is ConstructorDescriptor) { + descriptor = descriptor.getContainingDeclaration() + } + // todo: local functions + if (descriptor is ValueParameterDescriptor) return true + if (descriptor is ClassDescriptor && !descriptor.isInner()) return true + if (descriptor is MemberDescriptor) { + if (descriptor.getSource().getPsi() in propertiesToMoveInitializers) return true + descriptor = descriptor.getContainingDeclaration() + } + return descriptor is PackageFragmentDescriptor + || (descriptor is ClassDescriptor && DescriptorUtils.isSubclass(data.targetClassDescriptor, descriptor)) + } + }, + null + ) + } + + private fun getCommonInitializer( + currentInitializer: JetExpression?, + scope: JetBlockExpression?, + propertyDescriptor: PropertyDescriptor, + elementsToRemove: MutableSet): JetExpression? { + if (scope == null) return currentInitializer + + var initializerCandidate: JetExpression? = null + + for (statement in scope.getStatements()) { + statement.asAssignment()?.let body@ { + val lhs = JetPsiUtil.safeDeparenthesize(it.getLeft() ?: return@body) + val receiver = (lhs as? JetQualifiedExpression)?.getReceiverExpression() + if (receiver != null && receiver !is JetThisExpression) return@body + + val resolvedCall = lhs.getResolvedCall(data.resolutionFacade.analyze(it)) ?: return@body + if (resolvedCall.getResultingDescriptor() != propertyDescriptor) return@body + + if (initializerCandidate == null) { + if (currentInitializer == null) { + if (!statement.isMovable()) return null + + initializerCandidate = statement + elementsToRemove.add(statement) + } + else { + if (!JetPsiUnifier.DEFAULT.unify(statement, currentInitializer).matched) return null + + initializerCandidate = currentInitializer + elementsToRemove.add(statement) + } + } + else if (!JetPsiUnifier.DEFAULT.unify(statement, initializerCandidate).matched) return null + } + } + + return initializerCandidate + } + + private data class InitializerInfo( + val initializer: JetExpression?, + val usedProperties: Set, + val usedParameters: Set, + val elementsToRemove: Set + ) + + private fun getInitializerInfo(property: JetProperty, + propertyDescriptor: PropertyDescriptor, + targetConstructor: JetElement): InitializerInfo? { + val sourceConstructors = targetToSourceConstructors[targetConstructor] ?: return null + val elementsToRemove = LinkedHashSet() + val commonInitializer = sourceConstructors.fold(null as JetExpression?) { commonInitializer, constructor -> + val body = (constructor as? JetSecondaryConstructor)?.getBodyExpression() + getCommonInitializer(commonInitializer, body, propertyDescriptor, elementsToRemove) + } + if (commonInitializer == null) { + elementsToRemove.clear() + } + + val usedProperties = LinkedHashSet() + val usedParameters = LinkedHashSet() + val visitor = object : JetTreeVisitorVoid() { + override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) { + val context = data.resolutionFacade.analyze(expression) + val resolvedCall = expression.getResolvedCall(context) ?: return + val receiver = (resolvedCall.getExplicitReceiverValue() as? ExpressionReceiver)?.getExpression() + if (receiver != null && receiver !is JetThisExpression) return + val target = (resolvedCall.getResultingDescriptor() as? DeclarationDescriptorWithSource)?.getSource()?.getPsi() + when (target) { + is JetParameter -> usedParameters.add(target) + is JetProperty -> usedProperties.add(target) + } + } + } + commonInitializer?.accept(visitor) + if (targetConstructor == data.targetClass.getPrimaryConstructor() ?: data.targetClass) { + property.getInitializer()?.accept(visitor) + } + + return InitializerInfo(commonInitializer, usedProperties, usedParameters, elementsToRemove) + } + + private val propertiesToMoveInitializers = with(data) { + membersToMove + .filterIsInstance() + .filter { + val descriptor = memberDescriptors[it] as? PropertyDescriptor + descriptor != null && data.sourceClassContext[BindingContext.BACKING_FIELD_REQUIRED, descriptor] ?: false + } + } + + private val targetToSourceConstructors = LinkedHashMap>().let { result -> + if (!data.targetClass.isInterface()) { + result[data.targetClass.getPrimaryConstructor() ?: data.targetClass] = ArrayList() + data.sourceClass.accept( + object : JetTreeVisitorVoid() { + private fun processConstructorReference(expression: JetReferenceExpression, callingConstructorElement: JetElement) { + val descriptor = data.resolutionFacade.analyze(expression)[BindingContext.REFERENCE_TARGET, expression] + val constructorElement = (descriptor as? DeclarationDescriptorWithSource)?.getSource()?.getPsi() ?: return + if (constructorElement == data.targetClass + || (constructorElement as? JetConstructor<*>)?.getContainingClassOrObject() == data.targetClass) { + result.getOrPut(constructorElement as JetElement, { ArrayList() }).add(callingConstructorElement) + } + } + + override fun visitDelegationToSuperCallSpecifier(specifier: JetDelegatorToSuperCall) { + val constructorRef = specifier.getCalleeExpression().getConstructorReferenceExpression() ?: return + val containingClass = specifier.getStrictParentOfType() ?: return + val callingConstructorElement = containingClass.getPrimaryConstructor() ?: containingClass + processConstructorReference(constructorRef, callingConstructorElement) + } + + override fun visitSecondaryConstructor(constructor: JetSecondaryConstructor) { + val constructorRef = constructor.getDelegationCall().getCalleeExpression() ?: return + processConstructorReference(constructorRef, constructor) + } + } + ) + } + result + } + + private val targetConstructorToPropertyInitializerInfoMap = LinkedHashMap>().let { result -> + for (targetConstructor in targetToSourceConstructors.keySet()) { + val propertyToInitializerInfo = LinkedHashMap() + for (property in propertiesToMoveInitializers) { + val propertyDescriptor = data.memberDescriptors[property] as? PropertyDescriptor ?: continue + propertyToInitializerInfo[property] = getInitializerInfo(property, propertyDescriptor, targetConstructor) ?: continue + } + val unmovableProperties = RefactoringUtil.transitiveClosure( + object : RefactoringUtil.Graph { + override fun getVertices() = propertyToInitializerInfo.keySet() + + override fun getTargets(source: JetProperty) = propertyToInitializerInfo[source]?.usedProperties + }, + { !propertyToInitializerInfo.containsKey(it) } + ) + + propertyToInitializerInfo.keySet().removeAll(unmovableProperties) + result[targetConstructor] = propertyToInitializerInfo + } + result + } + + private var dummyField: PsiField? = null + + private fun addMovedMember(newMember: JetNamedDeclaration) { + if (newMember is JetProperty) { + // Add dummy light field since PullUpProcessor won't invoke moveFieldInitializations() if no PsiFields are present + if (dummyField == null) { + val factory = JavaPsiFacade.getElementFactory(newMember.getProject()) + val dummyField = object: LightField( + newMember.getManager(), + factory.createField("dummy", PsiType.BOOLEAN), + factory.createClass("Dummy") + ) { + // Prevent processing by JavaPullUpHelper + override fun getLanguage() = JetLanguage.INSTANCE + } + javaData.getMovedMembers().add(dummyField) + } + } + + when (newMember) { + is JetProperty, is JetNamedFunction -> { + newMember.getRepresentativeLightMethod()?.let { javaData.getMovedMembers().add(it) } + } + is JetClassOrObject -> { + newMember.toLightClass()?.let { javaData.getMovedMembers().add(it) } + } + } + } + private fun willBeUsedInSourceClass(member: PsiElement): Boolean { return !ReferencesSearch .search(member, LocalSearchScope(data.sourceClass), false) @@ -351,8 +556,6 @@ class KotlinPullUpHelper( && (movedMember.hasModifier(JetTokens.ABSTRACT_KEYWORD))) { data.targetClass.addModifierWithSpace(JetTokens.ABSTRACT_KEYWORD) } - - movedMember.getRepresentativeLightMethod()?.let { javaData.getMovedMembers().add(it) } } is JetClassOrObject -> { @@ -361,13 +564,14 @@ class KotlinPullUpHelper( } movedMember = addMemberToTarget(memberCopy) as JetClassOrObject member.delete() - movedMember.toLightClass()?.let { javaData.getMovedMembers().add(it) } } else -> return } processMarkedElements(movedMember) + + addMovedMember(movedMember) } finally { clearMarking(markedElements) @@ -379,7 +583,92 @@ class KotlinPullUpHelper( } override fun moveFieldInitializations(movedFields: LinkedHashSet) { + val psiFactory = JetPsiFactory(data.sourceClass) + fun JetClassOrObject.getOrCreateClassInitializer(): JetClassInitializer { + getOrCreateBody().getDeclarations().lastOrNull { it is JetClassInitializer }?.let { return it as JetClassInitializer } + return addDeclaration(psiFactory.createAnonymousInitializer()) as JetClassInitializer + } + + fun JetElement.getConstructorBodyBlock(): JetBlockExpression? { + return when (this) { + is JetClassOrObject -> { + getOrCreateClassInitializer().getBody() + } + is JetPrimaryConstructor -> { + getContainingClassOrObject().getOrCreateClassInitializer().getBody() + } + is JetSecondaryConstructor -> { + getBodyExpression() ?: add(psiFactory.createEmptyBody()) + } + else -> null + } as? JetBlockExpression + } + + fun JetClassOrObject.getDelegatorToSuperCall(): JetDelegatorToSuperCall? { + return getDelegationSpecifiers().singleOrNull { it is JetDelegatorToSuperCall } as? JetDelegatorToSuperCall + } + + fun addUsedParameters(constructorElement: JetElement, info: InitializerInfo) { + if (!info.usedParameters.isNotEmpty()) return + val constructor: JetConstructor<*> = when (constructorElement) { + is JetConstructor<*> -> constructorElement + is JetClass -> constructorElement.createPrimaryConstructorIfAbsent() + else -> return + } + + with(constructor.getValueParameterList()!!) { + info.usedParameters.forEach { addParameter(it) } + } + targetToSourceConstructors[constructorElement]!!.forEach { + val superCall: JetCallElement? = when (it) { + is JetClassOrObject -> it.getDelegatorToSuperCall() + is JetPrimaryConstructor -> it.getContainingClassOrObject().getDelegatorToSuperCall() + is JetSecondaryConstructor -> { + if (it.hasImplicitDelegationCall()) { + it.replaceImplicitDelegationCallWithExplicit(false) + } + else { + it.getDelegationCall() + } + } + else -> null + } + superCall?.getValueArgumentList()?.let { args -> + info.usedParameters.forEach { + args.addArgument(psiFactory.createArgument(psiFactory.createExpression(it.getName() ?: "_"))) + } + } + } + } + + for ((constructorElement, propertyToInitializerInfo) in targetConstructorToPropertyInitializerInfoMap.entrySet()) { + val properties = propertyToInitializerInfo.keySet().toArrayList().sortBy( + object : Comparator { + override fun compare(property1: JetProperty, property2: JetProperty): Int { + val info1 = propertyToInitializerInfo[property1]!! + val info2 = propertyToInitializerInfo[property2]!! + return when { + property2 in info1.usedProperties -> -1 + property1 in info2.usedProperties -> 1 + else -> 0 + } + } + } + ) + + for (oldProperty in properties) { + val info = propertyToInitializerInfo[oldProperty]!! + + addUsedParameters(constructorElement, info) + + info.initializer?.let { + val body = constructorElement.getConstructorBodyBlock() + body?.addAfter(it, body.getStatements().lastOrNull() ?: body.getLBrace()!!) + } + info.elementsToRemove.forEach { it.delete() } + } + } } override fun updateUsage(element: PsiElement) { diff --git a/idea/testData/refactoring/pullUp/fromClassToClassWithGenerics.kt.after b/idea/testData/refactoring/pullUp/fromClassToClassWithGenerics.kt.after index 2436eb3cc94..4e538d0b3ac 100644 --- a/idea/testData/refactoring/pullUp/fromClassToClassWithGenerics.kt.after +++ b/idea/testData/refactoring/pullUp/fromClassToClassWithGenerics.kt.after @@ -3,7 +3,7 @@ interface I interface Z -abstract class A { +abstract class A(x: X, y: Y) { // INFO: {"checked": "true"} val foo1: T // INFO: {"checked": "true"} @@ -33,7 +33,7 @@ abstract class A { } class C { - inner class B(x: X, y: Y): A>() { + inner class B(x: X, y: Y): A>(x, y) { // INFO: {"checked": "true", "toAbstract": "true"} override val foo5 = x diff --git a/idea/testData/refactoring/pullUp/initializerInConstructor.kt b/idea/testData/refactoring/pullUp/initializerInConstructor.kt new file mode 100644 index 00000000000..21812dad403 --- /dev/null +++ b/idea/testData/refactoring/pullUp/initializerInConstructor.kt @@ -0,0 +1,10 @@ +open class A + +class B: A { + // INFO: {"checked": "true"} + val n: Int + + constructor(a: Int) { + n = a + 1 + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/initializerInConstructor.kt.after b/idea/testData/refactoring/pullUp/initializerInConstructor.kt.after new file mode 100644 index 00000000000..3483ebd763e --- /dev/null +++ b/idea/testData/refactoring/pullUp/initializerInConstructor.kt.after @@ -0,0 +1,14 @@ +open class A(a: Int) { + // INFO: {"checked": "true"} + val n: Int + + init { + n = a + 1 + } +} + +class B: A { + + constructor(a: Int) : super(a) { + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt new file mode 100644 index 00000000000..501558bb72f --- /dev/null +++ b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt @@ -0,0 +1,14 @@ +open class A + +class B: A { + // INFO: {"checked": "true"} + val n: Int + + constructor(a: Int) { + n = 1 + 2 + } + + constructor() { + n = 1 + 2 + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt.after b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt.after new file mode 100644 index 00000000000..9336880ea2a --- /dev/null +++ b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt.after @@ -0,0 +1,17 @@ +open class A { + // INFO: {"checked": "true"} + val n: Int + + init { + n = 1 + 2 + } +} + +class B: A { + + constructor(a: Int) { + } + + constructor() { + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt new file mode 100644 index 00000000000..69f12a02785 --- /dev/null +++ b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt @@ -0,0 +1,14 @@ +open class A + +class B: A { + // INFO: {"checked": "true"} + val n: Int + + constructor(a: Int) { + n = a + 1 + } + + constructor(a: Int, b: Int) { + n = a*b + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt.after b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt.after new file mode 100644 index 00000000000..61c2e1e49b5 --- /dev/null +++ b/idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt.after @@ -0,0 +1,15 @@ +open class A { + // INFO: {"checked": "true"} + val n: Int +} + +class B: A { + + constructor(a: Int) { + n = a + 1 + } + + constructor(a: Int, b: Int) { + n = a*b + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt b/idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt new file mode 100644 index 00000000000..60fed0de2f3 --- /dev/null +++ b/idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt @@ -0,0 +1,11 @@ +open class A + +class B: A { + // INFO: {"checked": "true"} + var n: Int + + constructor(a: Int) { + n = a + 1 + n = a plus 1 + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt.after b/idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt.after new file mode 100644 index 00000000000..18e29bbb3f2 --- /dev/null +++ b/idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt.after @@ -0,0 +1,15 @@ +open class A(a: Int) { + // INFO: {"checked": "true"} + var n: Int + + init { + n = a + 1 + } +} + +class B: A { + + constructor(a: Int) : super(a) { + n = a plus 1 + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/noInitializationInInterface.kt b/idea/testData/refactoring/pullUp/noInitializationInInterface.kt new file mode 100644 index 00000000000..ee126deaacd --- /dev/null +++ b/idea/testData/refactoring/pullUp/noInitializationInInterface.kt @@ -0,0 +1,10 @@ +interface T + +class A: T { + // INFO: {"checked": "true"} + val n: Int + + constructor(a: Int) { + n = a + 1 + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/noInitializationInInterface.kt.after b/idea/testData/refactoring/pullUp/noInitializationInInterface.kt.after new file mode 100644 index 00000000000..9630899c742 --- /dev/null +++ b/idea/testData/refactoring/pullUp/noInitializationInInterface.kt.after @@ -0,0 +1,13 @@ +interface T { + // INFO: {"checked": "true"} + val n: Int +} + +class A: T { + // INFO: {"checked": "true"} + override val n: Int + + constructor(a: Int) { + n = a + 1 + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt b/idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt new file mode 100644 index 00000000000..f8f5e696b9b --- /dev/null +++ b/idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt @@ -0,0 +1,6 @@ +open class A + +class B(a: Int): A() { + // INFO: {"checked": "true"} + val n: Int = a + 1 +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt.after b/idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt.after new file mode 100644 index 00000000000..ec778db8dd8 --- /dev/null +++ b/idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt.after @@ -0,0 +1,7 @@ +open class A(a: Int) { + // INFO: {"checked": "true"} + val n: Int = a + 1 +} + +class B(a: Int): A(a) { +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt b/idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt new file mode 100644 index 00000000000..17603f62745 --- /dev/null +++ b/idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt @@ -0,0 +1,20 @@ +open class A + +class B: A { + // INFO: {"checked": "true"} + val n: Int + + // INFO: {"checked": "true"} + val x: Int = 3 + // INFO: {"checked": "true"} + val y: Int = 4 + + // INFO: {"checked": "true"} + val a: Int = 1 + // INFO: {"checked": "true"} + val b: Int = x + y + + constructor(p: Int) { + n = a + b - p + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt.after b/idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt.after new file mode 100644 index 00000000000..8f23872e280 --- /dev/null +++ b/idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt.after @@ -0,0 +1,22 @@ +open class A(p: Int) { + // INFO: {"checked": "true"} + val n: Int + // INFO: {"checked": "true"} + val x: Int = 3 + // INFO: {"checked": "true"} + val y: Int = 4 + // INFO: {"checked": "true"} + val a: Int = 1 + // INFO: {"checked": "true"} + val b: Int = x + y + + init { + n = a + b - p + } +} + +class B: A { + + constructor(p: Int) : super(p) { + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt b/idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt new file mode 100644 index 00000000000..5db36aead40 --- /dev/null +++ b/idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt @@ -0,0 +1,20 @@ +open class A + +class B: A { + // INFO: {"checked": "true"} + val n: Int + + // INFO: {"checked": "true"} + val x: Int = 3 + // INFO: {"checked": "false"} + val y: Int = 4 + + // INFO: {"checked": "true"} + val a: Int = 1 + // INFO: {"checked": "true"} + val b: Int = x + y + + constructor(p: Int) { + n = a + b - p + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt.after b/idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt.after new file mode 100644 index 00000000000..eb31bc94b4a --- /dev/null +++ b/idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt.after @@ -0,0 +1,20 @@ +open class A { + // INFO: {"checked": "true"} + val n: Int + // INFO: {"checked": "true"} + val x: Int = 3 + // INFO: {"checked": "true"} + val a: Int = 1 + // INFO: {"checked": "true"} + val b: Int = x + y +} + +class B: A { + + // INFO: {"checked": "false"} + val y: Int = 4 + + constructor(p: Int) { + n = a + b - p + } +} \ No newline at end of file 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 c98bdd7daa4..4fd1518c89e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/PullUpTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/pullUp/PullUpTestGenerated.java @@ -89,12 +89,36 @@ public class PullUpTestGenerated extends AbstractPullUpTest { doTest(fileName); } + @TestMetadata("initializerInConstructor.kt") + public void testInitializerInConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/initializerInConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("initializerInMultipleConstructorsEq.kt") + public void testInitializerInMultipleConstructorsEq() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/initializerInMultipleConstructorsEq.kt"); + doTest(fileName); + } + + @TestMetadata("initializerInMultipleConstructorsNonEq.kt") + public void testInitializerInMultipleConstructorsNonEq() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/initializerInMultipleConstructorsNonEq.kt"); + doTest(fileName); + } + @TestMetadata("innerClassToInterface.kt") public void testInnerClassToInterface() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/innerClassToInterface.kt"); doTest(fileName); } + @TestMetadata("multipleInitializersInConstructorsEq.kt") + public void testMultipleInitializersInConstructorsEq() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/multipleInitializersInConstructorsEq.kt"); + doTest(fileName); + } + @TestMetadata("noCaret.kt") public void testNoCaret() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/noCaret.kt"); @@ -107,6 +131,12 @@ public class PullUpTestGenerated extends AbstractPullUpTest { doTest(fileName); } + @TestMetadata("noInitializationInInterface.kt") + public void testNoInitializationInInterface() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/noInitializationInInterface.kt"); + doTest(fileName); + } + @TestMetadata("noSuperClass.kt") public void testNoSuperClass() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/noSuperClass.kt"); @@ -119,6 +149,24 @@ public class PullUpTestGenerated extends AbstractPullUpTest { doTest(fileName); } + @TestMetadata("parametersInPrimaryInitializer.kt") + public void testParametersInPrimaryInitializer() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/parametersInPrimaryInitializer.kt"); + doTest(fileName); + } + + @TestMetadata("propertyDependenceSatisfied.kt") + public void testPropertyDependenceSatisfied() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/propertyDependenceSatisfied.kt"); + doTest(fileName); + } + + @TestMetadata("propertyDependenceUnsatisfied.kt") + public void testPropertyDependenceUnsatisfied() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/propertyDependenceUnsatisfied.kt"); + doTest(fileName); + } + @TestMetadata("publicToInterface.kt") public void testPublicToInterface() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/pullUp/publicToInterface.kt");