Implement Members Quick-Fix: Support primary constructor parameters

#KT-8427 In Progress
This commit is contained in:
Alexey Sedunov
2015-12-24 16:40:24 +03:00
committed by Alexey
parent 00cc9d4150
commit 2b4f03feef
11 changed files with 105 additions and 20 deletions
@@ -60,7 +60,7 @@ class OverridesCompletion(
val classOrObject = position.getNonStrictParentOfType<KtClassOrObject>() ?: return val classOrObject = position.getNonStrictParentOfType<KtClassOrObject>() ?: return
val members = OverrideMembersHandler().collectMembersToGenerate(classOrObject) val members = OverrideMembersHandler(isConstructorParameter).collectMembersToGenerate(classOrObject)
for (memberObject in members) { for (memberObject in members) {
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
@@ -114,7 +114,7 @@ class OverridesCompletion(
// keep original modifiers // keep original modifiers
val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text) val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text)
val prototype = memberObject.generateMember(context.project, isConstructorParameter) val prototype = memberObject.generateMember(context.project)
prototype.modifierList!!.replace(modifierList) prototype.modifierList!!.replace(modifierList)
val insertedMember = dummyMember.replaced(prototype) val insertedMember = dummyMember.replaced(prototype)
@@ -21,10 +21,15 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.resolve.OverrideResolver import org.jetbrains.kotlin.resolve.OverrideResolver
public class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction { public open class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction {
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> { override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
return OverrideResolver.getMissingImplementations(descriptor) return OverrideResolver.getMissingImplementations(descriptor)
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY) } .map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY) }
@@ -39,3 +44,19 @@ public class ImplementMembersHandler : OverrideImplementMembersHandler(), Intent
override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file) override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file)
} }
public class ImplementAsConstructorParameter : ImplementMembersHandler() {
override fun getText() = "Implement as constructor parameters"
override fun isValidForClass(classOrObject: KtClassOrObject): Boolean {
if (classOrObject !is KtClass || classOrObject is KtEnumEntry || classOrObject.isInterface()) return false
val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false
return OverrideResolver.getMissingImplementations(classDescriptor).any { it is PropertyDescriptor }
}
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
return OverrideResolver.getMissingImplementations(descriptor)
.filter { it is PropertyDescriptor }
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY, true) }
}
}
@@ -51,11 +51,13 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio
protected abstract fun getChooserTitle(): String protected abstract fun getChooserTitle(): String
protected open fun isValidForClass(classOrObject: KtClassOrObject) = true
override fun isValidFor(editor: Editor, file: PsiFile): Boolean { override fun isValidFor(editor: Editor, file: PsiFile): Boolean {
if (file !is KtFile) return false if (file !is KtFile) return false
val elementAtCaret = file.findElementAt(editor.caretModel.offset) val elementAtCaret = file.findElementAt(editor.caretModel.offset)
val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>() val classOrObject = elementAtCaret?.getNonStrictParentOfType<KtClassOrObject>()
return classOrObject != null return classOrObject != null && isValidForClass(classOrObject)
} }
protected abstract fun getNoMembersFoundHint(): String protected abstract fun getNoMembersFoundHint(): String
@@ -39,15 +39,21 @@ interface OverrideMemberChooserObject : ClassMember {
val descriptor: CallableMemberDescriptor val descriptor: CallableMemberDescriptor
val immediateSuper: CallableMemberDescriptor val immediateSuper: CallableMemberDescriptor
val bodyType: BodyType val bodyType: BodyType
val preferConstructorParameter: Boolean
companion object { companion object {
fun create(project: Project, descriptor: CallableMemberDescriptor, immediateSuper: CallableMemberDescriptor, bodyType: BodyType): OverrideMemberChooserObject { fun create(project: Project,
descriptor: CallableMemberDescriptor,
immediateSuper: CallableMemberDescriptor,
bodyType: BodyType,
preferConstructorParameter: Boolean = false
): OverrideMemberChooserObject {
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
if (declaration != null) { if (declaration != null) {
return WithDeclaration(descriptor, declaration, immediateSuper, bodyType) return WithDeclaration(descriptor, declaration, immediateSuper, bodyType, preferConstructorParameter)
} }
else { else {
return WithoutDeclaration(descriptor, immediateSuper, bodyType) return WithoutDeclaration(descriptor, immediateSuper, bodyType, preferConstructorParameter)
} }
} }
@@ -55,7 +61,8 @@ interface OverrideMemberChooserObject : ClassMember {
descriptor: CallableMemberDescriptor, descriptor: CallableMemberDescriptor,
declaration: PsiElement, declaration: PsiElement,
override val immediateSuper: CallableMemberDescriptor, override val immediateSuper: CallableMemberDescriptor,
override val bodyType: BodyType override val bodyType: BodyType,
override val preferConstructorParameter: Boolean
) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject { ) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject {
override val descriptor: CallableMemberDescriptor override val descriptor: CallableMemberDescriptor
@@ -65,7 +72,8 @@ interface OverrideMemberChooserObject : ClassMember {
private class WithoutDeclaration( private class WithoutDeclaration(
override val descriptor: CallableMemberDescriptor, override val descriptor: CallableMemberDescriptor,
override val immediateSuper: CallableMemberDescriptor, override val immediateSuper: CallableMemberDescriptor,
override val bodyType: BodyType override val bodyType: BodyType,
override val preferConstructorParameter: Boolean
) : MemberChooserObjectBase(DescriptorMemberChooserObject.getText(descriptor), DescriptorMemberChooserObject.getIcon(null, descriptor)), OverrideMemberChooserObject { ) : MemberChooserObjectBase(DescriptorMemberChooserObject.getText(descriptor), DescriptorMemberChooserObject.getIcon(null, descriptor)), OverrideMemberChooserObject {
override fun getParentNodeDelegate(): MemberChooserObject? { override fun getParentNodeDelegate(): MemberChooserObject? {
@@ -76,12 +84,9 @@ interface OverrideMemberChooserObject : ClassMember {
} }
} }
fun OverrideMemberChooserObject.generateMember(project: Project, asConstructorParameter: Boolean = false): KtCallableDeclaration { fun OverrideMemberChooserObject.generateMember(project: Project): KtCallableDeclaration {
val descriptor = immediateSuper val descriptor = immediateSuper
if (asConstructorParameter) { if (preferConstructorParameter && descriptor is PropertyDescriptor) return generateConstructorParameter(project, descriptor)
assert(descriptor is PropertyDescriptor) { "asConstructorParameter is valid only for PropertyDescriptor" }
return generateConstructorParameter(project, descriptor as PropertyDescriptor)
}
return when (descriptor) { return when (descriptor) {
is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType) is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType)
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import java.util.* import java.util.*
public class OverrideMembersHandler : OverrideImplementMembersHandler() { public class OverrideMembersHandler(private val preferConstructorParameters: Boolean = false) : OverrideImplementMembersHandler() {
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> { override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
val result = ArrayList<OverrideMemberChooserObject>() val result = ArrayList<OverrideMemberChooserObject>()
for (member in descriptor.unsubstitutedMemberScope.getContributedDescriptors()) { for (member in descriptor.unsubstitutedMemberScope.getContributedDescriptors()) {
@@ -73,7 +73,7 @@ public class OverrideMembersHandler : OverrideImplementMembersHandler() {
else else
OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER
result.add(OverrideMemberChooserObject.create(project, realSuper, immediateSuperToUse, bodyType)) result.add(OverrideMemberChooserObject.create(project, realSuper, immediateSuperToUse, bodyType, preferConstructorParameters))
} }
} }
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementAsConstructorParameter
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory
import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix import org.jetbrains.kotlin.idea.inspections.AddReflectionQuickFix
@@ -156,10 +157,11 @@ public class QuickFixRegistrar : QuickFixContributor {
USELESS_NULLABLE_CHECK.registerFactory(RemoveNullableFix.createFactory(RemoveNullableFix.NullableKind.USELESS)) USELESS_NULLABLE_CHECK.registerFactory(RemoveNullableFix.createFactory(RemoveNullableFix.NullableKind.USELESS))
val implementMethodsHandler = ImplementMembersHandler() val implementMembersHandler = ImplementMembersHandler()
ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler) val implementMembersAsParametersHandler = ImplementAsConstructorParameter()
ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler) ABSTRACT_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMethodsHandler) ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY) VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY)
VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY) VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY)
@@ -0,0 +1,6 @@
// "Implement as constructor parameters" "true"
interface I {
val foo: Int
}
<caret>class A : I
@@ -0,0 +1,6 @@
// "Implement as constructor parameters" "true"
interface I {
val foo: Int
}
<caret>class A(override val foo: Int) : I
@@ -0,0 +1,13 @@
// "Implement as constructor parameters" "false"
// ACTION: Create test
// ACTION: Implement members
// ACTION: Make 'A' abstract
// ACTION: Make internal
// ACTION: Make private
// ACTION: Move 'A' to separate file
// ERROR: Class 'A' must be declared abstract or implement abstract member public abstract fun foo(): kotlin.Int defined in I
interface I {
fun foo(): Int
}
<caret>class A : I
@@ -0,0 +1,12 @@
// "Implement as constructor parameters" "false"
// ACTION: Create test
// ACTION: Implement members
// ACTION: Make internal
// ACTION: Make private
// ACTION: Move 'A' to separate file
// ERROR: Object 'A' must be declared abstract or implement abstract member public abstract val foo: kotlin.Int defined in I
interface I {
val foo: Int
}
<caret>object A : I
@@ -5364,6 +5364,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("implemenAsConstructorParameter.kt")
public void testImplemenAsConstructorParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameter.kt");
doTest(fileName);
}
@TestMetadata("implemenAsConstructorParameterFunction.kt")
public void testImplemenAsConstructorParameterFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameterFunction.kt");
doTest(fileName);
}
@TestMetadata("implemenAsConstructorParameterInObject.kt")
public void testImplemenAsConstructorParameterInObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implemenAsConstructorParameterInObject.kt");
doTest(fileName);
}
@TestMetadata("implementMember.kt") @TestMetadata("implementMember.kt")
public void testImplementMember() throws Exception { public void testImplementMember() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implementMember.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/override/implementMember.kt");