From 6f14e29c32718053e1301d4fdff7388877a89145 Mon Sep 17 00:00:00 2001 From: Marcin Aman Date: Fri, 11 Dec 2020 16:55:50 +0100 Subject: [PATCH] Parsing of JvmName --- .../src/main/kotlin/model/additionalExtras.kt | 29 ++- core/src/main/kotlin/model/jvmName.kt | 7 + .../kotlin/signatures/JvmSignatureUtils.kt | 4 +- .../transformers/documentables/utils.kt | 2 +- .../annotations/SinceKotlinTransformer.kt | 2 +- ...faultDescriptorToDocumentableTranslator.kt | 52 ++++-- .../annotations/ContentForAnnotationsTest.kt | 47 +++++ .../annotations/FileLevelJvmNameTest.kt | 111 ++++++++++++ .../base/src/test/kotlin/model/ClassesTest.kt | 8 +- .../src/test/kotlin/model/FunctionsTest.kt | 12 +- .../base/src/test/kotlin/model/JavaTest.kt | 2 +- .../src/test/kotlin/model/PropertyTest.kt | 4 +- .../src/main/kotlin/KotlinAsJavaPlugin.kt | 12 +- .../converters/KotlinToJavaConverter.kt | 171 +++++++++--------- .../kotlin-as-java/src/main/kotlin/jvmName.kt | 19 ++ .../signatures/JavaSignatureProvider.kt | 2 +- .../JvmNameDocumentableTransformer.kt | 100 ++++++++++ .../kotlin/transformers/JvmNameProvider.kt | 28 +++ .../KotlinAsJavaDocumentableTransformer.kt | 2 +- .../src/test/kotlin/JvmNameTest.kt | 158 ++++++++++++++++ 20 files changed, 648 insertions(+), 124 deletions(-) create mode 100644 core/src/main/kotlin/model/jvmName.kt create mode 100644 plugins/base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt create mode 100644 plugins/kotlin-as-java/src/main/kotlin/jvmName.kt create mode 100644 plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameDocumentableTransformer.kt create mode 100644 plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt create mode 100644 plugins/kotlin-as-java/src/test/kotlin/JvmNameTest.kt diff --git a/core/src/main/kotlin/model/additionalExtras.kt b/core/src/main/kotlin/model/additionalExtras.kt index 94d0e751d1..0a59ec3617 100644 --- a/core/src/main/kotlin/model/additionalExtras.kt +++ b/core/src/main/kotlin/model/additionalExtras.kt @@ -21,7 +21,10 @@ class AdditionalModifiers(val content: SourceSetDependent>) fun SourceSetDependent>.toAdditionalModifiers() = AdditionalModifiers(this) -class Annotations(val content: SourceSetDependent>) : ExtraProperty { +class Annotations( + @Deprecated("Use directAnnotations or fileLevelAnnotations") + val content: SourceSetDependent> +) : ExtraProperty { companion object : ExtraProperty.Key { override fun mergeStrategyFor(left: Annotations, right: Annotations): MergeStrategy = MergeStrategy.Replace(Annotations(left.content + right.content)) @@ -29,7 +32,12 @@ class Annotations(val content: SourceSetDependent>) : ExtraProp override val key: ExtraProperty.Key = Annotations - data class Annotation(val dri: DRI, val params: Map, val mustBeDocumented: Boolean = false) { + data class Annotation( + val dri: DRI, + val params: Map, + val mustBeDocumented: Boolean = false, + val scope: AnnotationScope = AnnotationScope.DIRECT + ) { override fun equals(other: Any?): Boolean = when (other) { is Annotation -> dri == other.dri else -> false @@ -37,6 +45,21 @@ class Annotations(val content: SourceSetDependent>) : ExtraProp override fun hashCode(): Int = dri.hashCode() } + + val directAnnotations: SourceSetDependent> = annotationsByScope(AnnotationScope.DIRECT) + + val fileLevelAnnotations: SourceSetDependent> = annotationsByScope(AnnotationScope.FILE) + + private fun annotationsByScope(scope: AnnotationScope): SourceSetDependent> = + content.entries.mapNotNull { (key, value) -> + val withoutFileLevel = value.filter { it.scope == scope } + if (withoutFileLevel.isEmpty()) null + else Pair(key, withoutFileLevel) + }.toMap() +} + +enum class AnnotationScope { + DIRECT, FILE } fun SourceSetDependent>.toAnnotations() = Annotations(this) @@ -65,7 +88,7 @@ data class ActualTypealias(val underlyingType: SourceSetDependent) : Extr override val key: ExtraProperty.Key = ActualTypealias } -data class ConstructorValues(val values: SourceSetDependent>) : ExtraProperty{ +data class ConstructorValues(val values: SourceSetDependent>) : ExtraProperty { companion object : ExtraProperty.Key { override fun mergeStrategyFor(left: ConstructorValues, right: ConstructorValues) = MergeStrategy.Replace(ConstructorValues(left.values + right.values)) diff --git a/core/src/main/kotlin/model/jvmName.kt b/core/src/main/kotlin/model/jvmName.kt new file mode 100644 index 0000000000..a9f23bf0ff --- /dev/null +++ b/core/src/main/kotlin/model/jvmName.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.links.DRI + +fun DRI.isJvmName(): Boolean = packageName == "kotlin.jvm" && classNames == "JvmName" + +fun Annotations.Annotation.isJvmName(): Boolean = dri.isJvmName() diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt index f934faa5d6..3851b39c45 100644 --- a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt +++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt @@ -21,7 +21,7 @@ interface JvmSignatureUtils { joinToString("") { it.name.toLowerCase() + " " } fun WithExtraProperties.annotations(): SourceSetDependent> = - extra[Annotations]?.content ?: emptyMap() + extra[Annotations]?.directAnnotations ?: emptyMap() private fun PageContentBuilder.DocumentableContentBuilder.annotations( d: Documentable, @@ -131,7 +131,7 @@ interface JvmSignatureUtils { } fun WithExtraProperties.stylesIfDeprecated(sourceSetData: DokkaSourceSet): Set = - if (extra[Annotations]?.content?.get(sourceSetData)?.any { + if (extra[Annotations]?.directAnnotations?.get(sourceSetData)?.any { it.dri == DRI("kotlin", "Deprecated") || it.dri == DRI("java.lang", "Deprecated") } == true) setOf(TextStyle.Strikethrough) else emptySet() diff --git a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt b/plugins/base/src/main/kotlin/transformers/documentables/utils.kt index ecf75a1f9f..961d2aca23 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/utils.kt @@ -11,7 +11,7 @@ fun T.isDeprecated() where T : WithExtraProperties = val T.deprecatedAnnotation where T : WithExtraProperties get() = extra[Annotations]?.let { annotations -> - annotations.content.values.flatten().firstOrNull { + annotations.directAnnotations.values.flatten().firstOrNull { it.dri.toString() == "kotlin/Deprecated///PointingToDeclaration/" || it.dri.toString() == "java.lang/Deprecated///PointingToDeclaration/" } diff --git a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt index acff5adad8..f437ebe3aa 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt @@ -69,7 +69,7 @@ class SinceKotlinTransformer(val context: DokkaContext) : DocumentableTransforme private fun Documentable.appendSinceKotlin() = sourceSets.fold(documentation) { acc, sourceSet -> - safeAs>()?.extra?.get(Annotations)?.content?.get(sourceSet)?.find { + safeAs>()?.extra?.get(Annotations)?.directAnnotations?.get(sourceSet)?.find { it.dri == DRI("kotlin", "SinceKotlin") }?.params?.get("version").safeAs()?.value?.let { version -> acc.mapValues { diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index 642f991f5a..46b61f1acb 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -19,10 +19,8 @@ import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.query import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator -import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType @@ -33,7 +31,6 @@ import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype import org.jetbrains.kotlin.codegen.isJvmStaticInObjectOrClassOrInterface import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.idea.kdoc.findKDoc @@ -51,8 +48,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import org.jetbrains.kotlin.resolve.source.PsiSourceFile import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.model.typeConstructor import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull @@ -117,7 +114,7 @@ private class DokkaDescriptorVisitor( } } - private fun T.toSourceSetDependent() = mapOf(sourceSet to this) + private fun T.toSourceSetDependent() = if(this != null) mapOf(sourceSet to this) else emptyMap() suspend fun visitPackageFragmentDescriptor( descriptor: PackageFragmentDescriptor, @@ -436,13 +433,16 @@ private class DokkaDescriptorVisitor( sourceSets = setOf(sourceSet), generics = generics.await(), isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll(listOfNotNull( - (descriptor.additionalExtras() + descriptor.getAnnotationsWithBackingField() - .toAdditionalExtras()).toSet().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations(), - descriptor.getDefaultValue()?.let { DefaultValue(it) }, - InheritedMember(inheritedFrom.toSourceSetDependent()), - )) + extra = PropertyContainer.withAll( + listOfNotNull( + (descriptor.additionalExtras() + descriptor.getAnnotationsWithBackingField() + .toAdditionalExtras()).toSet().toSourceSetDependent().toAdditionalModifiers(), + (descriptor.getAnnotationsWithBackingField() + descriptor.fileLevelAnnotations()).toSourceSetDependent() + .toAnnotations(), + descriptor.getDefaultValue()?.let { DefaultValue(it) }, + InheritedMember(inheritedFrom.toSourceSetDependent()), + ) + ) ) } } @@ -489,7 +489,7 @@ private class DokkaDescriptorVisitor( extra = PropertyContainer.withAll( InheritedMember(inheritedFrom.toSourceSetDependent()), descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + (descriptor.getAnnotations() + descriptor.fileLevelAnnotations()).toSourceSetDependent().toAnnotations(), ) ) } @@ -928,19 +928,31 @@ private class DokkaDescriptorVisitor( ) } } - else -> StringValue(toString()) + else -> StringValue(unquotedValue(toString())) } - private suspend fun AnnotationDescriptor.toAnnotation(): Annotations.Annotation { + private fun unquotedValue(value: String): String = if (value.startsWith('"') && value.endsWith('"')) { + if (value.length == 2) "" else value.substring(1, value.lastIndex) + } else { + value + } + + private suspend fun AnnotationDescriptor.toAnnotation(scope: AnnotationScope = AnnotationScope.DIRECT): Annotations.Annotation { + val dri = DRI.from(annotationClass as DeclarationDescriptor) return Annotations.Annotation( DRI.from(annotationClass as DeclarationDescriptor), allValueArguments.map { it.key.asString() to it.value.toValue() }.filter { it.second != null }.toMap() as Map, - annotationClass!!.annotations.hasAnnotation(FqName("kotlin.annotation.MustBeDocumented")) + mustBeDocumented(dri), + scope ) } + private fun AnnotationDescriptor.mustBeDocumented(dri: DRI): Boolean = + if (dri.isJvmName()) false + else annotationClass!!.annotations.hasAnnotation(FqName("kotlin.annotation.MustBeDocumented")) + private suspend fun PropertyDescriptor.getAnnotationsWithBackingField(): List = getAnnotations() + (backingField?.getAnnotations() ?: emptyList()) @@ -1004,6 +1016,14 @@ private class DokkaDescriptorVisitor( private fun ConstantsEnumValue.fullEnumEntryName() = "${this.enumClassId.relativeClassName.asString()}.${this.enumEntryName.identifier}" + + private fun DeclarationDescriptorWithSource.ktFile(): KtFile? = (source.containingFile as? PsiSourceFile)?.psiFile as? KtFile + + private suspend fun DeclarationDescriptorWithSource.fileLevelAnnotations() = ktFile() + ?.let { file -> resolutionFacade.resolveSession.getFileAnnotations(file) } + ?.toList() + .orEmpty() + .parallelMap { it.toAnnotation(scope = AnnotationScope.FILE) } } private data class AncestryLevel( diff --git a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt index b90c7350ee..815e28f157 100644 --- a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt +++ b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt @@ -4,10 +4,17 @@ import matchers.content.* import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.pages.PackagePageNode import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.AnnotationScope +import org.jetbrains.dokka.model.Annotations +import org.jetbrains.dokka.model.StringValue +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.junit.jupiter.api.Test import utils.ParamAttributes import utils.bareSignature import utils.propertySignature +import kotlin.test.assertEquals +import kotlin.test.assertFalse class ContentForAnnotationsTest : BaseAbstractTest() { @@ -18,6 +25,7 @@ class ContentForAnnotationsTest : BaseAbstractTest() { sourceSet { sourceRoots = listOf("src/") analysisPlatform = "jvm" + classpath += jvmStdlibPath!! } } } @@ -218,4 +226,43 @@ class ContentForAnnotationsTest : BaseAbstractTest() { } } } + + @Test + fun `JvmName for property with setter and getter`(){ + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + |@get:JvmName("xd") + |@set:JvmName("asd") + |var property: String + | get() = "" + | set(value) {} + """.trimIndent(), testConfiguration) { + documentablesCreationStage = { modules -> + fun expectedAnnotation(name: String) = Annotations.Annotation( + dri = DRI("kotlin.jvm", "JvmName"), + params = mapOf("name" to StringValue(name)), + scope = AnnotationScope.DIRECT, + mustBeDocumented = false + ) + + val property = modules.flatMap { it.packages }.flatMap { it.properties }.first() + val getterAnnotation = property.getter?.extra?.get(Annotations)?.let { + it.directAnnotations.entries.firstNotNullResult { (_, annotations) -> annotations.firstOrNull() } + } + val setterAnnotation = property.getter?.extra?.get(Annotations)?.let { + it.directAnnotations.entries.firstNotNullResult { (_, annotations) -> annotations.firstOrNull() } + } + + assertEquals(expectedAnnotation("xd"), getterAnnotation) + assertFalse(getterAnnotation?.mustBeDocumented!!) + assertEquals(AnnotationScope.DIRECT, getterAnnotation.scope) + + assertEquals(expectedAnnotation("asd"), setterAnnotation) + assertFalse(setterAnnotation?.mustBeDocumented!!) + assertEquals(AnnotationScope.DIRECT, setterAnnotation.scope) + } + } + } } diff --git a/plugins/base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt b/plugins/base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt new file mode 100644 index 0000000000..f4b2ef54a4 --- /dev/null +++ b/plugins/base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt @@ -0,0 +1,111 @@ +package content.annotations + +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.AnnotationScope +import org.jetbrains.dokka.model.Annotations +import org.jetbrains.dokka.model.StringValue +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import kotlin.test.assertEquals + +class FileLevelJvmNameTest : BaseAbstractTest() { + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + classpath += jvmStdlibPath!! + } + } + } + + companion object { + private const val functionTest = + """ + |/src/main/kotlin/test/source.kt + |@file:JvmName("CustomJvmName") + |package test + | + |fun function(abc: String): String { + | return "Hello, " + abc + |} + """ + + private const val extensionFunctionTest = + """ + |/src/main/kotlin/test/source.kt + |@file:JvmName("CustomJvmName") + |package test + | + |fun String.function(abc: String): String { + | return "Hello, " + abc + |} + """ + + private const val propertyTest = + """ + |/src/main/kotlin/test/source.kt + |@file:JvmName("CustomJvmName") + |package test + | + |val property: String + | get() = "" + """ + + private const val extensionPropertyTest = + """ + |/src/main/kotlin/test/source.kt + |@file:JvmName("CustomJvmName") + |package test + | + |val String.property: String + | get() = "" + """ + } + + @ParameterizedTest + @ValueSource(strings = [functionTest, extensionFunctionTest]) + fun `jvm name should be included in functions extra`(query: String){ + testInline( + query.trimIndent(), testConfiguration) { + documentablesCreationStage = { modules -> + val expectedAnnotation = Annotations.Annotation( + dri = DRI("kotlin.jvm", "JvmName"), + params = mapOf("name" to StringValue("CustomJvmName")), + scope = AnnotationScope.FILE, + mustBeDocumented = false + ) + val function = modules.flatMap { it.packages }.first().functions.first() + val annotation = function.extra[Annotations]?.fileLevelAnnotations?.entries?.first()?.value?.single() + assertEquals(emptyMap(), function.extra[Annotations]?.directAnnotations) + assertEquals(expectedAnnotation, annotation) + assertEquals(expectedAnnotation.scope, annotation?.scope) + assertEquals(expectedAnnotation.mustBeDocumented, annotation?.mustBeDocumented) + } + } + } + + @ParameterizedTest + @ValueSource(strings = [propertyTest, extensionPropertyTest]) + fun `jvm name should be included in properties extra`(query: String){ + testInline( + query.trimIndent(), testConfiguration) { + documentablesCreationStage = { modules -> + val expectedAnnotation = Annotations.Annotation( + dri = DRI("kotlin.jvm", "JvmName"), + params = mapOf("name" to StringValue("CustomJvmName")), + scope = AnnotationScope.FILE, + mustBeDocumented = false + ) + val properties = modules.flatMap { it.packages }.first().properties.first() + val annotation = properties.extra[Annotations]?.fileLevelAnnotations?.entries?.first()?.value?.single() + assertEquals(emptyMap(), properties.extra[Annotations]?.directAnnotations) + assertEquals(expectedAnnotation, annotation) + assertEquals(expectedAnnotation.scope, annotation?.scope) + assertEquals(expectedAnnotation.mustBeDocumented, annotation?.mustBeDocumented) + } + } + } +} \ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt index b6787126ff..ef912e707e 100644 --- a/plugins/base/src/test/kotlin/model/ClassesTest.kt +++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt @@ -182,7 +182,7 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class """ ) { with((this / "classes" / "Foo").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "Deprecated" @@ -361,7 +361,7 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class """ ) { with((this / "classes" / "C").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "SinceKotlin" @@ -426,7 +426,7 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class """@Suppress("abc") class Foo() {}""" ) { with((this / "classes" / "Foo").cast()) { - with(extra[Annotations]?.content?.values?.firstOrNull()?.firstOrNull().assertNotNull("annotations")) { + with(extra[Annotations]?.directAnnotations?.values?.firstOrNull()?.firstOrNull().assertNotNull("annotations")) { dri.toString() equals "kotlin/Suppress///PointingToDeclaration/" (params["names"].assertNotNull("param") as ArrayValue).value equals listOf(StringValue("\"abc\"")) } @@ -446,7 +446,7 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class """ ) { with((this / "classes" / "throws").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "Retention" diff --git a/plugins/base/src/test/kotlin/model/FunctionsTest.kt b/plugins/base/src/test/kotlin/model/FunctionsTest.kt index 9fffb4fcf1..84e6c866e6 100644 --- a/plugins/base/src/test/kotlin/model/FunctionsTest.kt +++ b/plugins/base/src/test/kotlin/model/FunctionsTest.kt @@ -141,7 +141,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun """ ) { with((this / "function" / "f").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "Suppress" @@ -226,7 +226,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun """ ) { with((this / "function" / "Fancy").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 3 with(map { it.dri.classNames to it }.toMap()) { with(this["Target"].assertNotNull("Target")) { @@ -249,7 +249,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun } with((this / "function" / "function" / "notInlined").cast()) { - with(this.extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(this.extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "Fancy" @@ -296,7 +296,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun } } - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 3 with(map { it.dri.classNames to it }.toMap()) { with(this["Target"].assertNotNull("Target")) { @@ -319,7 +319,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun } with((this / "function" / "f").cast()) { - with(this.extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(this.extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(this.first()) { dri.classNames equals "Fancy" @@ -381,7 +381,7 @@ class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "fun """ ) { with((this / "function" / "f").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "SinceKotlin" diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt index 189272181b..beb7c8e472 100644 --- a/plugins/base/src/test/kotlin/model/JavaTest.kt +++ b/plugins/base/src/test/kotlin/model/JavaTest.kt @@ -274,7 +274,7 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { """ ) { with((this / "java" / "Attribute").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { with(single()) { dri.classNames equals "Target" (params["value"].assertNotNull("value") as ArrayValue).value equals listOf( diff --git a/plugins/base/src/test/kotlin/model/PropertyTest.kt b/plugins/base/src/test/kotlin/model/PropertyTest.kt index af952b4308..287f0c3e8d 100644 --- a/plugins/base/src/test/kotlin/model/PropertyTest.kt +++ b/plugins/base/src/test/kotlin/model/PropertyTest.kt @@ -150,7 +150,7 @@ class PropertyTest : AbstractModelTest("/src/main/kotlin/property/Test.kt", "pro """ ) { with((this / "property" / "prop").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "SinceKotlin" @@ -178,7 +178,7 @@ class PropertyTest : AbstractModelTest("/src/main/kotlin/property/Test.kt", "pro } ) { with((this / "property" / "property").cast()) { - with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(extra[Annotations]!!.directAnnotations.entries.single().value.assertNotNull("Annotations")) { this counts 1 with(first()) { dri.classNames equals "Strictfp" diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt index 2a9b857a31..578eb287c9 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt @@ -4,6 +4,7 @@ import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.DokkaBaseConfiguration import org.jetbrains.dokka.kotlinAsJava.signatures.JavaSignatureProvider +import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameDocumentableTransformer import org.jetbrains.dokka.kotlinAsJava.transformers.KotlinAsJavaDocumentableTransformer import org.jetbrains.dokka.kotlinAsJava.translators.KotlinAsJavaDocumentableToPageTranslator import org.jetbrains.dokka.plugability.DokkaPlugin @@ -13,13 +14,20 @@ class KotlinAsJavaPlugin : DokkaPlugin() { val kotlinAsJavaDocumentableTransformer by extending { CoreExtensions.documentableTransformer with KotlinAsJavaDocumentableTransformer() } + + val jvmNameTransformer by extending { + CoreExtensions.documentableTransformer with JvmNameDocumentableTransformer() order { + before(kotlinAsJavaDocumentableTransformer) + } + } + val javaSignatureProvider by extending { with(plugin()) { signatureProvider providing ::JavaSignatureProvider override kotlinSignatureProvider } } val kotlinAsJavaDocumentableToPageTranslator by extending { - CoreExtensions.documentableToPageTranslator providing ::KotlinAsJavaDocumentableToPageTranslator override - plugin().documentableToPageTranslator + CoreExtensions.documentableToPageTranslator providing ::KotlinAsJavaDocumentableToPageTranslator override + plugin().documentableToPageTranslator } } \ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt index 0f7d5c7ab5..01de516484 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt @@ -1,27 +1,18 @@ package org.jetbrains.dokka.kotlinAsJava.converters -import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameProvider import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.PointingToDeclaration +import org.jetbrains.dokka.links.withClass import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.DAnnotation -import org.jetbrains.dokka.model.DEnum -import org.jetbrains.dokka.model.DFunction -import org.jetbrains.dokka.model.Nullable -import org.jetbrains.dokka.model.TypeConstructor import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType -import java.lang.IllegalStateException -private fun List.groupedByLocation() = - map { it.sources to it } - .groupBy({ (location, _) -> - location.let { - it.entries.first().value.path.split("/").last().split(".").first() + "Kt" - } // TODO: first() does not look reasonable - }) { it.second } +val jvmNameProvider = JvmNameProvider() private val DProperty.isConst: Boolean get() = extra[AdditionalModifiers] @@ -31,38 +22,39 @@ private val DProperty.isConst: Boolean } == true internal fun DPackage.asJava(): DPackage { - @Suppress("UNCHECKED_CAST") - val syntheticClasses = ((properties + functions) as List) - .groupedByLocation() - .map { (syntheticClassName, nodes) -> - DClass( - dri = dri.withClass(syntheticClassName), - name = syntheticClassName, - properties = nodes.filterIsInstance().map { it.asJava(true) }, - constructors = emptyList(), - functions = ( - nodes - .filterIsInstance() - .filterNot { it.isConst } - .flatMap { it.javaAccessors() } + - nodes.filterIsInstance() - .map { it.asJava(syntheticClassName) }), // TODO: methods are static and receiver is a param - classlikes = emptyList(), - sources = emptyMap(), - expectPresentInSet = null, - visibility = sourceSets.map { - it to JavaVisibility.Public - }.toMap(), - companion = null, - generics = emptyList(), - supertypes = emptyMap(), - documentation = emptyMap(), - modifier = sourceSets.map { it to JavaModifier.Final }.toMap(), - sourceSets = sourceSets, - isExpectActual = false, - extra = PropertyContainer.empty() - ) - } + val syntheticClasses = + (properties.map { jvmNameProvider.nameForSyntheticClass(it) to it } + + functions.map { jvmNameProvider.nameForSyntheticClass(it) to it }) + .groupBy({ it.first }) { it.second } + .map { (syntheticClassName, nodes) -> + DClass( + dri = dri.withClass(syntheticClassName), + name = syntheticClassName, + properties = nodes.filterIsInstance().map { it.asJava(true) }, + constructors = emptyList(), + functions = ( + nodes + .filterIsInstance() + .filterNot { it.isConst } + .flatMap { it.javaAccessors(relocateToClass = syntheticClassName) } + + nodes.filterIsInstance() + .map { it.asJava(syntheticClassName) }), // TODO: methods are static and receiver is a param + classlikes = emptyList(), + sources = emptyMap(), + expectPresentInSet = null, + visibility = sourceSets.map { + it to JavaVisibility.Public + }.toMap(), + companion = null, + generics = emptyList(), + supertypes = emptyMap(), + documentation = emptyMap(), + modifier = sourceSets.map { it to JavaModifier.Final }.toMap(), + sourceSets = sourceSets, + isExpectActual = false, + extra = PropertyContainer.empty() + ) + } return copy( functions = emptyList(), @@ -110,41 +102,52 @@ internal fun DProperty.javaModifierFromSetter() = internal fun DProperty.javaAccessors(isTopLevel: Boolean = false, relocateToClass: String? = null): List = listOfNotNull( - getter?.copy( - dri = if (relocateToClass.isNullOrBlank()) { - dri + getter?.let { getter -> + getter.copy( + dri = if (relocateToClass.isNullOrBlank()) { + getter.dri + } else { + getter.dri.withClass(relocateToClass) + }, + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { JavaVisibility.Public }, + type = getter.type.asJava(), + extra = if (isTopLevel) getter.extra + + getter.extra.mergeAdditionalModifiers( + sourceSets.map { + it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) + }.toMap() + ) + else getter.extra + ) + }, + setter?.let { setter -> + val baseDRI = if (relocateToClass.isNullOrBlank()) { + setter.dri } else { - dri.withClass(relocateToClass) - }, - name = "get" + name.capitalize(), - modifier = javaModifierFromSetter(), - visibility = visibility.mapValues { JavaVisibility.Public }, - type = type.asJava(), // TODO: check - extra = if (isTopLevel) getter!!.extra + - getter!!.extra.mergeAdditionalModifiers( - sourceSets.map { - it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) - }.toMap() + setter.dri.withClass(relocateToClass) + } + setter.copy( + dri = baseDRI, + parameters = setter.parameters.map { + it.copy( + dri = baseDRI.copy( + target = it.dri.target, + extra = it.dri.extra + ), type = it.type.asJava() ) - else getter!!.extra - ), - setter?.copy( - dri = if (relocateToClass.isNullOrBlank()) { - dri - } else { - dri.withClass(relocateToClass) - }, - name = "set" + name.capitalize(), - modifier = javaModifierFromSetter(), - visibility = visibility.mapValues { JavaVisibility.Public }, - type = type.asJava(), // TODO: check - extra = if (isTopLevel) setter!!.extra + setter!!.extra.mergeAdditionalModifiers( - sourceSets.map { - it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) - }.toMap() + }, + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { JavaVisibility.Public }, + type = Void, + extra = if (isTopLevel) setter.extra + setter.extra.mergeAdditionalModifiers( + sourceSets.map { + it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) + }.toMap() + ) + else setter.extra ) - else setter!!.extra - ) + } ) @@ -154,7 +157,7 @@ internal fun DFunction.asJava(containingClassName: String): DFunction { else -> name } return copy( -// dri = dri.copy(callable = dri.callable?.asJava()), + dri = dri.copy(classNames = containingClassName, callable = dri.callable?.copy(name = newName)), name = newName, type = type.asJava(), modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Final } && isConstructor) @@ -193,7 +196,7 @@ private fun DTypeParameter.asJava(): DTypeParameter = copy( bounds = bounds.map { it.asJava() } ) -private fun Projection.asJava(): Projection = when(this) { +private fun Projection.asJava(): Projection = when (this) { is Star -> Star is Covariance<*> -> copy(inner.asJava()) is Contravariance<*> -> copy(inner.asJava()) @@ -201,7 +204,7 @@ private fun Projection.asJava(): Projection = when(this) { is Bound -> asJava() } -private fun Bound.asJava(): Bound = when(this) { +private fun Bound.asJava(): Bound = when (this) { is TypeParameter -> copy(dri.possiblyAsJava()) is GenericTypeConstructor -> copy( dri = dri.possiblyAsJava(), @@ -286,7 +289,7 @@ internal fun DParameter.asJava(): DParameter = copy( ) internal fun Visibility.propertyVisibilityAsJava(): Visibility = - if(this is JavaVisibility) this + if (this is JavaVisibility) this else JavaVisibility.Private internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values() @@ -295,7 +298,7 @@ internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.b private fun DRI.partialFqName() = packageName?.let { "$it." } + classNames private fun DRI.possiblyAsJava() = this.partialFqName().mapToJava()?.toDRI(this) ?: this -private fun TypeConstructor.possiblyAsJava() = when(this) { +private fun TypeConstructor.possiblyAsJava() = when (this) { is GenericTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) is FunctionalTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) } @@ -318,7 +321,7 @@ internal fun TypeConstructorWithKind.asJava(): TypeConstructorWithKind = ) internal fun ClassKind.asJava(): ClassKind { - return when(this){ + return when (this) { is JavaClassKindTypes -> this KotlinClassKindTypes.CLASS -> JavaClassKindTypes.CLASS KotlinClassKindTypes.INTERFACE -> JavaClassKindTypes.INTERFACE diff --git a/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt b/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt new file mode 100644 index 0000000000..c81210d610 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt @@ -0,0 +1,19 @@ +package org.jetbrains.dokka.kotlinAsJava + +import org.jetbrains.dokka.model.Annotations +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.StringValue +import org.jetbrains.dokka.model.isJvmName +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult + +internal fun WithExtraProperties.directlyAnnotatedJvmName(): Annotations.Annotation? = + extra[Annotations]?.directAnnotations?.entries?.firstNotNullResult { (_, annotations) -> annotations.jvmNameAnnotation() } + +internal fun WithExtraProperties.fileLevelJvmName(): Annotations.Annotation? = + extra[Annotations]?.fileLevelAnnotations?.entries?.firstNotNullResult { (_, annotations) -> annotations.jvmNameAnnotation() } + +internal fun List.jvmNameAnnotation(): Annotations.Annotation? = + firstOrNull { it.isJvmName() } + +internal fun Annotations.Annotation.jvmNameAsString(): String? = (params["name"] as? StringValue)?.value \ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt index 60c2e5ccc4..fd40366f60 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt @@ -140,7 +140,7 @@ class JavaSignatureProvider internal constructor(ctcc: CommentsToContentConverte text(it.modifiers()[it]?.toSignatureString() ?: "") signatureForProjection(it.type) text(nbsp.toString()) - link(it.name!!, it.dri) + text(it.name!!) } text(")") } diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameDocumentableTransformer.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameDocumentableTransformer.kt new file mode 100644 index 0000000000..b082d44e33 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameDocumentableTransformer.kt @@ -0,0 +1,100 @@ +package org.jetbrains.dokka.kotlinAsJava.transformers + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer + +class JvmNameDocumentableTransformer : DocumentableTransformer { + private val jvmNameProvider = JvmNameProvider() + private lateinit var context: DokkaContext + + override fun invoke(original: DModule, context: DokkaContext): DModule { + this.context = context + return original.copy(packages = original.packages.map { transform(it) }) + } + + fun transform(documentable: T): T = + with(documentable) { + when (this) { + is DPackage -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DFunction -> { + val name = jvmNameProvider.nameFor(this) + copy( + dri = transformCallable(documentable.dri, name), + name = name + ) + } + is DProperty -> transformGetterAndSetter(this) + is DClasslike -> transformClassLike(this) + is DEnumEntry -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + else -> { + context.logger.warn("Failed to translate a JvmName for ${this.javaClass.canonicalName}") + this + } + } + } as T + + fun transformClassLike(documentable: DClasslike): DClasslike = + with(documentable) { + when (this) { + is DClass -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DAnnotation -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DObject -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DEnum -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DInterface -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + } + } + + fun transformGetterAndSetter(entry: DProperty): DProperty = + with(entry) { + copy( + setter = setter?.let { setter -> + val setterName = jvmNameProvider.nameAsJavaSetter(this) + setter.copy( + dri = transformCallable(setter.dri, setterName), + name = setterName + ) + }, + getter = getter?.let { getter -> + val getterName = jvmNameProvider.nameAsJavaGetter(this) + getter.copy( + dri = transformCallable(getter.dri, getterName), + name = getterName + ) + }) + } + + + fun transformCallable(dri: DRI, callableName: String): DRI = + dri.copy(callable = dri.callable?.copy(name = callableName)) + +} \ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt new file mode 100644 index 0000000000..441abb5185 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt @@ -0,0 +1,28 @@ +package org.jetbrains.dokka.kotlinAsJava.transformers + +import org.jetbrains.dokka.kotlinAsJava.directlyAnnotatedJvmName +import org.jetbrains.dokka.kotlinAsJava.jvmNameAsString +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult + +class JvmNameProvider { + fun nameFor(entry: T): String where T : Documentable, T : WithExtraProperties = + entry.directlyAnnotatedJvmName()?.jvmNameAsString() + ?: entry.name + ?: throw IllegalStateException("Failed to provide a name for ${entry.javaClass.canonicalName}") + + fun nameForSyntheticClass(entry: T): String where T : WithSources, T : WithExtraProperties = + entry.extra[Annotations]?.let { + it.fileLevelAnnotations.entries.firstNotNullResult { (_, annotations) -> + annotations.jvmNameAnnotation()?.jvmNameAsString() + } + } ?: entry.sources.entries.first().value.path.split("/").last().split(".").first().capitalize() + "Kt" + + fun nameAsJavaGetter(entry: DProperty): String = entry.getter?.directlyAnnotatedJvmName()?.jvmNameAsString() ?: "get" + entry.name.capitalize() + + fun nameAsJavaSetter(entry: DProperty): String = entry.setter?.directlyAnnotatedJvmName()?.jvmNameAsString() ?: "set" + entry.name.capitalize() + + private fun List.jvmNameAnnotation(): Annotations.Annotation? = + firstOrNull { it.isJvmName() } +} \ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt index 8b07670f09..5916a11c94 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt @@ -8,4 +8,4 @@ import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer class KotlinAsJavaDocumentableTransformer : DocumentableTransformer { override fun invoke(original: DModule, context: DokkaContext): DModule = original.copy(packages = original.packages.map { it.asJava() }) -} +} \ No newline at end of file diff --git a/plugins/kotlin-as-java/src/test/kotlin/JvmNameTest.kt b/plugins/kotlin-as-java/src/test/kotlin/JvmNameTest.kt new file mode 100644 index 0000000000..4c7e24450c --- /dev/null +++ b/plugins/kotlin-as-java/src/test/kotlin/JvmNameTest.kt @@ -0,0 +1,158 @@ +package kotlinAsJavaPlugin + +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.TypeConstructor +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class JvmNameTest : BaseAbstractTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + classpath += jvmStdlibPath!! + } + } + } + + @Test + fun `should change name for class containing top level function`() { + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt + |@file:JvmName("CustomJvmName") + |package kotlinAsJavaPlugin + |fun sample(): String = "" + """.trimMargin(), + configuration, + ) { + documentablesTransformationStage = { module -> + val expectedClassLikeDri = DRI( + packageName = "kotlinAsJavaPlugin", + classNames = "CustomJvmName", + ) + val classLike = module.packages.flatMap { it.classlikes }.first() + assertEquals(expectedClassLikeDri, classLike.dri) + assertEquals("CustomJvmName", classLike.name) + } + } + } + + @Test + fun `should change name for top level function`() { + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt + |@file:JvmName("CustomJvmName") + |package kotlinAsJavaPlugin + |@JvmName("jvmSample") + |fun sample(): String = "" + """.trimMargin(), + configuration, + ) { + documentablesTransformationStage = { module -> + val expectedFunctionDri = DRI( + packageName = "kotlinAsJavaPlugin", + classNames = "CustomJvmName", + callable = Callable( + "jvmSample", + receiver = null, + params = emptyList() + ) + ) + val function = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first() + assertEquals(expectedFunctionDri, function.dri) + assertEquals("jvmSample", function.name) + } + } + } + + @Test + fun `should change name of a setter for top level property`() { + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt + |@file:JvmName("CustomJvmName") + |package kotlinAsJavaPlugin + |@get:JvmName("xd") + |@set:JvmName("asd") + |var property: String + | get() = "" + | set(value) {} + """.trimMargin(), + configuration, + ) { + documentablesTransformationStage = { module -> + val expectedSetterDri = DRI( + packageName = "kotlinAsJavaPlugin", + classNames = "CustomJvmName", + callable = Callable( + "asd", + receiver = null, + //Todo this is bad, this should be a type in java, look at the bytecode + params = listOf(TypeConstructor("kotlin.String", emptyList())) + ) + ) + val function = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first { it.name == "asd"} + assertEquals(expectedSetterDri, function.dri) + assertEquals("asd", function.name) + } + } + } + + @Test + fun `should change name of a getter for top level property`() { + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt + |@file:JvmName("CustomJvmName") + |package kotlinAsJavaPlugin + |@get:JvmName("xd") + |@set:JvmName("asd") + |var property: String + | get() = "" + | set(value) {} + """.trimMargin(), + configuration, + ) { + documentablesTransformationStage = { module -> + val expectedGetterDri = DRI( + packageName = "kotlinAsJavaPlugin", + classNames = "CustomJvmName", + callable = Callable( + "xd", + receiver = null, + params = emptyList() + ) + ) + val function = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first { it.name == "xd"} + assertEquals(expectedGetterDri, function.dri) + assertEquals("xd", function.name) + } + } + } + + @Test + fun `should leave the name as default if annotation is not provided`() { + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt + |package kotlinAsJavaPlugin + |fun sample(): String = "" + """.trimMargin(), + configuration, + ) { + documentablesTransformationStage = { module -> + val expectedClassLikeDri = DRI( + packageName = "kotlinAsJavaPlugin", + classNames = "SampleKt", + ) + val classLike = module.packages.flatMap { it.classlikes }.first() + assertEquals(expectedClassLikeDri, classLike.dri) + assertEquals("SampleKt", classLike.name) + } + } + } +} \ No newline at end of file