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 ->
@@ -184,7 +184,7 @@ abstract class KotlinGenerateTestSupportActionBase(
val functionDescriptor = functionInPlace.unsafeResolveToDescriptor() as FunctionDescriptor
val overriddenDescriptors = functionDescriptor.overriddenDescriptors
val bodyText = when (overriddenDescriptors.size) {
0 -> generateUnsupportedOrSuperCall(project, functionDescriptor, BodyType.EMPTY)
0 -> generateUnsupportedOrSuperCall(project, functionDescriptor, BodyType.FROM_TEMPLATE)
1 -> generateUnsupportedOrSuperCall(project, overriddenDescriptors.single(), BodyType.SUPER)
else -> generateUnsupportedOrSuperCall(project, overriddenDescriptors.first(), BodyType.QUALIFIED_SUPER)
}
@@ -184,7 +184,7 @@ abstract class KotlinGenerateTestSupportActionBase(
val functionDescriptor = functionInPlace.unsafeResolveToDescriptor() as FunctionDescriptor
val overriddenDescriptors = functionDescriptor.overriddenDescriptors
val bodyText = when (overriddenDescriptors.size) {
0 -> generateUnsupportedOrSuperCall(project, functionDescriptor, BodyType.EMPTY)
0 -> generateUnsupportedOrSuperCall(project, functionDescriptor, BodyType.FROM_TEMPLATE)
1 -> generateUnsupportedOrSuperCall(project, overriddenDescriptors.single(), BodyType.SUPER)
else -> generateUnsupportedOrSuperCall(project, overriddenDescriptors.first(), BodyType.QUALIFIED_SUPER)
}
@@ -80,6 +80,6 @@ fun confirmMemberRewrite(targetClass: KtClass, vararg descriptors: FunctionDescr
fun generateFunctionSkeleton(descriptor: FunctionDescriptor, targetClass: KtClassOrObject): KtNamedFunction {
return OverrideMemberChooserObject
.create(targetClass.project, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY)
.create(targetClass.project, descriptor, descriptor, OverrideMemberChooserObject.BodyType.FROM_TEMPLATE)
.generateMember(targetClass, false) as KtNamedFunction
}
@@ -126,7 +126,7 @@ abstract class ImplementAbstractMemberIntentionBase :
val chooserObject = OverrideMemberChooserObject.create(member.project,
descriptorToImplement,
descriptorToImplement,
OverrideMemberChooserObject.BodyType.EMPTY,
OverrideMemberChooserObject.BodyType.FROM_TEMPLATE,
preferConstructorParameters)
OverrideImplementMembersHandler.generateMembers(editor, targetClass, listOf(chooserObject), false)
}
@@ -126,7 +126,7 @@ abstract class ImplementAbstractMemberIntentionBase :
val chooserObject = OverrideMemberChooserObject.create(member.project,
descriptorToImplement,
descriptorToImplement,
OverrideMemberChooserObject.BodyType.EMPTY,
OverrideMemberChooserObject.BodyType.FROM_TEMPLATE,
preferConstructorParameters)
OverrideImplementMembersHandler.generateMembers(editor, targetClass, listOf(chooserObject), false)
}
@@ -19,13 +19,16 @@ package org.jetbrains.kotlin.idea.quickfix.expectactual
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
@@ -53,7 +56,9 @@ class AddActualFix(
listOfNotNull(element.primaryConstructor)
)
for (declaration in pureActualClass.declarations) {
element.addDeclaration(declaration)
val actualDeclaration = element.addDeclaration(declaration)
val reformatted = CodeStyleManager.getInstance(project).reformat(actualDeclaration)
ShortenReferences.DEFAULT.process(reformatted as KtElement)
}
val primaryConstructor = pureActualClass.primaryConstructor
if (element.primaryConstructor == null && primaryConstructor != null) {
@@ -25,7 +25,6 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.JavaDirectoryService
import com.intellij.psi.codeStyle.CodeStyleManager
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -36,10 +35,12 @@ import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
import org.jetbrains.kotlin.idea.core.overrideImplement.generateActualMember
import org.jetbrains.kotlin.idea.core.overrideImplement.generateTopLevelActual
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
@@ -192,7 +193,7 @@ class CreateActualPropertyFix(
actualPlatform: MultiTargetPlatform.Specific
) : CreateActualFix<KtProperty>(property, actualModule, actualPlatform, { project, element ->
val descriptor = element.toDescriptor() as? PropertyDescriptor
descriptor?.let { generateProperty(project, element, descriptor, actualNeeded = true) }
descriptor?.let { generateProperty(project, element, descriptor) }
}) {
override val elementType = "property"
@@ -204,7 +205,7 @@ class CreateActualFunctionFix(
actualPlatform: MultiTargetPlatform.Specific
) : CreateActualFix<KtFunction>(function, actualModule, actualPlatform, { project, element ->
val descriptor = element.toDescriptor() as? FunctionDescriptor
descriptor?.let { generateFunction(project, element, descriptor, actualNeeded = true) }
descriptor?.let { generateFunction(project, element, descriptor) }
}) {
override val elementType = "function"
@@ -294,6 +295,7 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
}
}
}
actualClass.replaceExpectModifier(actualNeeded)
declLoop@ for (expectedDeclaration in expectedClass.declarations.filter { !it.exists() }) {
val descriptor = expectedDeclaration.toDescriptor() ?: continue
@@ -309,8 +311,8 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
continue@declLoop
}
when (expectedDeclaration) {
is KtFunction -> generateFunction(project, expectedDeclaration, descriptor as FunctionDescriptor, actualNeeded = true)
is KtProperty -> generateProperty(project, expectedDeclaration, descriptor as PropertyDescriptor, actualNeeded = true)
is KtFunction -> generateFunction(project, expectedDeclaration, descriptor as FunctionDescriptor, actualClass)
is KtProperty -> generateProperty(project, expectedDeclaration, descriptor as PropertyDescriptor, actualClass)
else -> continue@declLoop
}
}
@@ -329,9 +331,7 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass(
it.removeParameterDefaultValues()
}
return actualClass.apply {
replaceExpectModifier(actualNeeded)
}
return actualClass
}
private val forbiddenAnnotationFqNames = setOf(
@@ -340,41 +340,20 @@ private val forbiddenAnnotationFqNames = setOf(
ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAME
)
private fun KtPsiFactory.generateFunction(
private fun generateFunction(
project: Project,
expectedFunction: KtFunction,
descriptor: FunctionDescriptor,
actualNeeded: Boolean
targetClass: KtClassOrObject? = null
): KtFunction {
val returnType = descriptor.returnType
val body = run {
if (expectedFunction.hasBody()) {
""
} else if (returnType != null && !KotlinBuiltIns.isUnit(returnType)) {
val delegation = getFunctionBodyTextFromTemplate(
project,
TemplateKind.FUNCTION,
descriptor.name.asString(),
IdeDescriptorRenderers.SOURCE_CODE.renderType(returnType)
)
"{$delegation\n}"
} else {
"{}"
}
}
return (if (expectedFunction is KtSecondaryConstructor) {
createSecondaryConstructor(expectedFunction.text + " " + body)
val memberChooserObject = OverrideMemberChooserObject.create(
expectedFunction, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE
)
return if (targetClass != null) {
memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true)
} else {
createFunction(expectedFunction.text + " " + body)
} as KtFunction).apply {
replaceExpectModifier(actualNeeded)
if (returnType != null && KotlinBuiltIns.isUnit(returnType)) {
typeReference = null
}
removeParameterDefaultValues()
}
memberChooserObject.generateTopLevelActual(copyDoc = true, project = project)
} as KtFunction
}
private fun KtFunction.removeParameterDefaultValues() {
@@ -387,27 +366,20 @@ private fun KtFunction.removeParameterDefaultValues() {
}
}
private fun KtPsiFactory.generateProperty(
private fun generateProperty(
project: Project,
expectedProperty: KtProperty,
descriptor: PropertyDescriptor,
actualNeeded: Boolean
targetClass: KtClassOrObject? = null
): KtProperty {
val body = buildString {
append("\nget()")
append(" = ")
append(getFunctionBodyTextFromTemplate(
project,
TemplateKind.FUNCTION,
descriptor.name.asString(),
descriptor.returnType?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) } ?: ""
))
if (descriptor.isVar) {
append("\nset(value) {}")
}
}
return createProperty(expectedProperty.text + " " + body).apply {
replaceExpectModifier(actualNeeded)
}
val memberChooserObject = OverrideMemberChooserObject.create(
expectedProperty, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE
)
return if (targetClass != null) {
memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true)
} else {
memberChooserObject.generateTopLevelActual(copyDoc = true, project = project)
} as KtProperty
}
@@ -9,5 +9,5 @@ actual abstract class Abstract {
}
actual val isGood: Boolean
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
}
+4 -3
View File
@@ -8,16 +8,17 @@ actual class My {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
actual fun baz() {}
actual fun baz() {
}
actual constructor(flag: Boolean) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
actual val isGood: Boolean
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
actual var status: Int
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
set(value) {}
}
@@ -4,5 +4,6 @@
actual class My {
actual constructor()
actual fun test() {}
actual fun test() {
}
}
@@ -4,5 +4,6 @@
actual class My {
actual constructor(a: Int, b: String)
actual fun test() {}
actual fun test() {
}
}
@@ -4,5 +4,6 @@
actual class My actual constructor(a: Int, b: String) {
actual constructor(b: String, a: Int)
actual fun test() {}
actual fun test() {
}
}
@@ -3,5 +3,5 @@
actual class <caret>My(actual val name: String) {
actual val age: String
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
}
@@ -6,7 +6,7 @@ actual class <caret>My {
actual val correct = true
actual val x: String
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
actual val pi: Double
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
}
@@ -2,6 +2,6 @@
// DISABLE-ERRORS
actual class My {
// Incompilable function
actual fun foo() {}
actual fun foo() {
}
}
@@ -0,0 +1,7 @@
// "Create actual class for module proj_JVM (JVM)" "true"
import kotlin.random.Random
expect class My<caret>Generator {
fun generate(): Random
}
@@ -0,0 +1,7 @@
// "Create actual class for module proj_JVM (JVM)" "true"
import kotlin.random.Random
expect class MyGenerator {
fun generate(): Random
}
@@ -0,0 +1 @@
// MyGenerator: to be implemented
@@ -0,0 +1,8 @@
import kotlin.random.Random
// MyGenerator: to be implemented
actual class MyGenerator {
actual fun generate(): Random {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -3,7 +3,8 @@
actual class WithCompanion {
actual companion object {
actual fun foo() {}
actual fun foo() {
}
}
}
@@ -1,4 +1,5 @@
// to be created
actual class C {
actual fun f(p: Int) {}
actual fun f(p: Int) {
}
}
+1 -1
View File
@@ -5,6 +5,6 @@ actual enum class MyEnum {
LAST;
actual val num: Int
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
}
@@ -9,7 +9,8 @@ actual interface Interface {
actual var status: Int
actual class Nested {
actual fun bar() {}
actual fun bar() {
}
}
}
@@ -5,11 +5,13 @@ actual class WithNested {
}
actual class Nested {
actual fun bar() {}
actual fun bar() {
}
}
actual inner class Inner {
actual fun baz() {}
actual fun baz() {
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
// x: to be implemented
actual var x: Int
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
set(value) {}
@@ -3,4 +3,5 @@
package test
actual fun testHelper() {}
actual fun testHelper() {
}
@@ -1,2 +1,3 @@
// testHelper: to be implemented
actual fun testHelper() {}
actual fun testHelper() {
}
@@ -144,6 +144,11 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul
runTest("idea/testData/multiModuleQuickFix/classWithIncompilableFunction/");
}
@TestMetadata("classWithJdk")
public void testClassWithJdk() throws Exception {
runTest("idea/testData/multiModuleQuickFix/classWithJdk/");
}
@TestMetadata("companionAbsence")
public void testCompanionAbsence() throws Exception {
runTest("idea/testData/multiModuleQuickFix/companionAbsence/");