diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt index 2714b368b23..3d4c30ae31f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertPrimaryConstructorToSecondaryIntention.kt @@ -17,12 +17,19 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.openapi.editor.Editor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.lexer.KtTokens.THIS_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.VARARG_KEYWORD import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder import org.jetbrains.kotlin.psi.KtPsiFactory.CallableBuilder.Target.CONSTRUCTOR +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.parents class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingIntention( KtPrimaryConstructor::class.java, @@ -31,15 +38,33 @@ class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingIntention + (referencedDescriptor.containingDeclaration as? ConstructorDescriptor)?.containingDeclaration != classDescriptor + else -> + classDescriptor !in referencedDescriptor.parents + } + } + + private fun KtProperty.isIndependent(klass: KtClass, context: BindingContext): Boolean { + val propertyInitializer = initializer ?: return true + val classDescriptor = context[BindingContext.CLASS, klass] ?: return false + return !propertyInitializer.anyDescendantOfType { !it.isIndependent(classDescriptor, context) } + } + override fun applyTo(element: KtPrimaryConstructor, editor: Editor?) { val klass = element.containingClassOrObject as? KtClass ?: return + val context = klass.analyze() val factory = KtPsiFactory(klass) val initializerMap = mutableMapOf() for (property in klass.getProperties()) { if (property.typeReference == null) { SpecifyTypeExplicitlyIntention().applyTo(property, editor) } - val initializer = property.initializer ?: continue + if (property.isIndependent(klass, context)) continue + val initializer = property.initializer!! initializerMap[property] = initializer.text initializer.delete() property.equalsToken!!.delete() diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty.kt b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty.kt new file mode 100644 index 00000000000..5968b8c0095 --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty.kt @@ -0,0 +1,5 @@ +class Hello(val x: String) { + val y = "Hello" + + val z = x + y +} diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty.kt.after b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty.kt.after new file mode 100644 index 00000000000..78996a089ed --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty.kt.after @@ -0,0 +1,12 @@ +class Hello { + val x: String + + constructor(x: String) { + this.x = x + this.z = x + y + } + + val y: String = "Hello" + + val z: String +} diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty2.kt b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty2.kt new file mode 100644 index 00000000000..ded503b41ad --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty2.kt @@ -0,0 +1,9 @@ +object Storage { + val hello = "Hello" +} + +class Hello(val x: String) { + val y = Storage.hello + + val z = x + y +} diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty2.kt.after b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty2.kt.after new file mode 100644 index 00000000000..0963f811db1 --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty2.kt.after @@ -0,0 +1,16 @@ +object Storage { + val hello = "Hello" +} + +class Hello { + val x: String + + constructor(x: String) { + this.x = x + this.z = x + y + } + + val y: String = Storage.hello + + val z: String +} diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty3.kt b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty3.kt new file mode 100644 index 00000000000..c3c1a1b5ff5 --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty3.kt @@ -0,0 +1,5 @@ +fun foo(x: Int) { + class Local(val y: Int) { + val z = x + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty3.kt.after b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty3.kt.after new file mode 100644 index 00000000000..d07f8e74a72 --- /dev/null +++ b/idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty3.kt.after @@ -0,0 +1,11 @@ +fun foo(x: Int) { + class Local { + val y: Int + + constructor(y: Int) { + this.y = y + } + + val z: Int = x + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index f4ea5a93dc1..69a7f133587 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -4390,6 +4390,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("independentProperty.kt") + public void testIndependentProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty.kt"); + doTest(fileName); + } + + @TestMetadata("independentProperty2.kt") + public void testIndependentProperty2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty2.kt"); + doTest(fileName); + } + + @TestMetadata("independentProperty3.kt") + public void testIndependentProperty3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertPrimaryConstructorToSecondary/independentProperty3.kt"); + doTest(fileName); + } + @TestMetadata("initAndParams.kt") public void testInitAndParams() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertPrimaryConstructorToSecondary/initAndParams.kt");