From 725df49c8cab6f39bb01e957d10390a24b770ca9 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 29 Sep 2016 16:47:35 +0300 Subject: [PATCH] KT-9839: intention introduced: convert secondary constructor to primary one #KT-9839 Fixed (cherry picked from commit f3fa779) --- .../org/jetbrains/kotlin/psi/KtPsiFactory.kt | 4 + .../after.kt.template | 3 + .../before.kt.template | 9 ++ .../description.html | 5 + idea/src/META-INF/plugin.xml | 5 + ...tSecondaryConstructorToPrimaryIntention.kt | 149 ++++++++++++++++++ .../.intention | 1 + .../defaultValueChain.kt | 11 ++ .../defaultValueChain.kt.after | 3 + .../init.kt | 10 ++ .../init.kt.after | 11 ++ .../initAndParams.kt | 15 ++ .../initAndParams.kt.after | 15 ++ .../nonReachable.kt | 7 + .../nonReachableLoop.kt | 11 ++ .../protectedConstructor.kt | 7 + .../protectedConstructor.kt.after | 3 + .../simple.kt | 3 + .../simple.kt.after | 2 + .../useParam.kt | 7 + .../useParam.kt.after | 4 + .../varArg.kt | 10 ++ .../varArg.kt.after | 11 ++ .../varargVal.kt | 9 ++ .../varargVal.kt.after | 5 + .../withBaseClass.kt | 5 + .../withBaseClass.kt.after | 4 + .../withBaseClassNoArgs.kt | 14 ++ .../withBaseClassNoArgs.kt.after | 9 ++ .../withComposedModifiers.kt | 12 ++ .../withComposedModifiers.kt.after | 8 + .../withDelegation.kt | 7 + .../withDifferentTypeProperty.kt | 10 ++ .../withDifferentTypeProperty.kt.after | 6 + .../withModifiers.kt | 6 + .../withModifiers.kt.after | 4 + .../withParameters.kt | 3 + .../withParameters.kt.after | 2 + .../withPrimary.kt | 6 + .../withProperties.kt | 11 ++ .../withProperties.kt.after | 3 + .../superCallNoClass.kt | 1 + .../nonApplicableWithOneConstructor.kt | 1 + .../intentions/IntentionTestGenerated.java | 123 +++++++++++++++ 44 files changed, 555 insertions(+) create mode 100644 idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/ConvertSecondaryConstructorToPrimaryIntention.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/.intention create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachable.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachableLoop.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withDelegation.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt.after create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withPrimary.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt create mode 100644 idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt index 10b127b2e4e..d0a088ed6c8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt @@ -397,6 +397,10 @@ class KtPsiFactory(private val project: Project) { return createClass("class A()").getPrimaryConstructor()!! } + fun createPrimaryConstructor(modifiers: String?): KtPrimaryConstructor { + return modifiers?.let { createClass("class A $modifiers constructor()").getPrimaryConstructor() } ?: createPrimaryConstructor() + } + fun createConstructorKeyword(): PsiElement = createClass("class A constructor()").getPrimaryConstructor()!!.getConstructorKeyword()!! diff --git a/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/after.kt.template new file mode 100644 index 00000000000..d9ee4c351d2 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/after.kt.template @@ -0,0 +1,3 @@ +class Foo(val x: Int = 4, y: Int) { + val z = y +} diff --git a/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/before.kt.template new file mode 100644 index 00000000000..de2d3c26ff3 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/before.kt.template @@ -0,0 +1,9 @@ +class Foo { + val z = y + + val x: Int + + constructor(x: Int = 4, y: Int) { + this.x = x + } +} diff --git a/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/description.html b/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/description.html new file mode 100644 index 00000000000..d5fba0e6e1c --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertSecondaryConstructorToPrimaryIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts secondary constructor to primary one. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 94c2a230eab..ab075c898fd 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1377,6 +1377,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertSecondaryConstructorToPrimaryIntention + Kotlin + + ( + KtSecondaryConstructor::class.java, + "Convert to primary constructor" +) { + private tailrec fun ConstructorDescriptor.isReachableByDelegationFrom( + constructor: ConstructorDescriptor, context: BindingContext, visited: Set = emptySet() + ): Boolean { + if (constructor == this) return true + if (constructor in visited) return false + val resolvedDelegationCall = context[BindingContext.CONSTRUCTOR_RESOLVED_DELEGATION_CALL, constructor] ?: return false + val delegationDescriptor = resolvedDelegationCall.candidateDescriptor + return isReachableByDelegationFrom(delegationDescriptor, context, visited + constructor) + } + + override fun isApplicableTo(element: KtSecondaryConstructor, caretOffset: Int): Boolean { + val delegationCall = element.getDelegationCall() + if (delegationCall.isCallToThis) return false + val klass = element.containingClassOrObject ?: return false + if (klass.hasPrimaryConstructor()) return false + + val context = klass.analyze() + val classDescriptor = context[BindingContext.CLASS, klass] ?: return false + val elementDescriptor = context[BindingContext.CONSTRUCTOR, element] ?: return false + + for (constructorDescriptor in classDescriptor.constructors) { + if (constructorDescriptor == elementDescriptor) continue + if (!elementDescriptor.isReachableByDelegationFrom(constructorDescriptor, context)) return false + } + + return true + } + + private fun KtExpression.tryConvertToPropertyByParameterInitialization( + constructorDescriptor: ConstructorDescriptor, context: BindingContext + ): Pair? { + if (this !is KtBinaryExpression || operationToken != KtTokens.EQ) return null + val rightReference = right as? KtReferenceExpression ?: return null + val rightDescriptor = context[BindingContext.REFERENCE_TARGET, rightReference] as? ValueParameterDescriptor ?: return null + if (rightDescriptor.containingDeclaration != constructorDescriptor) return null + val left = left + val leftReference = when (left) { + is KtReferenceExpression -> + left + is KtDotQualifiedExpression -> + if (left.receiverExpression is KtThisExpression) left.selectorExpression as? KtReferenceExpression else null + else -> + null + } + val leftDescriptor = context[BindingContext.REFERENCE_TARGET, leftReference] as? PropertyDescriptor ?: return null + return rightDescriptor to leftDescriptor + } + + override fun applyTo(element: KtSecondaryConstructor, editor: Editor?) { + val klass = element.containingClassOrObject as? KtClass ?: return + val context = klass.analyzeFully() + val constructorDescriptor = context[BindingContext.CONSTRUCTOR, element] ?: return + val factory = KtPsiFactory(klass) + val constructorInClass = klass.createPrimaryConstructorIfAbsent() + val constructor = factory.createPrimaryConstructor(element.modifierList?.text?.replace("\n", " ")) + val parameterList = constructor.valueParameterList!! + val parameterToPropertyMap = mutableMapOf() + val initializer = factory.createAnonymousInitializer() as? KtClassInitializer + for (statement in element.bodyExpression?.statements ?: emptyList()) { + val (rightDescriptor, leftDescriptor) = statement.tryConvertToPropertyByParameterInitialization(constructorDescriptor, context) + ?: with (initializer) { + (initializer?.body as? KtBlockExpression)?.let { + it.addBefore(statement.copy(), it.rBrace) + it.addBefore(factory.createNewLine(), it.rBrace) + } + null to null + } + if (rightDescriptor == null || leftDescriptor == null) continue + parameterToPropertyMap[rightDescriptor] = leftDescriptor + } + + for (parameter in element.valueParameters) { + val newParameter = factory.createParameter(parameter.text) + val parameterDescriptor = context[BindingContext.VALUE_PARAMETER, parameter] + val propertyDescriptor = parameterToPropertyMap[parameterDescriptor] + if (parameterDescriptor != null && propertyDescriptor != null) { + val property = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor) as? KtProperty + if (property != null) { + if (propertyDescriptor.name == parameterDescriptor.name && propertyDescriptor.type == parameterDescriptor.type) { + val valOrVar = if (property.isVar) factory.createVarKeyword() else factory.createValKeyword() + newParameter.addBefore(valOrVar, newParameter.nameIdentifier) + val propertyModifiers = property.modifierList?.text + if (propertyModifiers != null) { + val newModifiers = factory.createModifierList(propertyModifiers) + newParameter.addBefore(newModifiers, newParameter.valOrVarKeyword) + } + property.delete() + } + else { + property.initializer = factory.createSimpleName(parameterDescriptor.name.asString()) + } + } + } + parameterList.addParameter(newParameter) + } + + val delegationCall = element.getDelegationCall() + val argumentList = delegationCall.valueArgumentList + if (!delegationCall.isImplicit && argumentList != null) { + for (superTypeListEntry in klass.getSuperTypeListEntries()) { + val typeReference = superTypeListEntry.typeReference ?: continue + val type = context[BindingContext.TYPE, typeReference] + if ((type?.constructor?.declarationDescriptor as? ClassDescriptor)?.kind == ClassKind.CLASS) { + val superTypeCallEntry = factory.createSuperTypeCallEntry("${typeReference.text}${argumentList.text}") + superTypeListEntry.replace(superTypeCallEntry) + break + } + } + } + + constructorInClass.replace(constructor) + element.delete() + if ((initializer?.body as? KtBlockExpression)?.statements?.isNotEmpty() ?: false) { + initializer?.let { klass.addDeclaration(it) } + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/.intention b/idea/testData/intentions/convertSecondaryConstructorToPrimary/.intention new file mode 100644 index 00000000000..6e8ef9390a0 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertSecondaryConstructorToPrimaryIntention diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt new file mode 100644 index 00000000000..e1f9bf03904 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt @@ -0,0 +1,11 @@ +class DefaultValueChain { + val x1: Int + val x3: Int + val x5: Int + + constructor(x1: Int, x2: Int = x1, x3: Int = x2, x4: Int = x3, x5: Int = x4) { + this.x1 = x1 + this.x3 = x3 + this.x5 = x5 + } +} diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt.after new file mode 100644 index 00000000000..d7c7aaed1d0 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt.after @@ -0,0 +1,3 @@ +class DefaultValueChain(val x1: Int, x2: Int = x1, val x3: Int = x2, x4: Int = x3, val x5: Int = x4) { + +} diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt new file mode 100644 index 00000000000..c9ae0215d00 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt @@ -0,0 +1,10 @@ +class ConvertToInit { + fun foo() {} + + fun bar() {} + + constructor() { + foo() + bar() + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt.after new file mode 100644 index 00000000000..106e0bbf1fb --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt.after @@ -0,0 +1,11 @@ +class ConvertToInit() { + fun foo() {} + + fun bar() {} + + init { + foo() + bar() + } + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt new file mode 100644 index 00000000000..7250e0fc3b5 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt @@ -0,0 +1,15 @@ +class InitAndParams { + constructor(x: Int, z: Int) { + this.y = x + w = foo(y) + this.v = w + z + } + + val y: Int + + val w: Int + + fun foo(arg: Int) = arg + + val v: Int +} diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt.after new file mode 100644 index 00000000000..73754d2174c --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt.after @@ -0,0 +1,15 @@ +class InitAndParams(x: Int, z: Int) { + + val y: Int = x + + val w: Int + + fun foo(arg: Int) = arg + + val v: Int + + init { + w = foo(y) + this.v = w + z + } +} diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachable.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachable.kt new file mode 100644 index 00000000000..d979b9bc9ca --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachable.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +class NonReachableConstructor { + constructor(x: String) + + constructor(x: Int) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachableLoop.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachableLoop.kt new file mode 100644 index 00000000000..bb3a14b7b41 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachableLoop.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false +// ERROR: There's a cycle in the delegation calls chain +// ERROR: There's a cycle in the delegation calls chain + +class NonReachableLoop { + constructor(x: String) + + constructor(x: Int, y: Int): this(x + y) + + constructor(x: Int): this(x, x) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt new file mode 100644 index 00000000000..19b55ff7c39 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt @@ -0,0 +1,7 @@ +class Protected { + internal var s: String + + protected constructor(s: String) { + this.s = s + } +} diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt.after new file mode 100644 index 00000000000..54577f05397 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt.after @@ -0,0 +1,3 @@ +class Protected protected constructor(internal var s: String) { + +} diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt new file mode 100644 index 00000000000..37103c20081 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt @@ -0,0 +1,3 @@ +class Simple { + constructor() +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt.after new file mode 100644 index 00000000000..c12aaa6795c --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt.after @@ -0,0 +1,2 @@ +class Simple() { +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt new file mode 100644 index 00000000000..427bc65a48c --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt @@ -0,0 +1,7 @@ +class UseParam { + constructor(x: Int) { + this.y = x + } + + val y: Int +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt.after new file mode 100644 index 00000000000..abd3b3c2245 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt.after @@ -0,0 +1,4 @@ +class UseParam(x: Int) { + + val y: Int = x +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt new file mode 100644 index 00000000000..d2418f0e5c0 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME + +class WithVarArg { + + val x: List + + constructor(vararg zz: String) { + x = listOf(*zz) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt.after new file mode 100644 index 00000000000..2db8b688dc4 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt.after @@ -0,0 +1,11 @@ +// WITH_RUNTIME + +class WithVarArg(vararg zz: String) { + + val x: List + + init { + x = listOf(*zz) + } + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt new file mode 100644 index 00000000000..943f906691e --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME + +class VarargVal { + val param: Array + + constructor(vararg param: String) { + this.param = param + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt.after new file mode 100644 index 00000000000..4d56d85d66e --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +class VarargVal(vararg val param: String) { + +} diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt new file mode 100644 index 00000000000..1e577f0c97f --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt @@ -0,0 +1,5 @@ +abstract class Base(val x: String) + +class Derived : Base { + constructor(x: String): super(x) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt.after new file mode 100644 index 00000000000..6ed250a2deb --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt.after @@ -0,0 +1,4 @@ +abstract class Base(val x: String) + +class Derived(x: String) : Base(x) { +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt new file mode 100644 index 00000000000..14e7d4bc0cd --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt @@ -0,0 +1,14 @@ +interface Interface + +interface Another + +abstract class Base + +class Derived : Interface, Base, Another { + + val x: String + + constructor(x: String): super() { + this.x = x + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt.after new file mode 100644 index 00000000000..abf87fd4174 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt.after @@ -0,0 +1,9 @@ +interface Interface + +interface Another + +abstract class Base + +class Derived(val x: String) : Interface, Base(), Another { + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt new file mode 100644 index 00000000000..440034d5787 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt @@ -0,0 +1,12 @@ +annotation class AnnParam + +annotation class AnnProperty + +abstract class WithComposedModifiers { + @AnnProperty + open val x: Array + + constructor(@AnnParam vararg x: String) { + this.x = x + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt.after new file mode 100644 index 00000000000..c8625bcf83e --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt.after @@ -0,0 +1,8 @@ +annotation class AnnParam + +annotation class AnnProperty + +abstract class WithComposedModifiers(@AnnParam vararg @AnnProperty +open val x: String) { + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDelegation.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDelegation.kt new file mode 100644 index 00000000000..acfe89cff45 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDelegation.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +class WithDelegation { + constructor() + + constructor(x: Int): this() +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt new file mode 100644 index 00000000000..5e26356a3a4 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt @@ -0,0 +1,10 @@ +class WithDifferentTypeProperty { + val x: Number + + val y: String + + constructor(x: Int, z: String) { + this.x = x + this.y = z + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt.after new file mode 100644 index 00000000000..8a07921e2a8 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt.after @@ -0,0 +1,6 @@ +class WithDifferentTypeProperty(x: Int, z: String) { + val x: Number = x + + val y: String = z + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt new file mode 100644 index 00000000000..aa69757d4dd --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt @@ -0,0 +1,6 @@ +annotation class Ann + +internal class WithModifiers { + @Ann + private constructor() +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt.after new file mode 100644 index 00000000000..8827e66e9fb --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt.after @@ -0,0 +1,4 @@ +annotation class Ann + +internal class WithModifiers @Ann private constructor() { +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt new file mode 100644 index 00000000000..00418c7f6bc --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt @@ -0,0 +1,3 @@ +class WithParameters { + constructor(x: Int = 42, y: String = "Hello") +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt.after new file mode 100644 index 00000000000..a283fd3b291 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt.after @@ -0,0 +1,2 @@ +class WithParameters(x: Int = 42, y: String = "Hello") { +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withPrimary.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withPrimary.kt new file mode 100644 index 00000000000..8cc2985bb58 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withPrimary.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +// ERROR: Primary constructor call expected + +class WithPrimary() { + constructor(x: Int) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt new file mode 100644 index 00000000000..f58c97dadcd --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt @@ -0,0 +1,11 @@ +class WithProperties { + val x: Int + val y: Int + private val z: Int + + constructor(x: Int, y: Int = 7, z: Int = 13) { + this.x = x + this.y = y + this.z = z + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt.after b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt.after new file mode 100644 index 00000000000..ffbe40907c6 --- /dev/null +++ b/idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt.after @@ -0,0 +1,3 @@ +class WithProperties(val x: Int, val y: Int = 7, private val z: Int = 13) { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallNoClass.kt b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallNoClass.kt index e82afcef257..658bd7d912f 100644 --- a/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallNoClass.kt +++ b/idea/testData/quickfix/createFromUsage/createSecondaryConstructor/superCallNoClass.kt @@ -1,5 +1,6 @@ // "Create secondary constructor" "false" // ERROR: Too many arguments for public constructor Any() defined in kotlin.Any +// ACTION: Convert to primary constructor // WITH_RUNTIME interface T { diff --git a/idea/testData/quickfix/insertDelegationCall/nonApplicableWithOneConstructor.kt b/idea/testData/quickfix/insertDelegationCall/nonApplicableWithOneConstructor.kt index 6d7f76c957e..2504d6092a0 100644 --- a/idea/testData/quickfix/insertDelegationCall/nonApplicableWithOneConstructor.kt +++ b/idea/testData/quickfix/insertDelegationCall/nonApplicableWithOneConstructor.kt @@ -1,5 +1,6 @@ // "class org.jetbrains.kotlin.idea.quickfix.InsertDelegationCallQuickfix" "false" // ACTION: Insert 'super()' call +// ACTION: Convert to primary constructor // ERROR: Explicit 'this' or 'super' call is required. There is no constructor in superclass that can be called without arguments open class B(val x: Int) diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 69a7f133587..b95aad17552 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -4688,6 +4688,129 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertSecondaryConstructorToPrimary extends AbstractIntentionTest { + public void testAllFilesPresentInConvertSecondaryConstructorToPrimary() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertSecondaryConstructorToPrimary"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("defaultValueChain.kt") + public void testDefaultValueChain() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/defaultValueChain.kt"); + doTest(fileName); + } + + @TestMetadata("init.kt") + public void testInit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/init.kt"); + doTest(fileName); + } + + @TestMetadata("initAndParams.kt") + public void testInitAndParams() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/initAndParams.kt"); + doTest(fileName); + } + + @TestMetadata("nonReachable.kt") + public void testNonReachable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachable.kt"); + doTest(fileName); + } + + @TestMetadata("nonReachableLoop.kt") + public void testNonReachableLoop() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/nonReachableLoop.kt"); + doTest(fileName); + } + + @TestMetadata("protectedConstructor.kt") + public void testProtectedConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/protectedConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/simple.kt"); + doTest(fileName); + } + + @TestMetadata("useParam.kt") + public void testUseParam() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/useParam.kt"); + doTest(fileName); + } + + @TestMetadata("varArg.kt") + public void testVarArg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/varArg.kt"); + doTest(fileName); + } + + @TestMetadata("varargVal.kt") + public void testVarargVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/varargVal.kt"); + doTest(fileName); + } + + @TestMetadata("withBaseClass.kt") + public void testWithBaseClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClass.kt"); + doTest(fileName); + } + + @TestMetadata("withBaseClassNoArgs.kt") + public void testWithBaseClassNoArgs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withBaseClassNoArgs.kt"); + doTest(fileName); + } + + @TestMetadata("withComposedModifiers.kt") + public void testWithComposedModifiers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withComposedModifiers.kt"); + doTest(fileName); + } + + @TestMetadata("withDelegation.kt") + public void testWithDelegation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withDelegation.kt"); + doTest(fileName); + } + + @TestMetadata("withDifferentTypeProperty.kt") + public void testWithDifferentTypeProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withDifferentTypeProperty.kt"); + doTest(fileName); + } + + @TestMetadata("withModifiers.kt") + public void testWithModifiers() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withModifiers.kt"); + doTest(fileName); + } + + @TestMetadata("withParameters.kt") + public void testWithParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withParameters.kt"); + doTest(fileName); + } + + @TestMetadata("withPrimary.kt") + public void testWithPrimary() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withPrimary.kt"); + doTest(fileName); + } + + @TestMetadata("withProperties.kt") + public void testWithProperties() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertSecondaryConstructorToPrimary/withProperties.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/convertToBlockBody") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)