diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt index 0a92194a8a1..098dde1d191 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt @@ -186,6 +186,7 @@ interface DescriptorRendererOptions { var withoutReturnType: Boolean var normalizedVisibilities: Boolean var renderDefaultVisibility: Boolean + var renderDefaultModality: Boolean var uninferredTypeParameterAsName: Boolean var overrideRenderingPolicy: OverrideRenderingPolicy var valueParametersHandler: DescriptorRenderer.ValueParametersHandler diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt index 1356eb01f2a..70e102db453 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt @@ -478,17 +478,33 @@ internal class DescriptorRendererImpl( builder.append(renderKeyword(visibility.displayName)).append(" ") } - private fun renderModality(modality: Modality, builder: StringBuilder) { + private fun renderModality(modality: Modality, builder: StringBuilder, defaultModality: Modality) { + if (!renderDefaultModality && modality == defaultModality) return renderModifier(builder, DescriptorRendererModifier.MODALITY in modifiers, modality.name.toLowerCase()) } + private fun MemberDescriptor.implicitModalityWithoutExtensions(): Modality { + if (this is ClassDescriptor) { + return if (kind == ClassKind.INTERFACE) Modality.ABSTRACT else Modality.FINAL + } + val containingClassDescriptor = containingDeclaration as? ClassDescriptor ?: return Modality.FINAL + if (this !is CallableMemberDescriptor) return Modality.FINAL + if (this.overriddenDescriptors.isNotEmpty()) { + if (containingClassDescriptor.modality != Modality.FINAL) return Modality.OPEN + } + if (containingClassDescriptor.kind == ClassKind.INTERFACE && this.visibility != Visibilities.PRIVATE) { + return if (this.modality == Modality.ABSTRACT) Modality.ABSTRACT else Modality.OPEN + } + return Modality.FINAL + } + private fun renderModalityForCallable(callable: CallableMemberDescriptor, builder: StringBuilder) { if (!DescriptorUtils.isTopLevelDeclaration(callable) || callable.modality != Modality.FINAL) { if (overrideRenderingPolicy == OverrideRenderingPolicy.RENDER_OVERRIDE && callable.modality == Modality.OPEN && overridesSomething(callable)) { return } - renderModality(callable.modality, builder) + renderModality(callable.modality, builder, callable.implicitModalityWithoutExtensions()) } } @@ -913,7 +929,7 @@ internal class DescriptorRendererImpl( } if (!(klass.kind == ClassKind.INTERFACE && klass.modality == Modality.ABSTRACT || klass.kind.isSingleton && klass.modality == Modality.FINAL)) { - renderModality(klass.modality, builder) + renderModality(klass.modality, builder, klass.implicitModalityWithoutExtensions()) } renderMemberModifiers(klass, builder) renderModifier(builder, DescriptorRendererModifier.INNER in modifiers && klass.isInner, "inner") diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt index e80e7c8d675..ec1404c26ae 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt @@ -81,6 +81,7 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions { override var enhancedTypes by property(false) override var normalizedVisibilities by property(false) override var renderDefaultVisibility by property(true) + override var renderDefaultModality by property(true) override var uninferredTypeParameterAsName by property(false) override var includePropertyConstant by property(false) override var withoutTypeParameters by property(false) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt index 218808800b6..0273614169b 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt @@ -142,7 +142,7 @@ private fun OverrideMemberChooserObject.generateMember( val bodyType = when { targetClass?.hasExpectModifier() == true -> NO_BODY - descriptor.extensionReceiverParameter != null -> FROM_TEMPLATE + descriptor.extensionReceiverParameter != null && !forceActual -> FROM_TEMPLATE else -> bodyType } @@ -211,27 +211,31 @@ private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions { } private val ACTUAL_RENDERER = OVERRIDE_RENDERER.withOptions { - modifiers = setOf(DescriptorRendererModifier.ANNOTATIONS) + modifiers = DescriptorRendererModifier.ALL renderConstructorKeyword = true secondaryConstructorsAsPrimary = false + renderDefaultVisibility = false + renderDefaultModality = false } -private fun PropertyDescriptor.wrap(): PropertyDescriptor { - val delegate = copy(containingDeclaration, Modality.OPEN, visibility, kind, true) as PropertyDescriptor +private fun PropertyDescriptor.wrap(forceOverride: Boolean): PropertyDescriptor { + val delegate = copy(containingDeclaration, if (forceOverride) Modality.OPEN else modality, visibility, kind, true) as PropertyDescriptor val newDescriptor = object : PropertyDescriptor by delegate { override fun isExpect() = false } - newDescriptor.setSingleOverridden(this) + if (forceOverride) { + newDescriptor.setSingleOverridden(this) + } return newDescriptor } -private fun FunctionDescriptor.wrap(): FunctionDescriptor { +private fun FunctionDescriptor.wrap(forceOverride: Boolean, forceAbstract: Boolean): FunctionDescriptor { if (this is ClassConstructorDescriptor) return this.wrap() return object : FunctionDescriptor by this { override fun isExpect() = false - override fun getModality() = Modality.OPEN + override fun getModality() = if (forceAbstract) Modality.ABSTRACT else if (forceOverride) Modality.OPEN else this@wrap.modality override fun getReturnType() = this@wrap.returnType?.approximateFlexibleTypes(preferNotNull = true, preferStarForRaw = true) - override fun getOverriddenDescriptors() = listOf(this@wrap) + override fun getOverriddenDescriptors() = if (forceOverride) listOf(this@wrap) else emptyList() override fun accept(visitor: DeclarationDescriptorVisitor, data: D) = visitor.visitFunctionDescriptor(this, data) } @@ -242,7 +246,7 @@ private fun ClassConstructorDescriptor.wrap(): ClassConstructorDescriptor { 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 getOverriddenDescriptors(): List = emptyList() override fun accept(visitor: DeclarationDescriptorVisitor, data: D) = visitor.visitConstructorDescriptor(this, data) } @@ -254,7 +258,8 @@ private fun generateProperty( renderer: DescriptorRenderer, bodyType: OverrideMemberChooserObject.BodyType ): KtProperty { - val newDescriptor = descriptor.wrap() + val actualRendererUsed = renderer === ACTUAL_RENDERER + val newDescriptor = descriptor.wrap(forceOverride = !actualRendererUsed) val returnType = descriptor.returnType val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType) @@ -274,7 +279,8 @@ private fun generateProperty( } private fun generateConstructorParameter(project: Project, descriptor: PropertyDescriptor, renderer: DescriptorRenderer): KtParameter { - val newDescriptor = descriptor.wrap() + val actualRendererUsed = renderer === ACTUAL_RENDERER + val newDescriptor = descriptor.wrap(forceOverride = !actualRendererUsed) newDescriptor.setSingleOverridden(descriptor) return KtPsiFactory(project).createParameter(renderer.render(newDescriptor)) } @@ -285,7 +291,8 @@ private fun generateFunction( renderer: DescriptorRenderer, bodyType: OverrideMemberChooserObject.BodyType ): KtFunction { - val newDescriptor = descriptor.wrap() + val actualRendererUsed = renderer === ACTUAL_RENDERER + val newDescriptor = descriptor.wrap(forceAbstract = actualRendererUsed && bodyType == NO_BODY, forceOverride = !actualRendererUsed) val returnType = descriptor.returnType val returnsNotUnit = returnType != null && !KotlinBuiltIns.isUnit(returnType) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt index 363590f5fe6..4170abda245 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/expectactual/CreateActualFix.kt @@ -25,17 +25,16 @@ 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.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory 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.OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE +import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.BodyType.NO_BODY +import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject.Companion.create import org.jetbrains.kotlin.idea.core.overrideImplement.generateActualMember import org.jetbrains.kotlin.idea.core.overrideImplement.generateTopLevelActual import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction @@ -252,7 +251,6 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass( } else { createClass(expectedText) } - val isInterface = expectedClass is KtClass && expectedClass.isInterface() actualClass.declarations.forEach { if (it.exists()) { it.delete() @@ -261,16 +259,7 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass( when (it) { is KtEnumEntry -> return@forEach is KtClassOrObject -> it.delete() - is KtCallableDeclaration -> { - if (!isInterface && !it.hasModifier(KtTokens.ABSTRACT_KEYWORD)) { - it.delete() - } else { - it.addModifier(KtTokens.ACTUAL_KEYWORD) - if (it is KtFunction) { - it.removeParameterDefaultValues() - } - } - } + is KtCallableDeclaration -> it.delete() } } val primaryConstructor = actualClass.primaryConstructor @@ -307,9 +296,6 @@ internal fun KtPsiFactory.generateClassOrObjectByExpectedClass( continue@declLoop } is KtCallableDeclaration -> { - if (isInterface || expectedDeclaration.hasModifier(KtTokens.ABSTRACT_KEYWORD)) { - continue@declLoop - } when (expectedDeclaration) { is KtFunction -> generateFunction(project, expectedDeclaration, descriptor as FunctionDescriptor, actualClass) is KtProperty -> generateProperty(project, expectedDeclaration, descriptor as PropertyDescriptor, actualClass) @@ -346,8 +332,9 @@ private fun generateFunction( descriptor: FunctionDescriptor, targetClass: KtClassOrObject? = null ): KtFunction { - val memberChooserObject = OverrideMemberChooserObject.create( - expectedFunction, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE + val memberChooserObject = create( + expectedFunction, descriptor, descriptor, + if (descriptor.modality == Modality.ABSTRACT) NO_BODY else EMPTY_OR_TEMPLATE ) return if (targetClass != null) { memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true) @@ -372,14 +359,14 @@ private fun generateProperty( descriptor: PropertyDescriptor, targetClass: KtClassOrObject? = null ): KtProperty { - val memberChooserObject = OverrideMemberChooserObject.create( - expectedProperty, descriptor, descriptor, OverrideMemberChooserObject.BodyType.EMPTY_OR_TEMPLATE + val memberChooserObject = create( + expectedProperty, descriptor, descriptor, + if (descriptor.modality == Modality.ABSTRACT) NO_BODY else EMPTY_OR_TEMPLATE ) return if (targetClass != null) { memberChooserObject.generateActualMember(targetClass = targetClass, copyDoc = true) } else { memberChooserObject.generateTopLevelActual(copyDoc = true, project = project) } as KtProperty - } diff --git a/idea/testData/multiModuleQuickFix/abstract/js/Abstract.kt.after b/idea/testData/multiModuleQuickFix/abstract/js/Abstract.kt.after index 705e96d98a3..1e520d3f4dc 100644 --- a/idea/testData/multiModuleQuickFix/abstract/js/Abstract.kt.after +++ b/idea/testData/multiModuleQuickFix/abstract/js/Abstract.kt.after @@ -1,13 +1,12 @@ // Abstract: to be implemented actual abstract class Abstract { - - actual abstract fun String.bar(y: Double): Boolean - - actual abstract var status: Int actual fun foo(param: String): Int { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } + actual abstract fun String.bar(y: Double): Boolean actual val isGood: Boolean get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + actual abstract var status: Int + } \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_common_dep(stdlib)/common.kt b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_common_dep(stdlib)/common.kt new file mode 100644 index 00000000000..38a0994b32e --- /dev/null +++ b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_common_dep(stdlib)/common.kt @@ -0,0 +1,7 @@ +// "Create actual class for module proj_JVM (JVM)" "true" + +import kotlin.random.Random + +expect abstract class MyGenerator { + abstract fun generate(): Random +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_common_dep(stdlib)/common.kt.after b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_common_dep(stdlib)/common.kt.after new file mode 100644 index 00000000000..1e4f5685439 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_common_dep(stdlib)/common.kt.after @@ -0,0 +1,7 @@ +// "Create actual class for module proj_JVM (JVM)" "true" + +import kotlin.random.Random + +expect abstract class MyGenerator { + abstract fun generate(): Random +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_jvm_dep(stdlib)/MyGenerator.kt b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_jvm_dep(stdlib)/MyGenerator.kt new file mode 100644 index 00000000000..e0c26053d3e --- /dev/null +++ b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_jvm_dep(stdlib)/MyGenerator.kt @@ -0,0 +1 @@ +// MyGenerator: to be implemented \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_jvm_dep(stdlib)/MyGenerator.kt.after b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_jvm_dep(stdlib)/MyGenerator.kt.after new file mode 100644 index 00000000000..2f16c172dfe --- /dev/null +++ b/idea/testData/multiModuleQuickFix/abstractClassWithJdk/proj_jvm_dep(stdlib)/MyGenerator.kt.after @@ -0,0 +1,6 @@ +import kotlin.random.Random + +// MyGenerator: to be implemented +actual abstract class MyGenerator { + actual abstract fun generate(): Random +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/interface/jvm/Interface.kt.after b/idea/testData/multiModuleQuickFix/interface/jvm/Interface.kt.after index f54cd84c686..e28505ad7df 100644 --- a/idea/testData/multiModuleQuickFix/interface/jvm/Interface.kt.after +++ b/idea/testData/multiModuleQuickFix/interface/jvm/Interface.kt.after @@ -1,11 +1,8 @@ // Interface: to be implemented actual interface Interface { actual fun foo(param: String): Int - actual fun String.bar(y: Double): Boolean - actual val isGood: Boolean - actual var status: Int actual class Nested { diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java index d18419f0f28..ce2e86aeb3d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java @@ -30,6 +30,11 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul runTest("idea/testData/multiModuleQuickFix/abstract/"); } + @TestMetadata("abstractClassWithJdk") + public void testAbstractClassWithJdk() throws Exception { + runTest("idea/testData/multiModuleQuickFix/abstractClassWithJdk/"); + } + @TestMetadata("actualImplementAsConstructorParam") public void testActualImplementAsConstructorParam() throws Exception { runTest("idea/testData/multiModuleQuickFix/actualImplementAsConstructorParam/");