Skip to content

Commit

Permalink
Add "sealed" keyword to sealed interface signatures
Browse files Browse the repository at this point in the history
Fixes #2994
  • Loading branch information
IgnatBeresnev committed May 19, 2023
1 parent d9c8c6c commit 74b2a84
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 38 deletions.
18 changes: 10 additions & 8 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -1100,16 +1100,17 @@ public final class org/jetbrains/dokka/model/DFunction : org/jetbrains/dokka/mod
public fun withNewExtras (Lorg/jetbrains/dokka/model/properties/PropertyContainer;)Lorg/jetbrains/dokka/model/DFunction;
}

public final class org/jetbrains/dokka/model/DInterface : org/jetbrains/dokka/model/DClasslike, org/jetbrains/dokka/model/WithCompanion, org/jetbrains/dokka/model/WithGenerics, org/jetbrains/dokka/model/WithSupertypes, org/jetbrains/dokka/model/properties/WithExtraProperties {
public fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;)V
public synthetic fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final class org/jetbrains/dokka/model/DInterface : org/jetbrains/dokka/model/DClasslike, org/jetbrains/dokka/model/WithAbstraction, org/jetbrains/dokka/model/WithCompanion, org/jetbrains/dokka/model/WithGenerics, org/jetbrains/dokka/model/WithSupertypes, org/jetbrains/dokka/model/properties/WithExtraProperties {
public fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;)V
public synthetic fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lorg/jetbrains/dokka/links/DRI;
public final fun component10 ()Lorg/jetbrains/dokka/model/DObject;
public final fun component11 ()Ljava/util/List;
public final fun component12 ()Ljava/util/Map;
public final fun component13 ()Ljava/util/Set;
public final fun component14 ()Z
public final fun component15 ()Lorg/jetbrains/dokka/model/properties/PropertyContainer;
public final fun component13 ()Ljava/util/Map;
public final fun component14 ()Ljava/util/Set;
public final fun component15 ()Z
public final fun component16 ()Lorg/jetbrains/dokka/model/properties/PropertyContainer;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/util/Map;
public final fun component4 ()Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;
Expand All @@ -1118,8 +1119,8 @@ public final class org/jetbrains/dokka/model/DInterface : org/jetbrains/dokka/mo
public final fun component7 ()Ljava/util/List;
public final fun component8 ()Ljava/util/List;
public final fun component9 ()Ljava/util/Map;
public final fun copy (Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;)Lorg/jetbrains/dokka/model/DInterface;
public static synthetic fun copy$default (Lorg/jetbrains/dokka/model/DInterface;Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)Lorg/jetbrains/dokka/model/DInterface;
public final fun copy (Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;)Lorg/jetbrains/dokka/model/DInterface;
public static synthetic fun copy$default (Lorg/jetbrains/dokka/model/DInterface;Lorg/jetbrains/dokka/links/DRI;Ljava/lang/String;Ljava/util/Map;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/util/Map;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;Lorg/jetbrains/dokka/model/DObject;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;Ljava/util/Set;ZLorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)Lorg/jetbrains/dokka/model/DInterface;
public fun equals (Ljava/lang/Object;)Z
public fun getChildren ()Ljava/util/List;
public fun getClasslikes ()Ljava/util/List;
Expand All @@ -1130,6 +1131,7 @@ public final class org/jetbrains/dokka/model/DInterface : org/jetbrains/dokka/mo
public fun getExtra ()Lorg/jetbrains/dokka/model/properties/PropertyContainer;
public fun getFunctions ()Ljava/util/List;
public fun getGenerics ()Ljava/util/List;
public fun getModifier ()Ljava/util/Map;
public fun getName ()Ljava/lang/String;
public fun getProperties ()Ljava/util/List;
public fun getSourceSets ()Ljava/util/Set;
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/kotlin/model/Documentable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,12 @@ data class DInterface(
override val visibility: SourceSetDependent<Visibility>,
override val companion: DObject?,
override val generics: List<DTypeParameter>,
override val modifier: SourceSetDependent<Modifier>,
override val supertypes: SourceSetDependent<List<TypeConstructorWithKind>>,
override val sourceSets: Set<DokkaSourceSet>,
override val isExpectActual: Boolean,
override val extra: PropertyContainer<DInterface> = PropertyContainer.empty()
) : DClasslike(), WithCompanion, WithGenerics, WithSupertypes, WithExtraProperties<DInterface> {
) : DClasslike(), WithCompanion, WithGenerics, WithAbstraction, WithSupertypes, WithExtraProperties<DInterface> {
override val children: List<Documentable>
get() = (functions + properties + classlikes)

Expand Down Expand Up @@ -519,4 +520,4 @@ interface DocumentableSource {
val path: String
}

data class TypeConstructorWithKind(val typeConstructor: TypeConstructor, val kind: ClassKind)
data class TypeConstructorWithKind(val typeConstructor: TypeConstructor, val kind: ClassKind)
46 changes: 28 additions & 18 deletions plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -148,18 +148,8 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
annotationsBlock(c)
c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") }
if (c.isExpectActual) keyword(if (sourceSet == c.expectPresentInSet) "expect " else "actual ")
if (c is DClass) {
val modifier =
if (c.modifier[sourceSet] !in ignoredModifiers) {
when {
c.extra[AdditionalModifiers]?.content?.get(sourceSet)?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> ""
c.modifier[sourceSet] is JavaModifier.Empty -> "${KotlinModifier.Open.name} "
else -> c.modifier[sourceSet]?.name?.let { "$it " }
}
} else {
null
}
modifier?.takeIf { it.isNotEmpty() }?.let { keyword(it) }
if (c is WithAbstraction) {
modifier(c, sourceSet)
}
when (c) {
is DClass -> {
Expand Down Expand Up @@ -257,9 +247,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
annotationsBlock(p)
p.visibility[sourceSet].takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") }
if (p.isExpectActual) keyword(if (sourceSet == p.expectPresentInSet) "expect " else "actual ")
p.modifier[sourceSet].takeIf { it !in ignoredModifiers }?.let {
if (it is JavaModifier.Empty) KotlinModifier.Open else it
}?.name?.let { keyword("$it ") }
modifier(p, sourceSet)
p.modifiers()[sourceSet]?.toSignatureString()?.let { keyword(it) }
if (p.isMutable()) keyword("var ") else keyword("val ")
list(p.generics, prefix = "<", suffix = "> ",
Expand Down Expand Up @@ -312,9 +300,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
if (f.isConstructor) {
keyword("constructor")
} else {
f.modifier[sourceSet]?.takeIf { it !in ignoredModifiers }?.let {
if (it is JavaModifier.Empty) KotlinModifier.Open else it
}?.name?.let { keyword("$it ") }
modifier(f, sourceSet)
f.modifiers()[sourceSet]?.toSignatureString()?.let { keyword(it) }
keyword("fun ")
list(
Expand Down Expand Up @@ -352,6 +338,30 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
}
}

private fun <T> PageContentBuilder.DocumentableContentBuilder.modifier(
documentable: T,
sourceSet: DokkaSourceSet
) where T : Documentable, T : WithAbstraction {

val modifier = documentable.modifier[sourceSet] ?: return
if (modifier in ignoredModifiers || documentable.isDataClass(sourceSet)) {
return
}

val modifierText = when {
modifier is JavaModifier.Empty && documentable !is DInterface-> "${KotlinModifier.Open.name} "
else -> modifier.name.let { "$it " }
}
modifierText.takeIf { it.isNotBlank() }?.let { keyword(it) }
}

private fun Documentable.isDataClass(sourceSet: DokkaSourceSet): Boolean {
return (this as? DClass)
?.extra?.get(AdditionalModifiers)
?.content?.get(sourceSet)
?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true
}

private fun DFunction.documentReturnType() = when {
this.isConstructor -> false
this.type is TypeConstructor && (this.type as TypeConstructor).dri == DriOfUnit -> false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ private class DokkaDescriptorVisitor(
visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(),
supertypes = info.supertypes.toSourceSetDependent(),
documentation = info.docs,
modifier = descriptor.modifier().toSourceSetDependent(),
generics = generics.await(),
companion = descriptor.companion(driWithPlatform),
sourceSets = setOf(sourceSet),
Expand Down Expand Up @@ -1063,12 +1064,23 @@ private class DokkaDescriptorVisitor(
objectDescriptor(it, dri)
}

private fun MemberDescriptor.modifier() = when (modality) {
Modality.FINAL -> KotlinModifier.Final
Modality.SEALED -> KotlinModifier.Sealed
Modality.OPEN -> KotlinModifier.Open
Modality.ABSTRACT -> KotlinModifier.Abstract
else -> KotlinModifier.Empty
private fun MemberDescriptor.modifier(): KotlinModifier {
val isInterface = this is ClassDescriptor && this.kind == ClassKind.INTERFACE
return if (isInterface) {
when (modality) {
// modifiers other than "sealed" are redundant for interfaces
Modality.SEALED -> KotlinModifier.Sealed
else -> KotlinModifier.Empty
}
} else {
when (modality) {
Modality.FINAL -> KotlinModifier.Final
Modality.SEALED -> KotlinModifier.Sealed
Modality.OPEN -> KotlinModifier.Open
Modality.ABSTRACT -> KotlinModifier.Abstract
else -> KotlinModifier.Empty
}
}
}

private fun MemberDescriptor.createSources(): SourceSetDependent<DocumentableSource> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ class DefaultPsiToDocumentableTranslator(
visibility = visibility,
companion = null,
generics = mapTypeParameters(dri),
modifier = modifiers,
supertypes = ancestors,
sourceSets = setOf(sourceSetData),
isExpectActual = false,
Expand Down Expand Up @@ -633,10 +634,16 @@ class DefaultPsiToDocumentableTranslator(
else -> getBound(type)
}

private fun PsiModifierListOwner.getModifier() = when {
hasModifier(JvmModifier.ABSTRACT) -> JavaModifier.Abstract
hasModifier(JvmModifier.FINAL) -> JavaModifier.Final
else -> JavaModifier.Empty
private fun PsiModifierListOwner.getModifier(): JavaModifier {
if (this is PsiClass && this.isInterface) {
return JavaModifier.Empty // all Java modifiers are redundant for interfaces
}

return when {
hasModifier(JvmModifier.ABSTRACT) -> JavaModifier.Abstract
hasModifier(JvmModifier.FINAL) -> JavaModifier.Final
else -> JavaModifier.Empty
}
}

private fun PsiTypeParameterListOwner.mapTypeParameters(dri: DRI): List<DTypeParameter> {
Expand Down
68 changes: 68 additions & 0 deletions plugins/base/src/test/kotlin/signatures/SignatureTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -991,4 +991,72 @@ class SignatureTest : BaseAbstractTest() {
}
}
}

@Test
fun `should add sealed keyword for sealed interfaces`() {
val writerPlugin = TestOutputWriterPlugin()
testInline(
"""
|/src/main/kotlin/example/SealedInterface.kt
|package example
|
|sealed interface SealedInterface {}
""".trimIndent(),
configuration,
pluginOverrides = listOf(writerPlugin)
) {
renderingStage = { _, _ ->
val sealedInterfacePage = writerPlugin.writer.renderedContent("root/example/-sealed-interface/index.html")

val signatures = sealedInterfacePage.signature().toList()
assertEquals(1, signatures.size, "Expected the page to have a single signature for SealedInterface")

signatures[0].match(
"sealed interface ", A("SealedInterface"),
ignoreSpanWithTokenStyle = true
)
}
}
}

@Test
fun `should add no additional keywords for plain interfaces`() {
val writerPlugin = TestOutputWriterPlugin()
testInline(
"""
|/src/main/kotlin/example/PlainKotlinInterface.kt
|package example
|
|interface PlainKotlinInterface {}
|
|/src/main/java/example/PlainJavaInterface.java
|package example;
|public interface PlainJavaInterface {}
""".trimIndent(),
configuration,
pluginOverrides = listOf(writerPlugin)
) {
renderingStage = { _, _ ->
writerPlugin.writer.renderedContent("root/example/-plain-kotlin-interface/index.html").let { ktInterface ->
val signatures = ktInterface.signature().toList()
assertEquals(1, signatures.size, "Expected the page to have a single signature for SealedInterface")

signatures[0].match(
"interface ", A("PlainKotlinInterface"),
ignoreSpanWithTokenStyle = true
)
}

writerPlugin.writer.renderedContent("root/example/-plain-java-interface/index.html").let { javaInterface ->
val signatures = javaInterface.signature().toList()
assertEquals(1, signatures.size, "Expected the page to have a single signature for SealedInterface")

signatures[0].match(
"interface ", A("PlainJavaInterface"),
ignoreSpanWithTokenStyle = true
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,50 @@ val soapXml = node("soap-env:Envelope", soapAttrs,
}
}
}

@Test
fun `should add sealed modifier to sealed interfaces`() {
testInline(
"""
|/src/main/kotlin/test/SealedInterface.kt
|package test
|
|sealed interface SealedInterface {
| fun foo(): String
|}
""".trimIndent(),
configuration
) {
documentablesMergingStage = { module ->
val sealedInterface = module.findClasslike("test", "SealedInterface") as DInterface

val modifier = sealedInterface.modifier.values.single()
assertEquals(KotlinModifier.Sealed, modifier)
}
}
}

@Test
fun `should add no redundant modifiers to plain interfaces`() {
testInline(
"""
|/src/main/kotlin/test/PlainInterface.kt
|package test
|
|interface PlainInterface {
| fun foo(): String
|}
""".trimIndent(),
configuration
) {
documentablesMergingStage = { module ->
val plainInterface = module.findClasslike("test", "PlainInterface") as DInterface

val modifier = plainInterface.modifier.values.single()
assertEquals(KotlinModifier.Empty, modifier)
}
}
}
}

private sealed class TestSuite {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,25 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() {
}
}
}

@Test
fun `should add no redundant modifiers for interfaces`() {
testInline(
"""
|/src/main/java/test/JavaInterface.java
|package test;
|interface JavaInterface {}
""".trimIndent(),
configuration
) {
documentablesMergingStage = { module ->
val javaInterface = module.findClasslike(packageName = "test", "JavaInterface") as DInterface

val modifier = javaInterface.modifier.values.single()
assertEquals(JavaModifier.Empty, modifier)
}
}
}
}

private fun DFunction.visibility() = visibility.values.first()

0 comments on commit 74b2a84

Please sign in to comment.