KT-9839: convert primary constructor to secondary one: leave independent properties in class body as is
(cherry picked from commit b90414a)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
7f50e6e70e
commit
28b70faa99
+26
-1
@@ -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>(
|
||||
KtPrimaryConstructor::class.java,
|
||||
@@ -31,15 +38,33 @@ class ConvertPrimaryConstructorToSecondaryIntention : SelfTargetingIntention<KtP
|
||||
override fun isApplicableTo(element: KtPrimaryConstructor, caretOffset: Int) =
|
||||
element.containingClassOrObject is KtClass && element.valueParameters.all { !it.hasValOrVar() || it.annotationEntries.isEmpty() }
|
||||
|
||||
private fun KtReferenceExpression.isIndependent(classDescriptor: ClassDescriptor, context: BindingContext): Boolean {
|
||||
val referencedDescriptor = context[BindingContext.REFERENCE_TARGET, this] ?: return false
|
||||
return when (referencedDescriptor) {
|
||||
is ValueParameterDescriptor ->
|
||||
(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<KtReferenceExpression> { !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<KtProperty, String>()
|
||||
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()
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class Hello<caret>(val x: String) {
|
||||
val y = "Hello"
|
||||
|
||||
val z = x + y
|
||||
}
|
||||
+12
@@ -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
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
object Storage {
|
||||
val hello = "Hello"
|
||||
}
|
||||
|
||||
class Hello<caret>(val x: String) {
|
||||
val y = Storage.hello
|
||||
|
||||
val z = x + y
|
||||
}
|
||||
Vendored
+16
@@ -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
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
fun foo(x: Int) {
|
||||
class Local<caret>(val y: Int) {
|
||||
val z = x
|
||||
}
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
fun foo(x: Int) {
|
||||
class Local {
|
||||
val y: Int
|
||||
|
||||
constructor(y: Int) {
|
||||
this.y = y
|
||||
}
|
||||
|
||||
val z: Int = x
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user