Skip to content

Commit

Permalink
Parsing of JvmName (#1675)
Browse files Browse the repository at this point in the history
* Parsing of JvmName

* Make JvmName processor run after KaJ
  • Loading branch information
MarcinAman authored Dec 29, 2020
1 parent e55f3b0 commit f5e7cff
Show file tree
Hide file tree
Showing 26 changed files with 683 additions and 146 deletions.
34 changes: 30 additions & 4 deletions core/src/main/kotlin/model/additionalExtras.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,48 @@ class AdditionalModifiers(val content: SourceSetDependent<Set<ExtraModifiers>>)

fun SourceSetDependent<Set<ExtraModifiers>>.toAdditionalModifiers() = AdditionalModifiers(this)

class Annotations(val content: SourceSetDependent<List<Annotation>>) : ExtraProperty<Documentable> {
data class Annotations(
private val myContent: SourceSetDependent<List<Annotation>>
) : ExtraProperty<Documentable> {
companion object : ExtraProperty.Key<Documentable, Annotations> {
override fun mergeStrategyFor(left: Annotations, right: Annotations): MergeStrategy<Documentable> =
MergeStrategy.Replace(Annotations(left.content + right.content))
MergeStrategy.Replace(Annotations(left.myContent + right.myContent))
}

override val key: ExtraProperty.Key<Documentable, *> = Annotations

data class Annotation(val dri: DRI, val params: Map<String, AnnotationParameterValue>, val mustBeDocumented: Boolean = false) {
data class Annotation(
val dri: DRI,
val params: Map<String, AnnotationParameterValue>,
val mustBeDocumented: Boolean = false,
val scope: AnnotationScope = AnnotationScope.DIRECT
) {
override fun equals(other: Any?): Boolean = when (other) {
is Annotation -> dri == other.dri
else -> false
}

override fun hashCode(): Int = dri.hashCode()
}

@Deprecated("Use directAnnotations or fileLevelAnnotations")
val content: SourceSetDependent<List<Annotation>>
get() = myContent

val directAnnotations: SourceSetDependent<List<Annotation>> = annotationsByScope(AnnotationScope.DIRECT)

val fileLevelAnnotations: SourceSetDependent<List<Annotation>> = annotationsByScope(AnnotationScope.FILE)

private fun annotationsByScope(scope: AnnotationScope): SourceSetDependent<List<Annotation>> =
myContent.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<List<Annotations.Annotation>>.toAnnotations() = Annotations(this)
Expand Down Expand Up @@ -65,7 +91,7 @@ data class ActualTypealias(val underlyingType: SourceSetDependent<Bound>) : Extr
override val key: ExtraProperty.Key<DClasslike, ActualTypealias> = ActualTypealias
}

data class ConstructorValues(val values: SourceSetDependent<List<String>>) : ExtraProperty<DEnumEntry>{
data class ConstructorValues(val values: SourceSetDependent<List<String>>) : ExtraProperty<DEnumEntry> {
companion object : ExtraProperty.Key<DEnumEntry, ConstructorValues> {
override fun mergeStrategyFor(left: ConstructorValues, right: ConstructorValues) =
MergeStrategy.Replace(ConstructorValues(left.values + right.values))
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/kotlin/model/jvmName.kt
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 2 additions & 2 deletions plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface JvmSignatureUtils {
joinToString("") { it.name.toLowerCase() + " " }

fun <T : Documentable> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> =
extra[Annotations]?.content ?: emptyMap()
extra[Annotations]?.directAnnotations ?: emptyMap()

private fun PageContentBuilder.DocumentableContentBuilder.annotations(
d: Documentable,
Expand Down Expand Up @@ -131,7 +131,7 @@ interface JvmSignatureUtils {
}

fun <T : Documentable> WithExtraProperties<T>.stylesIfDeprecated(sourceSetData: DokkaSourceSet): Set<TextStyle> =
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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fun <T> T.isDeprecated() where T : WithExtraProperties<out Documentable> =

val <T> T.deprecatedAnnotation where T : WithExtraProperties<out Documentable>
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/"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class SinceKotlinTransformer(val context: DokkaContext) : DocumentableTransforme

private fun Documentable.appendSinceKotlin() =
sourceSets.fold(documentation) { acc, sourceSet ->
safeAs<WithExtraProperties<Documentable>>()?.extra?.get(Annotations)?.content?.get(sourceSet)?.find {
safeAs<WithExtraProperties<Documentable>>()?.extra?.get(Annotations)?.directAnnotations?.get(sourceSet)?.find {
it.dri == DRI("kotlin", "SinceKotlin")
}?.params?.get("version").safeAs<StringValue>()?.value?.let { version ->
acc.mapValues {
Expand Down
3 changes: 3 additions & 0 deletions plugins/base/src/main/kotlin/translators/annotationsValue.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.jetbrains.dokka.base.translators

internal fun unquotedValue(value: String): String = value.removeSurrounding("\"")
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.jetbrains.dokka.analysis.from
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.parsers.MarkdownParser
import org.jetbrains.dokka.base.translators.isDirectlyAnException
import org.jetbrains.dokka.base.translators.unquotedValue
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.links.Callable
import org.jetbrains.dokka.model.*
Expand All @@ -19,10 +20,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
Expand All @@ -33,7 +32,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
Expand All @@ -51,8 +49,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
Expand Down Expand Up @@ -117,7 +115,7 @@ private class DokkaDescriptorVisitor(
}
}

private fun <T> T.toSourceSetDependent() = mapOf(sourceSet to this)
private fun <T> T.toSourceSetDependent() = if(this != null) mapOf(sourceSet to this) else emptyMap()

suspend fun visitPackageFragmentDescriptor(
descriptor: PackageFragmentDescriptor,
Expand Down Expand Up @@ -436,13 +434,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()),
)
)
)
}
}
Expand Down Expand Up @@ -489,7 +490,7 @@ private class DokkaDescriptorVisitor(
extra = PropertyContainer.withAll(
InheritedMember(inheritedFrom.toSourceSetDependent()),
descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(),
descriptor.getAnnotations().toSourceSetDependent().toAnnotations()
(descriptor.getAnnotations() + descriptor.fileLevelAnnotations()).toSourceSetDependent().toAnnotations(),
)
)
}
Expand Down Expand Up @@ -588,7 +589,7 @@ private class DokkaDescriptorVisitor(
val name = run {
val modifier = if (isGetter) "get" else "set"
val rawName = propertyDescriptor.name.asString()
"$modifier${rawName[0].toUpperCase()}${rawName.drop(1)}"
"$modifier${rawName.capitalize()}"
}

val parameters =
Expand Down Expand Up @@ -782,8 +783,7 @@ private class DokkaDescriptorVisitor(

private suspend fun org.jetbrains.kotlin.descriptors.annotations.Annotations.getPresentableName(): String? =
map { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name")
.safeAs<StringValue>()?.value?.drop(1)
?.dropLast(1) // Dropping enclosing doublequotes because we don't want to have it in our custom signature serializer
.safeAs<StringValue>()?.value?.let { unquotedValue(it) }

private suspend fun KotlinType.toBound(): Bound = when (this) {

Expand Down Expand Up @@ -928,19 +928,25 @@ private class DokkaDescriptorVisitor(
)
}
}
else -> StringValue(toString())
else -> StringValue(unquotedValue(toString()))
}

private suspend fun AnnotationDescriptor.toAnnotation(): Annotations.Annotation {
private suspend fun AnnotationDescriptor.toAnnotation(scope: Annotations.AnnotationScope = Annotations.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<String, AnnotationParameterValue>,
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<Annotations.Annotation> =
getAnnotations() + (backingField?.getAnnotations() ?: emptyList())

Expand Down Expand Up @@ -1004,6 +1010,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 = Annotations.AnnotationScope.FILE) }
}

private data class AncestryLevel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.translators.isDirectlyAnException
import org.jetbrains.dokka.base.translators.psi.parsers.JavaDocumentationParser
import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser
import org.jetbrains.dokka.base.translators.unquotedValue
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.nextTarget
import org.jetbrains.dokka.links.withClass
Expand Down Expand Up @@ -530,6 +531,9 @@ class DefaultPsiToDocumentableTranslator(
private fun JvmAnnotationAttribute.toValue(): AnnotationParameterValue = when (this) {
is PsiNameValuePair -> value?.toValue() ?: StringValue("")
else -> StringValue(this.attributeName)
}.let { annotationValue ->
if (annotationValue is StringValue) annotationValue.copy(unquotedValue(annotationValue.value))
else annotationValue
}

private fun PsiAnnotationMemberValue.toValue(): AnnotationParameterValue? = when (this) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ 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.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() {
Expand All @@ -18,6 +24,7 @@ class ContentForAnnotationsTest : BaseAbstractTest() {
sourceSet {
sourceRoots = listOf("src/")
analysisPlatform = "jvm"
classpath += jvmStdlibPath!!
}
}
}
Expand Down Expand Up @@ -218,4 +225,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 = Annotations.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(Annotations.AnnotationScope.DIRECT, getterAnnotation.scope)

assertEquals(expectedAnnotation("asd"), setterAnnotation)
assertFalse(setterAnnotation?.mustBeDocumented!!)
assertEquals(Annotations.AnnotationScope.DIRECT, setterAnnotation.scope)
}
}
}
}
Loading

0 comments on commit f5e7cff

Please sign in to comment.