diff options
| author | Andrzej Ratajczak <andrzej.ratajczak98@gmail.com> | 2020-05-04 13:53:10 +0200 |
|---|---|---|
| committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-05-26 11:32:03 +0200 |
| commit | d47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc (patch) | |
| tree | 364724f661349211f053ea80db1a7feb283f48ba /plugins/base/src/main/kotlin/translators | |
| parent | b1e3033fca65ac1e8e312e51d2eed4f278ddb076 (diff) | |
| download | dokka-d47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc.tar.gz dokka-d47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc.tar.bz2 dokka-d47d386ad8c0ff4a2c3b9d5b4450a773bdcba2dc.zip | |
Enhance signature presentation. Support presetnation Java as Kotlin and Kotlin as Java. Refactor annotations creation from PSI/Descriptors. Add proper rendering of annotation signatures in both kotlin syntax and java syntax. Tests for annotations
Diffstat (limited to 'plugins/base/src/main/kotlin/translators')
2 files changed, 140 insertions, 59 deletions
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index f5b86df6..c24a3384 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.base.translators.descriptors +import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.dokka.analysis.DokkaResolutionFacade import org.jetbrains.dokka.links.* import org.jetbrains.dokka.links.Callable @@ -12,19 +13,27 @@ import org.jetbrains.dokka.parsers.MarkdownParser import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator +import org.jetbrains.kotlin.asJava.classes.tryResolveMarkerInterfaceFQName import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.builtins.isFunctionType 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.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies import org.jetbrains.kotlin.idea.kdoc.findKDoc import org.jetbrains.kotlin.load.kotlin.toSourceElement import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.components.isVararg -import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.constants.AnnotationValue as ConstantsAnnotationValue +import org.jetbrains.kotlin.resolve.constants.ArrayValue as ConstantsArrayValue +import org.jetbrains.kotlin.resolve.constants.EnumValue as ConstantsEnumValue +import org.jetbrains.kotlin.resolve.constants.KClassValue as ConstantsKtClassValue +import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.NormalClass +import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.LocalClass import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny @@ -32,10 +41,13 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.types.DynamicType import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeProjection import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.nio.file.Paths +import java.lang.IllegalArgumentException +import kotlin.reflect.jvm.internal.impl.resolve.constants.KClassValue object DefaultDescriptorToDocumentableTranslator : SourceToDocumentableTranslator { @@ -285,7 +297,11 @@ private class DokkaDescriptorVisitor( expectPresentInSet = sourceSet.takeIf { isExpect }, sourceSets = listOf(sourceSet), generics = descriptor.typeParameters.map { it.toTypeParameter() }, - extra = PropertyContainer.withAll(descriptor.additionalExtras(), descriptor.getAnnotations()) + extra = PropertyContainer.withAll( + (descriptor.additionalExtras() + (descriptor.backingField?.getAnnotationsAsExtraModifiers() + ?: emptyList())).toProperty(), + descriptor.getAllAnnotations() + ) ) } @@ -375,7 +391,8 @@ private class DokkaDescriptorVisitor( type = descriptor.type.toBound(), expectPresentInSet = null, documentation = descriptor.resolveDescriptorData(), - sourceSets = listOf(sourceSet) + sourceSets = listOf(sourceSet), + extra = PropertyContainer.withAll(descriptor.getAnnotations()) ) private fun visitPropertyAccessorDescriptor( @@ -395,7 +412,7 @@ private class DokkaDescriptorVisitor( expectPresentInSet = sourceSet.takeIf { isExpect }, documentation = descriptor.resolveDescriptorData(), sourceSets = listOf(sourceSet), - extra = PropertyContainer.withAll(descriptor.additionalExtras(), descriptor.getAnnotations()) + extra = PropertyContainer.withAll(descriptor.additionalExtras(), getAllAnnotations()) ) val name = run { @@ -523,20 +540,23 @@ private class DokkaDescriptorVisitor( extra = PropertyContainer.withAll(additionalExtras()) ) - private fun KotlinType.toBound(): Bound = when (val ctor = constructor.declarationDescriptor) { - is TypeParameterDescriptor -> OtherParameter( - declarationDRI = DRI.from(ctor.containingDeclaration), - name = ctor.name.asString() - ).let { - if (isMarkedNullable) Nullable(it) else it + private fun KotlinType.toBound(): Bound = when (this) { + is DynamicType -> Dynamic + else -> when (val ctor = constructor.declarationDescriptor) { + is TypeParameterDescriptor -> OtherParameter( + declarationDRI = DRI.from(ctor.containingDeclaration), + name = ctor.name.asString() + ).let { + if (isMarkedNullable) Nullable(it) else it + } + else -> TypeConstructor( + DRI.from(constructor.declarationDescriptor!!), // TODO: remove '!!' + arguments.map { it.toProjection() }, + if (isExtensionFunctionType) FunctionModifiers.EXTENSION + else if (isFunctionType) FunctionModifiers.FUNCTION + else FunctionModifiers.NONE + ) } - else -> TypeConstructor( - DRI.from(constructor.declarationDescriptor!!), // TODO: remove '!!' - arguments.map { it.toProjection() }, - if (isExtensionFunctionType) FunctionModifiers.EXTENSION - else if (isFunctionType) FunctionModifiers.FUNCTION - else FunctionModifiers.NONE - ) } private fun TypeProjection.toProjection(): Projection = @@ -573,7 +593,6 @@ private class DokkaDescriptorVisitor( DescriptorDocumentableSource(this).toSourceSetDependent() private fun FunctionDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.DYNAMIC.takeIf { isDynamic() }, ExtraModifiers.INFIX.takeIf { isInfix }, ExtraModifiers.INLINE.takeIf { isInline }, ExtraModifiers.SUSPEND.takeIf { isSuspend }, @@ -585,17 +604,14 @@ private class DokkaDescriptorVisitor( ).toProperty() private fun ClassDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.DYNAMIC.takeIf { isDynamic() }, ExtraModifiers.INLINE.takeIf { isInline }, ExtraModifiers.EXTERNAL.takeIf { isExternal }, ExtraModifiers.INNER.takeIf { isInner }, - ExtraModifiers.DATA.takeIf { isData }, - ExtraModifiers.OVERRIDE.takeIf { getSuperInterfaces().isNotEmpty() || getSuperClassNotAny() != null } + ExtraModifiers.DATA.takeIf { isData } ).toProperty() private fun ValueParameterDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.DYNAMIC.takeIf { isDynamic() }, ExtraModifiers.NOINLINE.takeIf { isNoinline }, ExtraModifiers.CROSSINLINE.takeIf { isCrossinline }, ExtraModifiers.CONST.takeIf { isConst }, @@ -605,28 +621,62 @@ private class DokkaDescriptorVisitor( private fun TypeParameterDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.DYNAMIC.takeIf { isDynamic() }, ExtraModifiers.REIFIED.takeIf { isReified } ).toProperty() private fun PropertyDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.DYNAMIC.takeIf { isDynamic() }, ExtraModifiers.CONST.takeIf { isConst }, ExtraModifiers.LATEINIT.takeIf { isLateInit }, ExtraModifiers.STATIC.takeIf { isJvmStaticInObjectOrClassOrInterface() }, ExtraModifiers.EXTERNAL.takeIf { isExternal }, ExtraModifiers.OVERRIDE.takeIf { DescriptorUtils.isOverride(this) } - ).toProperty() + ) private fun List<ExtraModifiers>.toProperty() = AdditionalModifiers(this.toSet()) - private fun DeclarationDescriptor.getAnnotations() = annotations.map { annotation -> - Annotations.Annotation( - annotation.let { it.annotationClass as DeclarationDescriptor }.let { DRI.from(it) }, - annotation.allValueArguments.map { (k, v) -> k.asString() to v.value.toString() }.toMap() - ) - }.let(::Annotations) + private fun Annotated.getAnnotations() = getListOfAnnotations().let(::Annotations) + + private fun Annotated.getListOfAnnotations() = annotations.map { it.toAnnotation() } + + private fun ConstantValue<*>.toValue(): AnnotationParameterValue = when (this) { + is ConstantsAnnotationValue -> AnnotationValue(value.let { it.toAnnotation() }) + is ConstantsArrayValue -> ArrayValue(value.map { it.toValue() }) + is ConstantsEnumValue -> EnumValue( + enumEntryName.identifier, + enumClassId.let { DRI(it.packageFqName.asString(), it.relativeClassName.asString()) }) + is ConstantsKtClassValue -> when(value) { + is NormalClass -> (value as NormalClass).value.classId.let { + ClassValue( + it.relativeClassName.asString(), + DRI(it.packageFqName.asString(), it.relativeClassName.asString()) + ) + } + is LocalClass -> (value as LocalClass).type.let { + ClassValue( + it.toString(), + DRI.from(it.constructor.declarationDescriptor as DeclarationDescriptor) + ) + } + } + else -> StringValue(toString()) + } + + private fun AnnotationDescriptor.toAnnotation() = Annotations.Annotation( + DRI.from(annotationClass as DeclarationDescriptor), + allValueArguments.map { it.key.asString() to it.value.toValue() }.toMap() + ) + + private fun PropertyDescriptor.getAllAnnotations() = + (getListOfAnnotations() + (backingField?.getListOfAnnotations() ?: emptyList())).let(::Annotations) + + private fun FieldDescriptor.getAnnotationsAsExtraModifiers() = getAnnotations().content.mapNotNull { + try { + ExtraModifiers.valueOf(it.dri.classNames?.toUpperCase() ?: "") + } catch (e: IllegalArgumentException) { + null + } + } private fun ValueParameterDescriptor.getDefaultValue(): String? = (source as? KotlinSourceElement)?.psi?.children?.find { it is KtExpression }?.text diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index ab4a84f6..0f8fc011 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -1,10 +1,13 @@ package org.jetbrains.dokka.base.translators.psi +import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute import com.intellij.lang.jvm.JvmModifier import com.intellij.lang.jvm.types.JvmReferenceType import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.* import com.intellij.psi.impl.source.PsiClassReferenceType +import com.intellij.psi.impl.source.PsiImmediateClassType +import com.intellij.psi.impl.source.tree.java.PsiArrayInitializerMemberValueImpl import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.nextTarget import org.jetbrains.dokka.links.withClass @@ -24,6 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.io.File +import java.lang.ClassValue object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { @@ -162,7 +166,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { constructors.map { parseFunction(it, true) }, mapTypeParameters(dri), listOf(sourceSetData), - PropertyContainer.empty<DAnnotation>() + annotations.toList().toExtra() + PropertyContainer.empty<DAnnotation>() + annotations.toList().toListOfAnnotations().let(::Annotations) ) isEnum -> DEnum( dri, @@ -177,7 +181,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { emptyList(), emptyList(), listOf(sourceSetData), - PropertyContainer.empty<DEnumEntry>() + entry.annotations.toList().toExtra() + PropertyContainer.empty<DEnumEntry>() + entry.annotations.toList().toListOfAnnotations().let(::Annotations) ) }, documentation, @@ -191,7 +195,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { constructors.map { parseFunction(it, true) }, ancestors, listOf(sourceSetData), - PropertyContainer.empty<DEnum>() + annotations.toList().toExtra() + PropertyContainer.empty<DEnum>() + annotations.toList().toListOfAnnotations().let(::Annotations) ) isInterface -> DInterface( dri, @@ -207,7 +211,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { mapTypeParameters(dri), ancestors, listOf(sourceSetData), - PropertyContainer.empty<DInterface>() + annotations.toList().toExtra() + PropertyContainer.empty<DInterface>() + annotations.toList().toListOfAnnotations().let(::Annotations) ) else -> DClass( dri, @@ -225,7 +229,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { null, modifiers, listOf(sourceSetData), - PropertyContainer.empty<DClass>() + annotations.toList().toExtra() + PropertyContainer.empty<DClass>() + annotations.toList().toListOfAnnotations().let(::Annotations) ) } } @@ -259,15 +263,17 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { null, psi.getModifier().toPlatformDependant(), listOf(sourceSetData), - PropertyContainer.withAll( - InheritedFunction(isInherited), - psi.annotations.toList().toExtra(), - psi.additionalExtras() - ) + psi.additionalExtras().let { + PropertyContainer.withAll( + InheritedFunction(isInherited), + it, + (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).let(::Annotations) + ) + } ) } - private fun PsiMethod.additionalExtras() = AdditionalModifiers( + private fun PsiModifierListOwner.additionalExtras() = AdditionalModifiers( listOfNotNull( ExtraModifiers.STATIC.takeIf { hasModifier(JvmModifier.STATIC) }, ExtraModifiers.NATIVE.takeIf { hasModifier(JvmModifier.NATIVE) }, @@ -279,6 +285,13 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { ).toSet() ) + private fun AdditionalModifiers.toListOfAnnotations() = this.content.map { + if (it.name != "STATIC") + Annotations.Annotation(DRI("kotlin.jvm", it.name.toLowerCase().capitalize()), emptyMap()) + else + Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap()) + } + private fun getBound(type: PsiType): Bound = cachedBounds.getOrPut(type.canonicalText) { when (type) { @@ -295,7 +308,8 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { DRI("kotlin", "Array"), listOf(getProjection(type.componentType)) ) - is PsiPrimitiveType -> if(type.name == "void") Void else PrimitiveJavaType(type.name) + is PsiPrimitiveType -> if (type.name == "void") Void else PrimitiveJavaType(type.name) + is PsiImmediateClassType -> JavaObject else -> throw IllegalStateException("${type.presentableText} is not supported by PSI parser") } } @@ -375,26 +389,43 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { psi.getModifier().toPlatformDependant(), listOf(sourceSetData), emptyList(), - PropertyContainer.empty<DProperty>() + psi.annotations.toList().toExtra() + psi.additionalExtras().let { + PropertyContainer.withAll<DProperty>( + it, + (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).let(::Annotations) + ) + } ) } - private fun Collection<PsiAnnotation>.toExtra() = mapNotNull { annotation -> - val resolved = annotation.getChildOfType<PsiJavaCodeReferenceElement>()?.resolve() ?: run { - logger.error("$annotation cannot be resolved to symbol!") - return@mapNotNull null - } + private fun Collection<PsiAnnotation>.toListOfAnnotations() = mapNotNull { it.toAnnotation() } - Annotations.Annotation( - DRI.from(resolved), - annotation.attributes.mapNotNull { attr -> - if (attr is PsiNameValuePair) { - attr.value?.text?.let { attr.attributeName to it } - } else { - attr.attributeName to "" - } - }.toMap() + private fun JvmAnnotationAttribute.toValue(): AnnotationParameterValue = when (this) { + is PsiNameValuePair -> value?.toValue() ?: StringValue("") + else -> StringValue(this.attributeName) + } + + private fun PsiAnnotationMemberValue.toValue(): AnnotationParameterValue = when(this) { + is PsiAnnotation -> AnnotationValue(toAnnotation()) + is PsiArrayInitializerMemberValue -> ArrayValue(this.initializers.map { it.toValue() }) + is PsiReferenceExpression -> EnumValue( + text ?: "", + driOfReference() ) - }.let(::Annotations) + is PsiClassObjectAccessExpression -> ClassValue( + text ?: "", + DRI.from(((type as PsiImmediateClassType).parameters.single() as PsiClassReferenceType).resolve()!!) + ) + else -> StringValue(text ?: "") + } + + private fun PsiAnnotation.toAnnotation() = Annotations.Annotation( + driOfReference(), + attributes.mapNotNull { it.attributeName to it.toValue() }.toMap() + ) + + private fun PsiElement.driOfReference() = DRI.from(getChildOfType<PsiJavaCodeReferenceElement>()?.resolve() ?: + throw IllegalStateException("$this cannot be resolved to symbol!") + ) } } |
