Skip to content

Commit 2b4f03f

Browse files
asedunovAlexey
authored and
Alexey
committed
Implement Members Quick-Fix: Support primary constructor parameters
#KT-8427 In Progress
1 parent 00cc9d4 commit 2b4f03f

File tree

11 files changed

+105
-20
lines changed

11 files changed

+105
-20
lines changed

idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class OverridesCompletion(
6060

6161
val classOrObject = position.getNonStrictParentOfType<KtClassOrObject>() ?: return
6262

63-
val members = OverrideMembersHandler().collectMembersToGenerate(classOrObject)
63+
val members = OverrideMembersHandler(isConstructorParameter).collectMembersToGenerate(classOrObject)
6464

6565
for (memberObject in members) {
6666
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
@@ -114,7 +114,7 @@ class OverridesCompletion(
114114
// keep original modifiers
115115
val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text)
116116

117-
val prototype = memberObject.generateMember(context.project, isConstructorParameter)
117+
val prototype = memberObject.generateMember(context.project)
118118
prototype.modifierList!!.replace(modifierList)
119119
val insertedMember = dummyMember.replaced(prototype)
120120

idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/ImplementMembersHandler.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,15 @@ import com.intellij.openapi.editor.Editor
2121
import com.intellij.openapi.project.Project
2222
import com.intellij.psi.PsiFile
2323
import org.jetbrains.kotlin.descriptors.ClassDescriptor
24+
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
2425
import org.jetbrains.kotlin.idea.KotlinBundle
26+
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
27+
import org.jetbrains.kotlin.psi.KtClass
28+
import org.jetbrains.kotlin.psi.KtClassOrObject
29+
import org.jetbrains.kotlin.psi.KtEnumEntry
2530
import org.jetbrains.kotlin.resolve.OverrideResolver
2631

27-
public class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction {
32+
public open class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction {
2833
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
2934
return OverrideResolver.getMissingImplementations(descriptor)
3035
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY) }
@@ -39,3 +44,19 @@ public class ImplementMembersHandler : OverrideImplementMembersHandler(), Intent
3944

4045
override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file)
4146
}
47+
48+
public class ImplementAsConstructorParameter : ImplementMembersHandler() {
49+
override fun getText() = "Implement as constructor parameters"
50+
51+
override fun isValidForClass(classOrObject: KtClassOrObject): Boolean {
52+
if (classOrObject !is KtClass || classOrObject is KtEnumEntry || classOrObject.isInterface()) return false
53+
val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false
54+
return OverrideResolver.getMissingImplementations(classDescriptor).any { it is PropertyDescriptor }
55+
}
56+
57+
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
58+
return OverrideResolver.getMissingImplementations(descriptor)
59+
.filter { it is PropertyDescriptor }
60+
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY, true) }
61+
}
62+
}

idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideImplementMembersHandler.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,13 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio
5151

5252
protected abstract fun getChooserTitle(): String
5353

54+
protected open fun isValidForClass(classOrObject: KtClassOrObject) = true
55+
5456
override fun isValidFor(editor: Editor, file: PsiFile): Boolean {
5557
if (file !is KtFile) return false
5658
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
5759
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>()
58-
return classOrObject != null
60+
return classOrObject != null && isValidForClass(classOrObject)
5961
}
6062

6163
protected abstract fun getNoMembersFoundHint(): String

idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,30 @@ interface OverrideMemberChooserObject : ClassMember {
3939
val descriptor: CallableMemberDescriptor
4040
val immediateSuper: CallableMemberDescriptor
4141
val bodyType: BodyType
42+
val preferConstructorParameter: Boolean
4243

4344
companion object {
44-
fun create(project: Project, descriptor: CallableMemberDescriptor, immediateSuper: CallableMemberDescriptor, bodyType: BodyType): OverrideMemberChooserObject {
45+
fun create(project: Project,
46+
descriptor: CallableMemberDescriptor,
47+
immediateSuper: CallableMemberDescriptor,
48+
bodyType: BodyType,
49+
preferConstructorParameter: Boolean = false
50+
): OverrideMemberChooserObject {
4551
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
4652
if (declaration != null) {
47-
return WithDeclaration(descriptor, declaration, immediateSuper, bodyType)
53+
return WithDeclaration(descriptor, declaration, immediateSuper, bodyType, preferConstructorParameter)
4854
}
4955
else {
50-
return WithoutDeclaration(descriptor, immediateSuper, bodyType)
56+
return WithoutDeclaration(descriptor, immediateSuper, bodyType, preferConstructorParameter)
5157
}
5258
}
5359

5460
private class WithDeclaration(
5561
descriptor: CallableMemberDescriptor,
5662
declaration: PsiElement,
5763
override val immediateSuper: CallableMemberDescriptor,
58-
override val bodyType: BodyType
64+
override val bodyType: BodyType,
65+
override val preferConstructorParameter: Boolean
5966
) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject {
6067

6168
override val descriptor: CallableMemberDescriptor
@@ -65,7 +72,8 @@ interface OverrideMemberChooserObject : ClassMember {
6572
private class WithoutDeclaration(
6673
override val descriptor: CallableMemberDescriptor,
6774
override val immediateSuper: CallableMemberDescriptor,
68-
override val bodyType: BodyType
75+
override val bodyType: BodyType,
76+
override val preferConstructorParameter: Boolean
6977
) : MemberChooserObjectBase(DescriptorMemberChooserObject.getText(descriptor), DescriptorMemberChooserObject.getIcon(null, descriptor)), OverrideMemberChooserObject {
7078

7179
override fun getParentNodeDelegate(): MemberChooserObject? {
@@ -76,12 +84,9 @@ interface OverrideMemberChooserObject : ClassMember {
7684
}
7785
}
7886

79-
fun OverrideMemberChooserObject.generateMember(project: Project, asConstructorParameter: Boolean = false): KtCallableDeclaration {
87+
fun OverrideMemberChooserObject.generateMember(project: Project): KtCallableDeclaration {
8088
val descriptor = immediateSuper
81-
if (asConstructorParameter) {
82-
assert(descriptor is PropertyDescriptor) { "asConstructorParameter is valid only for PropertyDescriptor" }
83-
return generateConstructorParameter(project, descriptor as PropertyDescriptor)
84-
}
89+
if (preferConstructorParameter && descriptor is PropertyDescriptor) return generateConstructorParameter(project, descriptor)
8590

8691
return when (descriptor) {
8792
is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType)

idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMembersHandler.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
2323
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
2424
import java.util.*
2525

26-
public class OverrideMembersHandler : OverrideImplementMembersHandler() {
26+
public class OverrideMembersHandler(private val preferConstructorParameters: Boolean = false) : OverrideImplementMembersHandler() {
2727
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
2828
val result = ArrayList<OverrideMemberChooserObject>()
2929
for (member in descriptor.unsubstitutedMemberScope.getContributedDescriptors()) {
@@ -73,7 +73,7 @@ public class OverrideMembersHandler : OverrideImplementMembersHandler() {
7373
else
7474
OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER
7575

76-
result.add(OverrideMemberChooserObject.create(project, realSuper, immediateSuperToUse, bodyType))
76+
result.add(OverrideMemberChooserObject.create(project, realSuper, immediateSuperToUse, bodyType, preferConstructorParameters))
7777
}
7878
}
7979
}

idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.quickfix
1919
import com.intellij.codeInsight.intention.IntentionAction
2020
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
2121
import org.jetbrains.kotlin.diagnostics.Errors.*
22+
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementAsConstructorParameter
2223
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
2324
import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory
2425
import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix
@@ -156,10 +157,11 @@ public class QuickFixRegistrar : QuickFixContributor {
156157
USELESS_NULLABLE_CHECK.registerFactory(RemoveNullableFix.createFactory(RemoveNullableFix.NullableKind.USELESS))
157158

158159

159-
val implementMethodsHandler = ImplementMembersHandler()
160-
ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler)
161-
ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler)
162-
MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler)
160+
val implementMembersHandler = ImplementMembersHandler()
161+
val implementMembersAsParametersHandler = ImplementAsConstructorParameter()
162+
ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
163+
ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
164+
MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
163165

164166
VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY)
165167
VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// "Implement as constructor parameters" "true"
2+
interface I {
3+
val foo: Int
4+
}
5+
6+
<caret>class A : I
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// "Implement as constructor parameters" "true"
2+
interface I {
3+
val foo: Int
4+
}
5+
6+
<caret>class A(override val foo: Int) : I
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// "Implement as constructor parameters" "false"
2+
// ACTION: Create test
3+
// ACTION: Implement members
4+
// ACTION: Make 'A' abstract
5+
// ACTION: Make internal
6+
// ACTION: Make private
7+
// ACTION: Move 'A' to separate file
8+
// ERROR: Class 'A' must be declared abstract or implement abstract member public abstract fun foo(): kotlin.Int defined in I
9+
interface I {
10+
fun foo(): Int
11+
}
12+
13+
<caret>class A : I
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// "Implement as constructor parameters" "false"
2+
// ACTION: Create test
3+
// ACTION: Implement members
4+
// ACTION: Make internal
5+
// ACTION: Make private
6+
// ACTION: Move 'A' to separate file
7+
// ERROR: Object 'A' must be declared abstract or implement abstract member public abstract val foo: kotlin.Int defined in I
8+
interface I {
9+
val foo: Int
10+
}
11+
12+
<caret>object A : I

idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5364,6 +5364,24 @@ public void testChangeToInvocation() throws Exception {
53645364
doTest(fileName);
53655365
}
53665366

5367+
@TestMetadata("implemenAsConstructorParameter.kt")
5368+
public void testImplemenAsConstructorParameter() throws Exception {
5369+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameter.kt");
5370+
doTest(fileName);
5371+
}
5372+
5373+
@TestMetadata("implemenAsConstructorParameterFunction.kt")
5374+
public void testImplemenAsConstructorParameterFunction() throws Exception {
5375+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameterFunction.kt");
5376+
doTest(fileName);
5377+
}
5378+
5379+
@TestMetadata("implemenAsConstructorParameterInObject.kt")
5380+
public void testImplemenAsConstructorParameterInObject() throws Exception {
5381+
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameterInObject.kt");
5382+
doTest(fileName);
5383+
}
5384+
53675385
@TestMetadata("implementMember.kt")
53685386
public void testImplementMember() throws Exception {
53695387
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implementMember.kt");

0 commit comments

Comments
 (0)