Use OverrideMemberChooserObject to generate actual decls in quick-fixes

This can fix a lot of issues related to inexact generation, in particular
#KT-27093 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-11-13 15:41:23 +03:00
parent 5645b6c1b7
commit 3612c2983e
31 changed files with 261 additions and 162 deletions
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.resolve.OverrideResolver
open class ImplementMembersHandler : OverrideImplementMembersHandler(), IntentionAction {
override fun collectMembersToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
return OverrideResolver.getMissingImplementations(descriptor)
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.EMPTY) }
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.FROM_TEMPLATE) }
}
override fun getChooserTitle() = "Implement Members"
@@ -67,6 +67,6 @@ class ImplementAsConstructorParameter : ImplementMembersHandler() {
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) }
.map { OverrideMemberChooserObject.create(project, it, it, OverrideMemberChooserObject.BodyType.FROM_TEMPLATE, true) }
}
}
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.TemplateKind
import org.jetbrains.kotlin.idea.core.getFunctionBodyTextFromTemplate
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.*
import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject
import org.jetbrains.kotlin.idea.j2k.IdeaDocCommentConverter
import org.jetbrains.kotlin.idea.kdoc.KDocElementFactory
@@ -45,9 +46,10 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden
import org.jetbrains.kotlin.util.findCallableMemberBySignature
interface OverrideMemberChooserObject : ClassMember {
sealed class BodyType {
sealed class BodyType(val requiresReturn: Boolean = true) {
object NO_BODY : BodyType()
object EMPTY : BodyType()
object EMPTY_OR_TEMPLATE : BodyType(requiresReturn = false)
object FROM_TEMPLATE : BodyType(requiresReturn = false)
object SUPER : BodyType()
object QUALIFIED_SUPER : BodyType()
@@ -60,27 +62,36 @@ interface OverrideMemberChooserObject : ClassMember {
val preferConstructorParameter: Boolean
companion object {
fun create(project: Project,
descriptor: CallableMemberDescriptor,
immediateSuper: CallableMemberDescriptor,
bodyType: BodyType,
preferConstructorParameter: Boolean = false
fun create(
project: Project,
descriptor: CallableMemberDescriptor,
immediateSuper: CallableMemberDescriptor,
bodyType: BodyType,
preferConstructorParameter: Boolean = false
): OverrideMemberChooserObject {
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor)
return if (declaration != null) {
WithDeclaration(descriptor, declaration, immediateSuper, bodyType, preferConstructorParameter)
}
else {
create(declaration, descriptor, immediateSuper, bodyType, preferConstructorParameter)
} else {
WithoutDeclaration(descriptor, immediateSuper, bodyType, preferConstructorParameter)
}
}
fun create(
declaration: PsiElement,
descriptor: CallableMemberDescriptor,
immediateSuper: CallableMemberDescriptor,
bodyType: BodyType,
preferConstructorParameter: Boolean = false
): OverrideMemberChooserObject =
WithDeclaration(descriptor, declaration, immediateSuper, bodyType, preferConstructorParameter)
private class WithDeclaration(
descriptor: CallableMemberDescriptor,
declaration: PsiElement,
override val immediateSuper: CallableMemberDescriptor,
override val bodyType: BodyType,
override val preferConstructorParameter: Boolean
descriptor: CallableMemberDescriptor,
declaration: PsiElement,
override val immediateSuper: CallableMemberDescriptor,
override val bodyType: BodyType,
override val preferConstructorParameter: Boolean
) : DescriptorMemberChooserObject(declaration, descriptor), OverrideMemberChooserObject {
override val descriptor: CallableMemberDescriptor
@@ -88,40 +99,68 @@ interface OverrideMemberChooserObject : ClassMember {
}
private class WithoutDeclaration(
override val descriptor: CallableMemberDescriptor,
override val immediateSuper: CallableMemberDescriptor,
override val bodyType: BodyType,
override val preferConstructorParameter: Boolean
) : MemberChooserObjectBase(DescriptorMemberChooserObject.getText(descriptor), DescriptorMemberChooserObject.getIcon(null, descriptor)), OverrideMemberChooserObject {
override val descriptor: CallableMemberDescriptor,
override val immediateSuper: CallableMemberDescriptor,
override val bodyType: BodyType,
override val preferConstructorParameter: Boolean
) : MemberChooserObjectBase(
DescriptorMemberChooserObject.getText(descriptor), DescriptorMemberChooserObject.getIcon(null, descriptor)
), OverrideMemberChooserObject {
override fun getParentNodeDelegate(): MemberChooserObject? {
val parentClassifier = descriptor.containingDeclaration as? ClassifierDescriptor ?: return null
return MemberChooserObjectBase(DescriptorMemberChooserObject.getText(parentClassifier), DescriptorMemberChooserObject.getIcon(null, parentClassifier))
return MemberChooserObjectBase(
DescriptorMemberChooserObject.getText(parentClassifier), DescriptorMemberChooserObject.getIcon(null, parentClassifier)
)
}
}
}
}
fun OverrideMemberChooserObject.generateMember(targetClass: KtClassOrObject, copyDoc: Boolean): KtCallableDeclaration {
val project = targetClass.project
fun OverrideMemberChooserObject.generateTopLevelActual(
copyDoc: Boolean,
project: Project
) = generateMember(null, copyDoc, project, forceActual = true)
fun OverrideMemberChooserObject.generateActualMember(
targetClass: KtClassOrObject,
copyDoc: Boolean
) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = true)
fun OverrideMemberChooserObject.generateMember(
targetClass: KtClassOrObject,
copyDoc: Boolean
) = generateMember(targetClass, copyDoc, targetClass.project, forceActual = false)
private fun OverrideMemberChooserObject.generateMember(
targetClass: KtClassOrObject?,
copyDoc: Boolean,
project: Project,
forceActual: Boolean
): KtCallableDeclaration {
val descriptor = immediateSuper
val bodyType = when {
targetClass.hasExpectModifier() -> OverrideMemberChooserObject.BodyType.NO_BODY
descriptor.extensionReceiverParameter != null -> OverrideMemberChooserObject.BodyType.EMPTY
targetClass?.hasExpectModifier() == true -> NO_BODY
descriptor.extensionReceiverParameter != null -> FROM_TEMPLATE
else -> bodyType
}
if (preferConstructorParameter && descriptor is PropertyDescriptor) return generateConstructorParameter(project, descriptor)
val renderer = if (forceActual) ACTUAL_RENDERER else OVERRIDE_RENDERER
if (preferConstructorParameter && descriptor is PropertyDescriptor) {
return generateConstructorParameter(project, descriptor, renderer)
}
val newMember: KtCallableDeclaration = when (descriptor) {
is SimpleFunctionDescriptor -> generateFunction(project, descriptor, bodyType)
is PropertyDescriptor -> generateProperty(project, descriptor, bodyType)
is FunctionDescriptor -> generateFunction(project, descriptor, renderer, bodyType)
is PropertyDescriptor -> generateProperty(project, descriptor, renderer, bodyType)
else -> error("Unknown member to override: $descriptor")
}
if (targetClass.hasActualModifier()) {
if (forceActual) {
newMember.addModifier(KtTokens.ACTUAL_KEYWORD)
} else if (targetClass?.hasActualModifier() == true) {
val expectClassDescriptors =
targetClass.resolveToDescriptorIfAny()?.expectedDescriptors()?.filterIsInstance<ClassDescriptor>().orEmpty()
if (expectClassDescriptors.any { expectClassDescriptor ->
@@ -143,7 +182,7 @@ fun OverrideMemberChooserObject.generateMember(targetClass: KtClassOrObject, cop
findDocComment(superDeclaration)
is PsiDocCommentOwner -> {
val kDocText = superDeclaration.docComment?.let { IdeaDocCommentConverter.convertDocComment(it) }
if (kDocText.isNullOrEmpty()) null else KDocElementFactory(project).createKDocFromText(kDocText!!)
if (kDocText.isNullOrEmpty()) null else KDocElementFactory(project).createKDocFromText(kDocText)
}
else -> null
}
@@ -171,6 +210,12 @@ private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions {
presentableUnresolvedTypes = true
}
private val ACTUAL_RENDERER = OVERRIDE_RENDERER.withOptions {
modifiers = setOf(DescriptorRendererModifier.ANNOTATIONS)
renderConstructorKeyword = true
secondaryConstructorsAsPrimary = false
}
private fun PropertyDescriptor.wrap(): PropertyDescriptor {
val delegate = copy(containingDeclaration, Modality.OPEN, visibility, kind, true) as PropertyDescriptor
val newDescriptor = object : PropertyDescriptor by delegate {
@@ -181,74 +226,112 @@ private fun PropertyDescriptor.wrap(): PropertyDescriptor {
}
private fun FunctionDescriptor.wrap(): FunctionDescriptor {
if (this is ClassConstructorDescriptor) return this.wrap()
return object : FunctionDescriptor by this {
override fun isExpect() = false
override fun getModality() = Modality.OPEN
override fun getReturnType() = this@wrap.returnType?.approximateFlexibleTypes(preferNotNull = true, preferStarForRaw = true)
override fun getOverriddenDescriptors() = listOf(this@wrap)
override fun <R : Any?, D : Any?> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D) = visitor.visitFunctionDescriptor(this, data)
override fun <R : Any?, D : Any?> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D) =
visitor.visitFunctionDescriptor(this, data)
}
}
private fun generateProperty(project: Project, descriptor: PropertyDescriptor, bodyType: OverrideMemberChooserObject.BodyType): KtProperty {
val newDescriptor = descriptor.wrap()
val body =
if (bodyType != OverrideMemberChooserObject.BodyType.NO_BODY) {
buildString {
append("\nget()")
append(" = ")
append(generateUnsupportedOrSuperCall(project, descriptor, bodyType))
if (descriptor.isVar) {
append("\nset(value) {}")
}
}
} else ""
return KtPsiFactory(project).createProperty(OVERRIDE_RENDERER.render(newDescriptor) + body)
private fun ClassConstructorDescriptor.wrap(): ClassConstructorDescriptor {
return object : ClassConstructorDescriptor by this {
override fun isExpect() = false
override fun getModality() = Modality.FINAL
override fun getReturnType() = this@wrap.returnType.approximateFlexibleTypes(preferNotNull = true, preferStarForRaw = true)
override fun getOverriddenDescriptors() = listOf(this@wrap)
override fun <R : Any?, D : Any?> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D) =
visitor.visitConstructorDescriptor(this, data)
}
}
private fun generateConstructorParameter(project: Project, descriptor: PropertyDescriptor): KtParameter {
val newDescriptor = descriptor.wrap()
newDescriptor.setSingleOverridden(descriptor)
return KtPsiFactory(project).createParameter(OVERRIDE_RENDERER.render(newDescriptor))
}
private fun generateFunction(project: Project, descriptor: FunctionDescriptor, bodyType: OverrideMemberChooserObject.BodyType): KtNamedFunction {
private fun generateProperty(
project: Project,
descriptor: PropertyDescriptor,
renderer: DescriptorRenderer,
bodyType: OverrideMemberChooserObject.BodyType
): KtProperty {
val newDescriptor = descriptor.wrap()
val returnType = descriptor.returnType
val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType)
val body = if (bodyType != OverrideMemberChooserObject.BodyType.NO_BODY) {
val delegation = generateUnsupportedOrSuperCall(project, descriptor, bodyType)
"{" + (if (returnsNotUnit && bodyType != OverrideMemberChooserObject.BodyType.EMPTY) "return " else "") + delegation + "\n}"
}
else ""
val body =
if (bodyType != NO_BODY) {
buildString {
append("\nget()")
append(" = ")
append(generateUnsupportedOrSuperCall(project, descriptor, bodyType, !returnsNotUnit))
if (descriptor.isVar) {
append("\nset(value) {}")
}
}
} else ""
return KtPsiFactory(project).createProperty(renderer.render(newDescriptor) + body)
}
return KtPsiFactory(project).createFunction(OVERRIDE_RENDERER.render(newDescriptor) + body)
private fun generateConstructorParameter(project: Project, descriptor: PropertyDescriptor, renderer: DescriptorRenderer): KtParameter {
val newDescriptor = descriptor.wrap()
newDescriptor.setSingleOverridden(descriptor)
return KtPsiFactory(project).createParameter(renderer.render(newDescriptor))
}
private fun generateFunction(
project: Project,
descriptor: FunctionDescriptor,
renderer: DescriptorRenderer,
bodyType: OverrideMemberChooserObject.BodyType
): KtFunction {
val newDescriptor = descriptor.wrap()
val returnType = descriptor.returnType
val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType)
val body = if (bodyType != NO_BODY) {
val delegation = generateUnsupportedOrSuperCall(project, descriptor, bodyType, !returnsNotUnit)
val returnPrefix = if (returnsNotUnit && bodyType.requiresReturn) "return " else ""
"{$returnPrefix$delegation\n}"
} else ""
val factory = KtPsiFactory(project)
val functionText = renderer.render(newDescriptor) + body
return when (descriptor) {
is ClassConstructorDescriptor -> factory.createSecondaryConstructor(functionText)
else -> factory.createFunction(functionText)
}
}
fun generateUnsupportedOrSuperCall(
project: Project,
descriptor: CallableMemberDescriptor,
bodyType: OverrideMemberChooserObject.BodyType
project: Project,
descriptor: CallableMemberDescriptor,
bodyType: OverrideMemberChooserObject.BodyType,
canBeEmpty: Boolean = true
): String {
if (bodyType == OverrideMemberChooserObject.BodyType.EMPTY) {
val templateKind = if (descriptor is FunctionDescriptor) TemplateKind.FUNCTION else TemplateKind.PROPERTY_INITIALIZER
return getFunctionBodyTextFromTemplate(project,
templateKind,
descriptor.name.asString(),
descriptor.returnType?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) } ?: "Unit",
null)
}
else {
return buildString {
val effectiveBodyType = if (!canBeEmpty && bodyType == EMPTY_OR_TEMPLATE) FROM_TEMPLATE else bodyType
when (effectiveBodyType) {
EMPTY_OR_TEMPLATE -> return ""
FROM_TEMPLATE -> {
val templateKind = if (descriptor is FunctionDescriptor) TemplateKind.FUNCTION else TemplateKind.PROPERTY_INITIALIZER
return getFunctionBodyTextFromTemplate(
project,
templateKind,
descriptor.name.asString(),
descriptor.returnType?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) } ?: "Unit",
null
)
}
else -> return buildString {
if (bodyType is OverrideMemberChooserObject.BodyType.Delegate) {
append(bodyType.receiverName)
}
else {
} else {
append("super")
if (bodyType == OverrideMemberChooserObject.BodyType.QUALIFIED_SUPER) {
val superClassFqName = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(descriptor.containingDeclaration as ClassifierDescriptor)
if (bodyType == QUALIFIED_SUPER) {
val superClassFqName = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(
descriptor.containingDeclaration as ClassifierDescriptor
)
append("<").append(superClassFqName).append(">")
}
}
@@ -69,7 +69,7 @@ class OverrideMembersHandler(private val preferConstructorParameters: Boolean =
descriptor.kind == ClassKind.INTERFACE && realSuper.builtIns.isMemberOfAny(realSuper) ->
OverrideMemberChooserObject.BodyType.NO_BODY
immediateSuperToUse.modality == Modality.ABSTRACT ->
OverrideMemberChooserObject.BodyType.EMPTY
OverrideMemberChooserObject.BodyType.FROM_TEMPLATE
realSupersToUse.size == 1 ->
OverrideMemberChooserObject.BodyType.SUPER
else ->