diff options
author | Ignat Beresnev <ignat.beresnev@jetbrains.com> | 2023-07-05 10:04:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-05 10:04:55 +0200 |
commit | 9559158bfeeb274e9ccf1b4563f1b23b42afc493 (patch) | |
tree | 3ece0887623cfe2b7148af23001867a1dd5e6597 /plugins/base/src/main/kotlin/translators/descriptors | |
parent | cbd9733d3dd2f52992e98e7cebd072091a572529 (diff) | |
download | dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.gz dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.bz2 dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.zip |
Decompose Kotlin/Java analysis (#3034)
* Extract analysis into separate modules
Diffstat (limited to 'plugins/base/src/main/kotlin/translators/descriptors')
6 files changed, 0 insertions, 1518 deletions
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt deleted file mode 100644 index ce4776e7..00000000 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ /dev/null @@ -1,1250 +0,0 @@ -package org.jetbrains.dokka.base.translators.descriptors - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.util.PsiLiteralUtil.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.runBlocking -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.KotlinAnalysis -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.psi.parsers.JavadocParser -import org.jetbrains.dokka.base.translators.typeConstructorsBeingExceptions -import org.jetbrains.dokka.base.translators.unquotedValue -import org.jetbrains.dokka.links.* -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.AnnotationTarget -import org.jetbrains.dokka.model.Nullable -import org.jetbrains.dokka.model.Visibility -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.querySingle -import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.dokka.utilities.parallelMap -import org.jetbrains.dokka.utilities.parallelMapNotNull -import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor -import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType -import org.jetbrains.kotlin.builtins.isExtensionFunctionType -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.annotations.Annotated -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.descriptors.java.JavaVisibilities -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi -import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor -import org.jetbrains.kotlin.load.kotlin.toSourceElement -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.calls.components.isVararg -import org.jetbrains.kotlin.resolve.calls.util.getValueArgumentsInParentheses -import org.jetbrains.kotlin.resolve.constants.ConstantValue -import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.LocalClass -import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.NormalClass -import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull -import org.jetbrains.kotlin.resolve.descriptorUtil.parents -import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.resolve.scopes.StaticScopeForKotlinEnum -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.typeUtil.immediateSupertypes -import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -import org.jetbrains.kotlin.utils.addToStdlib.safeAs -import java.nio.file.Paths -import org.jetbrains.kotlin.resolve.constants.AnnotationValue as ConstantsAnnotationValue -import org.jetbrains.kotlin.resolve.constants.ArrayValue as ConstantsArrayValue -import org.jetbrains.kotlin.resolve.constants.BooleanValue as ConstantsBooleanValue -import org.jetbrains.kotlin.resolve.constants.DoubleValue as ConstantsDoubleValue -import org.jetbrains.kotlin.resolve.constants.EnumValue as ConstantsEnumValue -import org.jetbrains.kotlin.resolve.constants.FloatValue as ConstantsFloatValue -import org.jetbrains.kotlin.resolve.constants.IntValue as ConstantsIntValue -import org.jetbrains.kotlin.resolve.constants.KClassValue as ConstantsKtClassValue -import org.jetbrains.kotlin.resolve.constants.LongValue as ConstantsLongValue -import org.jetbrains.kotlin.resolve.constants.NullValue as ConstantsNullValue -import org.jetbrains.kotlin.resolve.constants.UIntValue as ConstantsUIntValue -import org.jetbrains.kotlin.resolve.constants.ULongValue as ConstantsULongValue - -class DefaultDescriptorToDocumentableTranslator( - private val context: DokkaContext -) : AsyncSourceToDocumentableTranslator, ExternalClasslikesTranslator { - - private val kotlinAnalysis: KotlinAnalysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis } - - override suspend fun invokeSuspending(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { - val (environment, facade) = kotlinAnalysis[sourceSet] - val packageFragments = environment.getSourceFiles().asSequence() - .map { it.packageFqName } - .distinct() - .mapNotNull { facade.resolveSession.getPackageFragment(it) } - .toList() - - return DokkaDescriptorVisitor(sourceSet, kotlinAnalysis[sourceSet].facade, context.logger).run { - packageFragments.mapNotNull { it.safeAs<PackageFragmentDescriptor>() }.parallelMap { - visitPackageFragmentDescriptor( - it - ) - } - }.let { - DModule( - name = context.configuration.moduleName, - packages = it, - documentation = emptyMap(), - expectPresentInSet = null, - sourceSets = setOf(sourceSet) - ) - } - } - - override fun translateClassDescriptor(descriptor: ClassDescriptor, sourceSet: DokkaSourceSet): DClasslike { - val driInfo = DRI.from(descriptor.parents.first()).withEmptyInfo() - - return runBlocking(Dispatchers.Default) { - DokkaDescriptorVisitor(sourceSet, kotlinAnalysis[sourceSet].facade, context.logger) - .visitClassDescriptor(descriptor, driInfo) - } - } -} - -data class DRIWithPlatformInfo( - val dri: DRI, - val actual: SourceSetDependent<DocumentableSource> -) - -fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, emptyMap()) - -private class DokkaDescriptorVisitor( - private val sourceSet: DokkaSourceSet, - private val resolutionFacade: DokkaResolutionFacade, - private val logger: DokkaLogger -) { - private val javadocParser = JavadocParser(logger, resolutionFacade) - private val syntheticDocProvider = SyntheticDescriptorDocumentationProvider(resolutionFacade) - - private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter { - it.toSourceElement.containingFile.toString().let { path -> - path.isNotBlank() && sourceSet.sourceRoots.any { root -> - Paths.get(path).startsWith(root.toPath()) - } - } - } - - private fun <T> T.toSourceSetDependent() = if (this != null) mapOf(sourceSet to this) else emptyMap() - - suspend fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor): DPackage { - val name = descriptor.fqName.asString().takeUnless { it.isBlank() } ?: "" - val driWithPlatform = DRI(packageName = name).withEmptyInfo() - val scope = descriptor.getMemberScope() - return coroutineScope { - val descriptorsWithKind = scope.getDescriptorsWithKind(true) - - val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } - val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } - val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val typealiases = async { descriptorsWithKind.typealiases.visitTypealiases() } - - DPackage( - dri = driWithPlatform.dri, - functions = functions.await(), - properties = properties.await(), - classlikes = classlikes.await(), - typealiases = typealiases.await(), - documentation = descriptor.resolveDescriptorData(), - sourceSets = setOf(sourceSet) - ) - } - } - - suspend fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClasslike = - when (descriptor.kind) { - ClassKind.ENUM_CLASS -> enumDescriptor(descriptor, parent) - ClassKind.OBJECT -> objectDescriptor(descriptor, parent) - ClassKind.INTERFACE -> interfaceDescriptor(descriptor, parent) - ClassKind.ANNOTATION_CLASS -> annotationDescriptor(descriptor, parent) - else -> classDescriptor(descriptor, parent) - } - - private suspend fun interfaceDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DInterface { - val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() - val scope = descriptor.unsubstitutedMemberScope - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() - - return coroutineScope { - val descriptorsWithKind = scope.getDescriptorsWithKind() - - val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } - val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } - val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } - - DInterface( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - functions = functions.await(), - properties = properties.await(), - classlikes = classlikes.await(), - sources = descriptor.createSources(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - documentation = info.docs, - generics = generics.await(), - companion = descriptor.companion(driWithPlatform), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()), - info.ancestry.exceptionInSupertypesOrNull() - ) - ) - } - } - - private suspend fun objectDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DObject { - val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() - val scope = descriptor.unsubstitutedMemberScope - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() - - - return coroutineScope { - val descriptorsWithKind = scope.getDescriptorsWithKind() - - val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } - val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } - val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - - DObject( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - functions = functions.await(), - properties = properties.await(), - classlikes = classlikes.await(), - sources = descriptor.createSources(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - documentation = info.docs, - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()), - info.ancestry.exceptionInSupertypesOrNull() - ) - ) - } - - - } - - private suspend fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnum { - val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() - - return coroutineScope { - val descriptorsWithKind = descriptor.getEnumDescriptorsWithKind() - - val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } - val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } - val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val constructors = - async { descriptor.constructors.parallelMap { visitConstructorDescriptor(it, driWithPlatform) } } - val entries = async { descriptorsWithKind.enumEntries.visitEnumEntries(driWithPlatform) } - - DEnum( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - entries = entries.await(), - constructors = constructors.await(), - functions = functions.await(), - properties = properties.await(), - classlikes = classlikes.await(), - sources = descriptor.createSources(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - documentation = info.docs, - companion = descriptor.companion(driWithPlatform), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()) - ) - ) - } - } - - private fun ClassDescriptor.getEnumDescriptorsWithKind(): DescriptorsWithKind { - val descriptorsWithKind = this.unsubstitutedMemberScope.getDescriptorsWithKind() - val staticScopeForKotlinEnum = (this.staticScope as? StaticScopeForKotlinEnum) ?: return descriptorsWithKind - - // synthetic values() and valueOf() functions are not present among average class functions - val enumSyntheticFunctions = staticScopeForKotlinEnum.getContributedDescriptors { true } - .filterIsInstance<FunctionDescriptor>() - - return descriptorsWithKind.copy(functions = descriptorsWithKind.functions + enumSyntheticFunctions) - } - - private suspend fun visitEnumEntryDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnumEntry { - val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() - val scope = descriptor.unsubstitutedMemberScope - val isExpect = descriptor.isExpect - - return coroutineScope { - val descriptorsWithKind = scope.getDescriptorsWithKind() - - val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } - val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } - val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - - DEnumEntry( - dri = driWithPlatform.dri.withEnumEntryExtra(), - name = descriptor.name.asString(), - documentation = descriptor.resolveDescriptorData(), - functions = functions.await(), - properties = properties.await(), - classlikes = classlikes.await(), - sourceSets = setOf(sourceSet), - expectPresentInSet = sourceSet.takeIf { isExpect }, - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ) - ) - } - } - - private suspend fun annotationDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DAnnotation { - val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() - val scope = descriptor.unsubstitutedMemberScope - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - - return coroutineScope { - val descriptorsWithKind = scope.getDescriptorsWithKind() - - val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } - val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } - val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } - val constructors = - async { descriptor.constructors.parallelMap { visitConstructorDescriptor(it, driWithPlatform) } } - - DAnnotation( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - documentation = descriptor.resolveDescriptorData(), - functions = functions.await(), - properties = properties.await(), - classlikes = classlikes.await(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ), - companion = descriptor.companionObjectDescriptor?.let { objectDescriptor(it, driWithPlatform) }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - generics = generics.await(), - constructors = constructors.await(), - sources = descriptor.createSources() - ) - } - - - } - - private suspend fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClass { - val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() - val scope = descriptor.unsubstitutedMemberScope - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - val info = descriptor.resolveClassDescriptionData() - val actual = descriptor.createSources() - - return coroutineScope { - val descriptorsWithKind = scope.getDescriptorsWithKind() - - val (regularFunctions, accessors) = splitFunctionsAndInheritedAccessors( - properties = descriptorsWithKind.properties, - functions = descriptorsWithKind.functions - ) - - val functions = async { regularFunctions.visitFunctions(driWithPlatform) } - val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform, accessors) } - val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } - val constructors = async { - descriptor.constructors.parallelMap { - visitConstructorDescriptor( - it, - if (it.isPrimary) DRIWithPlatformInfo(driWithPlatform.dri, actual) - else DRIWithPlatformInfo(driWithPlatform.dri, emptyMap()) - ) - } - } - - DClass( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - constructors = constructors.await(), - functions = functions.await(), - properties = properties.await(), - classlikes = classlikes.await(), - sources = actual, - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - generics = generics.await(), - documentation = info.docs, - modifier = descriptor.modifier().toSourceSetDependent(), - companion = descriptor.companion(driWithPlatform), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.ancestry.allImplementedInterfaces().toSourceSetDependent()), - info.ancestry.exceptionInSupertypesOrNull() - ) - ) - } - } - - /** - * @param implicitAccessors getters/setters that are not part of the property descriptor, for instance - * average methods inherited from java sources that access the property - */ - private suspend fun visitPropertyDescriptor( - originalDescriptor: PropertyDescriptor, - implicitAccessors: DescriptorAccessorHolder?, - parent: DRIWithPlatformInfo - ): DProperty { - val (dri, _) = originalDescriptor.createDRI() - val inheritedFrom = dri.getInheritedFromDRI(parent) - val descriptor = originalDescriptor.getConcreteDescriptor() - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - - val actual = originalDescriptor.createSources() - - // example - generated getter that comes with data classes - suspend fun getDescriptorGetter() = - descriptor.accessors - .firstIsInstanceOrNull<PropertyGetterDescriptor>() - ?.let { - visitPropertyAccessorDescriptor(it, descriptor, dri, inheritedFrom) - } - - suspend fun getImplicitAccessorGetter() = - implicitAccessors?.getter?.let { visitFunctionDescriptor(it, parent) } - - // example - generated setter that comes with data classes - suspend fun getDescriptorSetter() = - descriptor.accessors - .firstIsInstanceOrNull<PropertySetterDescriptor>() - ?.let { - visitPropertyAccessorDescriptor(it, descriptor, dri, inheritedFrom) - } - - suspend fun getImplicitAccessorSetter() = - implicitAccessors?.setter?.let { visitFunctionDescriptor(it, parent) } - - return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } - val getter = getDescriptorGetter() ?: getImplicitAccessorGetter() - val setter = getDescriptorSetter() ?: getImplicitAccessorSetter() - - DProperty( - dri = dri, - name = descriptor.name.asString(), - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) - }, - sources = actual, - getter = getter, - setter = setter, - visibility = descriptor.getVisibility(implicitAccessors).toSourceSetDependent(), - documentation = descriptor.resolveDescriptorData(), - modifier = descriptor.modifier().toSourceSetDependent(), - type = descriptor.returnType!!.toBound(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - sourceSets = setOf(sourceSet), - generics = generics.await(), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - listOfNotNull( - (descriptor.additionalExtras() + descriptor.getAnnotationsWithBackingField() - .toAdditionalExtras()).toSet().toSourceSetDependent().toAdditionalModifiers(), - (descriptor.getAnnotationsWithBackingField() + descriptor.fileLevelAnnotations()).toSourceSetDependent() - .toAnnotations(), - descriptor.getDefaultValue()?.let { DefaultValue(it.toSourceSetDependent()) }, - inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) }, - takeIf { descriptor.isVar(getter, setter) }?.let { IsVar }, - ) - ) - ) - } - } - - private fun PropertyDescriptor.isVar(getter: DFunction?, setter: DFunction?): Boolean { - return if (this is JavaPropertyDescriptor) { - // in Java, concepts of extensibility and mutability are mixed into a single `final` modifier - // in Kotlin, it's different - val/var controls mutability and open modifier controls extensibility - // so when inheriting Java properties, you can end up with a final var - non extensible mutable prop - val isMutable = this.isVar - // non-final java property should be var if it has no accessors at all or has a setter - (isMutable && getter == null && setter == null) || (getter != null && setter != null) - } else { - this.isVar - } - } - - private fun PropertyDescriptor.getVisibility(implicitAccessors: DescriptorAccessorHolder?): Visibility { - val isNonPublicJavaProperty = this is JavaPropertyDescriptor && !this.visibility.isPublicAPI - val visibility = - if (isNonPublicJavaProperty) { - // only try to take implicit getter's visibility if it's a java property - // because it's not guaranteed that implicit accessor will be used - // for the kotlin property, as it may have an explicit accessor of its own, - // i.e in data classes or with get() and set() are overridden - (implicitAccessors?.getter?.visibility ?: this.visibility) - } else { - this.visibility - } - - return visibility.toDokkaVisibility() - } - - private fun CallableMemberDescriptor.createDRI(wasOverridenBy: DRI? = null): Pair<DRI, DRI?> = - if (kind == CallableMemberDescriptor.Kind.DECLARATION || overriddenDescriptors.isEmpty()) - Pair(DRI.from(this), wasOverridenBy) - else - overriddenDescriptors.first().createDRI(DRI.from(this)) - - private suspend fun visitFunctionDescriptor( - originalDescriptor: FunctionDescriptor, - parent: DRIWithPlatformInfo - ): DFunction { - val (dri, _) = originalDescriptor.createDRI() - val inheritedFrom = dri.getInheritedFromDRI(parent) - val descriptor = originalDescriptor.getConcreteDescriptor() - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - - val actual = originalDescriptor.createSources() - return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } - - DFunction( - dri = dri, - name = descriptor.name.asString(), - isConstructor = false, - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) - }, - parameters = descriptor.valueParameters.mapIndexed { index, desc -> - parameter(index, desc, DRIWithPlatformInfo(dri, actual)) - }, - expectPresentInSet = sourceSet.takeIf { isExpect }, - sources = actual, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - generics = generics.await(), - documentation = descriptor.getDocumentation(), - modifier = descriptor.modifier().toSourceSetDependent(), - type = descriptor.returnType!!.toBound(), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) }, - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - (descriptor.getAnnotations() + descriptor.fileLevelAnnotations()).toSourceSetDependent() - .toAnnotations(), - ObviousMember.takeIf { descriptor.isObvious() }, - ) - ) - } - } - - private fun FunctionDescriptor.getDocumentation(): SourceSetDependent<DocumentationNode> { - val isSynthesized = this.kind == CallableMemberDescriptor.Kind.SYNTHESIZED - return if (isSynthesized) { - syntheticDocProvider.getDocumentation(this)?.toSourceSetDependent() ?: emptyMap() - } else { - this.resolveDescriptorData() - } - } - - /** - * `createDRI` returns the DRI of the exact element and potential DRI of an element that is overriding it - * (It can be also FAKE_OVERRIDE which is in fact just inheritance of the symbol) - * - * Looking at what PSIs do, they give the DRI of the element within the classnames where it is actually - * declared and inheritedFrom as the same DRI but truncated callable part. - * Therefore, we set callable to null and take the DRI only if it is indeed coming from different class. - */ - private fun DRI.getInheritedFromDRI(parent: DRIWithPlatformInfo): DRI? { - return this.copy(callable = null) - .takeIf { parent.dri.classNames != this.classNames || parent.dri.packageName != this.packageName } - } - - private fun FunctionDescriptor.isObvious(): Boolean { - return kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE - || (kind == CallableMemberDescriptor.Kind.SYNTHESIZED && !syntheticDocProvider.isDocumented(this)) - || containingDeclaration.fqNameOrNull()?.isObvious() == true - } - - private fun FqName.isObvious(): Boolean = with(this.asString()) { - return this == "kotlin.Any" || this == "kotlin.Enum" - || this == "java.lang.Object" || this == "java.lang.Enum" - } - - suspend fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): DFunction { - val name = descriptor.constructedClass.name.toString() - val dri = parent.dri.copy(callable = Callable.from(descriptor, name)) - val actual = descriptor.createSources() - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - - return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } - - DFunction( - dri = dri, - name = name, - isConstructor = true, - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) - }, - parameters = descriptor.valueParameters.mapIndexed { index, desc -> - parameter(index, desc, DRIWithPlatformInfo(dri, actual)) - }, - sources = actual, - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - documentation = descriptor.resolveDescriptorData().let { sourceSetDependent -> - if (descriptor.isPrimary) { - sourceSetDependent.map { entry -> - Pair( - entry.key, - entry.value.copy(children = (entry.value.children.find { it is Constructor }?.root?.let { constructor -> - listOf(Description(constructor)) - } ?: emptyList<TagWrapper>()) + entry.value.children.filterIsInstance<Param>())) - }.toMap() - } else { - sourceSetDependent - } - }, - type = descriptor.returnType.toBound(), - modifier = descriptor.modifier().toSourceSetDependent(), - generics = generics.await(), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll<DFunction>( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ).let { - if (descriptor.isPrimary) { - it + PrimaryConstructorExtra - } else it - } - ) - } - } - - private suspend fun visitReceiverParameterDescriptor( - descriptor: ReceiverParameterDescriptor, - parent: DRIWithPlatformInfo - ) = DParameter( - dri = parent.dri.copy(target = PointingToDeclaration), - name = null, - type = descriptor.type.toBound(), - expectPresentInSet = null, - documentation = descriptor.resolveDescriptorData(), - sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll(descriptor.getAnnotations().toSourceSetDependent().toAnnotations()) - ) - - private suspend fun visitPropertyAccessorDescriptor( - descriptor: PropertyAccessorDescriptor, - propertyDescriptor: PropertyDescriptor, - parent: DRI, - inheritedFrom: DRI? = null - ): DFunction { - val dri = parent.copy(callable = Callable.from(descriptor)) - val isGetter = descriptor is PropertyGetterDescriptor - val isExpect = descriptor.isExpect - val isActual = descriptor.isActual - - suspend fun PropertyDescriptor.asParameter(parent: DRI) = - DParameter( - parent.copy(target = PointingToCallableParameters(parameterIndex = 1)), - this.name.asString(), - type = this.type.toBound(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - documentation = descriptor.resolveDescriptorData(), - sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations() - ) - ) - - val name = run { - val rawName = propertyDescriptor.name.asString() - /* - * Kotlin has special rules for conversion around properties that - * start with "is" For more info see: - * https://kotlinlang.org/docs/java-interop.html#getters-and-setters - * https://kotlinlang.org/docs/java-to-kotlin-interop.html#properties - * - * Based on our testing, this rule only applies when the letter after - * the "is" is *not* lowercase. This means that words like "issue" won't - * have the rule applied but "is_foobar" and "is1of" will have the rule applied. - */ - val specialCaseIs = rawName.startsWith("is") - && rawName.getOrNull(2)?.isLowerCase() == false - - if (specialCaseIs) { - if (isGetter) rawName else rawName.replaceFirst("is", "set") - } else { - if (isGetter) "get${rawName.capitalize()}" else "set${rawName.capitalize()}" - } - } - - val parameters = - if (isGetter) { - emptyList() - } else { - listOf(propertyDescriptor.asParameter(dri)) - } - - return coroutineScope { - val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } - DFunction( - dri, - name, - isConstructor = false, - parameters = parameters, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - documentation = descriptor.resolveDescriptorData().mapInheritedTagWrappers(), - type = descriptor.returnType!!.toBound(), - generics = generics.await(), - modifier = descriptor.modifier().toSourceSetDependent(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor( - it, - DRIWithPlatformInfo(dri, descriptor.createSources()) - ) - }, - sources = descriptor.createSources(), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) } - ) - ) - } - } - - /** - * Workaround for a problem with inheriting parent TagWrappers of the wrong type. - * - * For instance, if you annotate a class with `@property`, kotlin compiler will propagate - * this tag to the property and its getters and setters. In case of getters and setters, - * it's more correct to display propagated docs as description instead of property - */ - private fun SourceSetDependent<DocumentationNode>.mapInheritedTagWrappers(): SourceSetDependent<DocumentationNode> { - return this.mapValues { (_, value) -> - val mappedChildren = value.children.map { - when (it) { - is Property -> Description(it.root) - else -> it - } - } - value.copy(children = mappedChildren) - } - } - - private suspend fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor) = - with(descriptor) { - coroutineScope { - val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } - val info = buildAncestryInformation(defaultType).copy( - superclass = buildAncestryInformation(underlyingType), - interfaces = emptyList() - ) - DTypeAlias( - dri = DRI.from(this@with), - name = name.asString(), - type = defaultType.toBound(), - expectPresentInSet = null, - underlyingType = underlyingType.toBound().toSourceSetDependent(), - visibility = visibility.toDokkaVisibility().toSourceSetDependent(), - documentation = resolveDescriptorData(), - sourceSets = setOf(sourceSet), - generics = generics.await(), - sources = descriptor.createSources(), - extra = PropertyContainer.withAll( - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - info.exceptionInSupertypesOrNull(), - ) - ) - } - } - - private suspend fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRIWithPlatformInfo) = - DParameter( - dri = parent.dri.copy(target = PointingToCallableParameters(index)), - name = descriptor.name.asString(), - type = descriptor.varargElementType?.toBound() ?: descriptor.type.toBound(), - expectPresentInSet = null, - documentation = descriptor.resolveDescriptorData(), - sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll(listOfNotNull( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - descriptor.getDefaultValue()?.let { DefaultValue(it.toSourceSetDependent()) } - )) - ) - - private data class DescriptorsWithKind( - val functions: List<FunctionDescriptor>, - val properties: List<PropertyDescriptor>, - val classlikes: List<ClassDescriptor>, - val typealiases: List<TypeAliasDescriptor>, - val enumEntries: List<ClassDescriptor> - ) - - private fun MemberScope.getDescriptorsWithKind(shouldFilter: Boolean = false): DescriptorsWithKind { - val descriptors = getContributedDescriptors { true }.let { - if (shouldFilter) it.filterDescriptorsInSourceSet() else it - } - - class EnumEntryDescriptor - - val groupedDescriptors = descriptors.groupBy { - when { - it is FunctionDescriptor -> FunctionDescriptor::class - it is PropertyDescriptor -> PropertyDescriptor::class - it is ClassDescriptor && it.kind != ClassKind.ENUM_ENTRY -> ClassDescriptor::class - it is TypeAliasDescriptor -> TypeAliasDescriptor::class - it is ClassDescriptor && it.kind == ClassKind.ENUM_ENTRY -> EnumEntryDescriptor::class - else -> IllegalStateException::class - } - } - - @Suppress("UNCHECKED_CAST") - return DescriptorsWithKind( - (groupedDescriptors[FunctionDescriptor::class] ?: emptyList()) as List<FunctionDescriptor>, - (groupedDescriptors[PropertyDescriptor::class] ?: emptyList()) as List<PropertyDescriptor>, - (groupedDescriptors[ClassDescriptor::class] ?: emptyList()) as List<ClassDescriptor>, - (groupedDescriptors[TypeAliasDescriptor::class] ?: emptyList()) as List<TypeAliasDescriptor>, - (groupedDescriptors[EnumEntryDescriptor::class] ?: emptyList()) as List<ClassDescriptor> - ) - } - - private suspend fun List<FunctionDescriptor>.visitFunctions(parent: DRIWithPlatformInfo): List<DFunction> = - coroutineScope { parallelMap { visitFunctionDescriptor(it, parent) } } - - private suspend fun List<PropertyDescriptor>.visitProperties( - parent: DRIWithPlatformInfo, - implicitAccessors: Map<PropertyDescriptor, DescriptorAccessorHolder> = emptyMap(), - ): List<DProperty> { - return coroutineScope { - parallelMap { - visitPropertyDescriptor(it, implicitAccessors[it], parent) - } - } - } - - private suspend fun List<ClassDescriptor>.visitClasslikes(parent: DRIWithPlatformInfo): List<DClasslike> = - coroutineScope { parallelMap { visitClassDescriptor(it, parent) } } - - private suspend fun List<TypeAliasDescriptor>.visitTypealiases(): List<DTypeAlias> = - coroutineScope { parallelMap { visitTypeAliasDescriptor(it) } } - - private suspend fun List<ClassDescriptor>.visitEnumEntries(parent: DRIWithPlatformInfo): List<DEnumEntry> = - coroutineScope { parallelMap { visitEnumEntryDescriptor(it, parent) } } - - private fun DeclarationDescriptor.resolveDescriptorData(): SourceSetDependent<DocumentationNode> = - getDocumentation()?.toSourceSetDependent() ?: emptyMap() - - - private suspend fun toTypeConstructor(kt: KotlinType) = - GenericTypeConstructor( - DRI.from(kt.constructor.declarationDescriptor as DeclarationDescriptor), - kt.arguments.map { it.toProjection() }, - extra = PropertyContainer.withAll(kt.getAnnotations().toSourceSetDependent().toAnnotations()) - ) - - private suspend fun buildAncestryInformation( - kotlinType: KotlinType - ): AncestryNode { - val (interfaces, superclass) = kotlinType.immediateSupertypes().filterNot { it.isAnyOrNullableAny() } - .partition { - val declaration = it.constructor.declarationDescriptor - val descriptor = declaration as? ClassDescriptor - ?: (declaration as? TypeAliasDescriptor)?.underlyingType?.constructor?.declarationDescriptor as? ClassDescriptor - descriptor?.kind == ClassKind.INTERFACE - } - - return coroutineScope { - AncestryNode( - typeConstructor = toTypeConstructor(kotlinType), - superclass = superclass.parallelMap(::buildAncestryInformation).singleOrNull(), - interfaces = interfaces.parallelMap(::buildAncestryInformation) - ) - } - } - - - private suspend fun ClassDescriptor.resolveClassDescriptionData(): ClassInfo { - return coroutineScope { - ClassInfo( - buildAncestryInformation(this@resolveClassDescriptionData.defaultType), - resolveDescriptorData() - ) - } - } - - private suspend fun TypeParameterDescriptor.toVariantTypeParameter() = - DTypeParameter( - variantTypeParameter( - TypeParameter(DRI.from(this), name.identifier, annotations.getPresentableName()) - ), - resolveDescriptorData(), - null, - upperBounds.map { it.toBound() }, - setOf(sourceSet), - extra = PropertyContainer.withAll( - additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - getAnnotations().toSourceSetDependent().toAnnotations() - ) - ) - - private fun org.jetbrains.kotlin.descriptors.annotations.Annotations.getPresentableName(): String? = - mapNotNull { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name") - .safeAs<StringValue>()?.value?.let { unquotedValue(it) } - - private suspend fun KotlinType.toBound(): Bound { - suspend fun <T : AnnotationTarget> annotations(): PropertyContainer<T> = - getAnnotations().takeIf { it.isNotEmpty() }?.let { annotations -> - PropertyContainer.withAll(annotations.toSourceSetDependent().toAnnotations()) - } ?: PropertyContainer.empty() - - return when (this) { - is DynamicType -> Dynamic - is AbbreviatedType -> TypeAliased( - abbreviation.toBound(), - expandedType.toBound(), - annotations() - ) - is DefinitelyNotNullType -> DefinitelyNonNullable( - original.toBound() - ) - else -> when (val ctor = constructor.declarationDescriptor) { - is TypeParameterDescriptor -> TypeParameter( - dri = DRI.from(ctor), - name = ctor.name.asString(), - presentableName = annotations.getPresentableName(), - extra = annotations() - ) - is FunctionClassDescriptor -> FunctionalTypeConstructor( - DRI.from(ctor), - arguments.map { it.toProjection() }, - isExtensionFunction = isExtensionFunctionType || isBuiltinExtensionFunctionalType, - isSuspendable = isSuspendFunctionTypeOrSubtype, - presentableName = annotations.getPresentableName(), - extra = annotations() - ) - else -> GenericTypeConstructor( - DRI.from(ctor!!), // TODO: remove '!!' - arguments.map { it.toProjection() }, - annotations.getPresentableName(), - extra = annotations() - ) - }.let { - if (isMarkedNullable) Nullable(it) else it - } - } - } - - private suspend fun TypeProjection.toProjection(): Projection = - if (isStarProjection) Star else formPossiblyVariant() - - private suspend fun TypeProjection.formPossiblyVariant(): Projection = - type.toBound().wrapWithVariance(projectionKind) - - private fun TypeParameterDescriptor.variantTypeParameter(type: TypeParameter) = - type.wrapWithVariance(variance) - - private fun <T : Bound> T.wrapWithVariance(variance: org.jetbrains.kotlin.types.Variance) = - when (variance) { - org.jetbrains.kotlin.types.Variance.INVARIANT -> Invariance(this) - org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Contravariance(this) - org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Covariance(this) - } - - private fun descriptorToAnyDeclaration(descriptor: DeclarationDescriptor): PsiElement? { - val effectiveReferencedDescriptors = DescriptorToSourceUtils.getEffectiveReferencedDescriptors(descriptor) - //take any - return effectiveReferencedDescriptors.firstOrNull()?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) } - } - - private fun DeclarationDescriptor.getDocumentation() = (findKDoc(::descriptorToAnyDeclaration)?.let { - MarkdownParser.parseFromKDocTag( - kDocTag = it, - externalDri = { link: String -> - try { - resolveKDocLink( - context = resolutionFacade.resolveSession.bindingContext, - resolutionFacade = resolutionFacade, - fromDescriptor = this, - fromSubjectOfTag = null, - qualifiedName = link.split('.') - ).firstOrNull()?.let { DRI.from(it) } - } catch (e1: IllegalArgumentException) { - logger.warn("Couldn't resolve link for $link") - null - } - }, - kdocLocation = toSourceElement.containingFile.name?.let { - val fqName = fqNameOrNull()?.asString() - if (fqName != null) "$it/$fqName" - else it - } - ) - } ?: getJavaDocs())?.takeIf { it.children.isNotEmpty() } - - private fun DeclarationDescriptor.getJavaDocs(): DocumentationNode? { - val overriddenDescriptors = (this as? CallableDescriptor)?.overriddenDescriptors ?: emptyList() - val allDescriptors = overriddenDescriptors + listOf(this) - return allDescriptors - .mapNotNull { it.findPsi() as? PsiNamedElement } - .firstOrNull() - ?.let { javadocParser.parseDocumentation(it) } - } - - private suspend fun ClassDescriptor.companion(dri: DRIWithPlatformInfo): DObject? = companionObjectDescriptor?.let { - 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.createSources(): SourceSetDependent<DocumentableSource> = - DescriptorDocumentableSource(this).toSourceSetDependent() - - private fun FunctionDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.KotlinOnlyModifiers.Infix.takeIf { isInfix }, - ExtraModifiers.KotlinOnlyModifiers.Inline.takeIf { isInline }, - ExtraModifiers.KotlinOnlyModifiers.Suspend.takeIf { isSuspend }, - ExtraModifiers.KotlinOnlyModifiers.Operator.takeIf { isOperator }, - ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isJvmStaticInObjectOrClassOrInterface() }, - ExtraModifiers.KotlinOnlyModifiers.TailRec.takeIf { isTailrec }, - ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal }, - ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { DescriptorUtils.isOverride(this) } - ).toSet() - - private fun ClassDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.KotlinOnlyModifiers.Inline.takeIf { isInline }, - ExtraModifiers.KotlinOnlyModifiers.Value.takeIf { isValue }, - ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal }, - ExtraModifiers.KotlinOnlyModifiers.Inner.takeIf { isInner }, - ExtraModifiers.KotlinOnlyModifiers.Data.takeIf { isData }, - ExtraModifiers.KotlinOnlyModifiers.Fun.takeIf { isFun }, - ).toSet() - - private fun ValueParameterDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.KotlinOnlyModifiers.NoInline.takeIf { isNoinline }, - ExtraModifiers.KotlinOnlyModifiers.CrossInline.takeIf { isCrossinline }, - ExtraModifiers.KotlinOnlyModifiers.Const.takeIf { isConst }, - ExtraModifiers.KotlinOnlyModifiers.LateInit.takeIf { isLateInit }, - ExtraModifiers.KotlinOnlyModifiers.VarArg.takeIf { isVararg } - ).toSet() - - private fun TypeParameterDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.KotlinOnlyModifiers.Reified.takeIf { isReified } - ).toSet() - - private fun PropertyDescriptor.additionalExtras() = listOfNotNull( - ExtraModifiers.KotlinOnlyModifiers.Const.takeIf { isConst }, - ExtraModifiers.KotlinOnlyModifiers.LateInit.takeIf { isLateInit }, - ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isJvmStaticInObjectOrClassOrInterface() }, - ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal }, - ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { DescriptorUtils.isOverride(this) } - ) - - private suspend fun Annotated.getAnnotations() = annotations.parallelMapNotNull { it.toAnnotation() } - - private fun ConstantValue<*>.toValue(): AnnotationParameterValue = when (this) { - is ConstantsAnnotationValue -> AnnotationValue(value.toAnnotation()) - is ConstantsArrayValue -> ArrayValue(value.map { it.toValue() }) - is ConstantsEnumValue -> EnumValue( - fullEnumEntryName(), - DRI(enumClassId.packageFqName.asString(), fullEnumEntryName()) - ) - 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) - ) - } - } - is ConstantsFloatValue -> FloatValue(value) - is ConstantsDoubleValue -> DoubleValue(value) - is ConstantsUIntValue -> IntValue(value) - is ConstantsULongValue -> LongValue(value) - is ConstantsIntValue -> IntValue(value) - is ConstantsLongValue -> LongValue(value) - is ConstantsBooleanValue -> BooleanValue(value) - is ConstantsNullValue -> NullValue - else -> StringValue(unquotedValue(toString())) - } - - private fun AnnotationDescriptor.toAnnotation(scope: Annotations.AnnotationScope = Annotations.AnnotationScope.DIRECT): Annotations.Annotation = - Annotations.Annotation( - DRI.from(annotationClass as DeclarationDescriptor), - allValueArguments.map { it.key.asString() to it.value.toValue() }.toMap(), - mustBeDocumented(), - scope - ) - - private fun AnnotationDescriptor.mustBeDocumented(): Boolean = - if (source.toString() == "NO_SOURCE") false - else annotationClass?.annotations?.hasAnnotation(FqName("kotlin.annotation.MustBeDocumented")) ?: false - - private suspend fun PropertyDescriptor.getAnnotationsWithBackingField(): List<Annotations.Annotation> = - getAnnotations() + (backingField?.getAnnotations() ?: emptyList()) - - private fun List<Annotations.Annotation>.toAdditionalExtras() = mapNotNull { - try { - ExtraModifiers.valueOf(it.dri.classNames?.toLowerCase() ?: "") - } catch (e: IllegalArgumentException) { - null - } - } - - private fun <T : CallableMemberDescriptor> T.getConcreteDescriptor(): T { - return if (kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - this - } else { - @Suppress("UNCHECKED_CAST") - overriddenDescriptors.first().getConcreteDescriptor() as T - } - } - - private fun ValueParameterDescriptor.getDefaultValue(): Expression? = - ((source as? KotlinSourceElement)?.psi as? KtParameter)?.defaultValue?.toDefaultValueExpression() - - private fun PropertyDescriptor.getDefaultValue(): Expression? = - (source as? KotlinSourceElement)?.psi?.children?.filterIsInstance<KtConstantExpression>()?.firstOrNull() - ?.toDefaultValueExpression() - - private fun ClassDescriptor.getAppliedConstructorParameters() = - (source as PsiSourceElement).psi?.children?.flatMap { - it.safeAs<KtInitializerList>()?.initializersAsExpression().orEmpty() - }.orEmpty() - - private fun KtInitializerList.initializersAsExpression() = - initializers.firstIsInstanceOrNull<KtCallElement>() - ?.getValueArgumentsInParentheses() - ?.map { it.getArgumentExpression()?.toDefaultValueExpression() ?: ComplexExpression("") } - .orEmpty() - - private fun KtExpression.toDefaultValueExpression(): Expression? = when (node?.elementType) { - KtNodeTypes.INTEGER_CONSTANT -> parseLong(node?.text)?.let { IntegerConstant(it) } - KtNodeTypes.FLOAT_CONSTANT -> if (node?.text?.toLowerCase()?.endsWith('f') == true) - parseFloat(node?.text)?.let { FloatConstant(it) } - else parseDouble(node?.text)?.let { DoubleConstant(it) } - KtNodeTypes.BOOLEAN_CONSTANT -> BooleanConstant(node?.text == "true") - KtNodeTypes.STRING_TEMPLATE -> StringConstant(node.findChildByType(KtNodeTypes.LITERAL_STRING_TEMPLATE_ENTRY)?.text.orEmpty()) - else -> node?.text?.let { ComplexExpression(it) } - } - - private data class ClassInfo(val ancestry: AncestryNode, val docs: SourceSetDependent<DocumentationNode>) { - val supertypes: List<TypeConstructorWithKind> - get() = listOfNotNull(ancestry.superclass?.let { - it.typeConstructor.let { - TypeConstructorWithKind( - it, - KotlinClassKindTypes.CLASS - ) - } - }) + ancestry.interfaces.map { TypeConstructorWithKind(it.typeConstructor, KotlinClassKindTypes.INTERFACE) } - } - - private fun DescriptorVisibility.toDokkaVisibility(): Visibility = when (this.delegate) { - Visibilities.Public -> KotlinVisibility.Public - Visibilities.Protected -> KotlinVisibility.Protected - Visibilities.Internal -> KotlinVisibility.Internal - Visibilities.Private, Visibilities.PrivateToThis -> KotlinVisibility.Private - JavaVisibilities.ProtectedAndPackage -> KotlinVisibility.Protected - JavaVisibilities.ProtectedStaticVisibility -> KotlinVisibility.Protected - JavaVisibilities.PackageVisibility -> JavaVisibility.Default - else -> KotlinVisibility.Public - } - - 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() - ?.parallelMap { it.toAnnotation(scope = Annotations.AnnotationScope.FILE) } - .orEmpty() - - private fun AncestryNode.exceptionInSupertypesOrNull(): ExceptionInSupertypes? = - typeConstructorsBeingExceptions().takeIf { it.isNotEmpty() }?.let { ExceptionInSupertypes(it.toSourceSetDependent()) } -} - diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt deleted file mode 100644 index 05982301..00000000 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.jetbrains.dokka.base.translators.descriptors - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DClasslike -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered - -class DefaultExternalDocumentablesProvider(context: DokkaContext) : ExternalDocumentablesProvider { - private val analysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis } - - private val translator = context.plugin<DokkaBase>().querySingle { externalClasslikesTranslator } - - override fun findClasslike(dri: DRI, sourceSet: DokkaSourceSet): DClasslike? { - val pkg = dri.packageName?.let { FqName(it) } ?: FqName.ROOT - val names = dri.classNames?.split('.') ?: return null - - val packageDsc = analysis[sourceSet].facade.moduleDescriptor.getPackage(pkg) - val classDsc = names.fold<String, DeclarationDescriptor?>(packageDsc) { dsc, name -> - dsc?.scope?.getDescriptorsFiltered { it.identifier == name } - ?.filterIsInstance<ClassDescriptor>() - ?.firstOrNull() - } - - return (classDsc as? ClassDescriptor)?.let { translator.translateClassDescriptor(it, sourceSet) } - } - - private val DeclarationDescriptor.scope: MemberScope - get() = when (this) { - is PackageViewDescriptor -> memberScope - is ClassDescriptor -> unsubstitutedMemberScope - else -> throw IllegalArgumentException("Unexpected type of descriptor: ${this::class}") - } -}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt b/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt deleted file mode 100644 index 292dbfca..00000000 --- a/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt +++ /dev/null @@ -1,145 +0,0 @@ -package org.jetbrains.dokka.base.translators.descriptors - -import org.jetbrains.dokka.base.translators.firstNotNullOfOrNull -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.load.java.JvmAbi -import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor -import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName -import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName - -internal data class DescriptorFunctionsHolder( - val regularFunctions: List<FunctionDescriptor>, - val accessors: Map<PropertyDescriptor, DescriptorAccessorHolder> -) - -internal data class DescriptorAccessorHolder( - val getter: FunctionDescriptor? = null, - val setter: FunctionDescriptor? = null -) - -/** - * Separate regular Kotlin/Java functions and inherited Java accessors - * to properly display properties inherited from Java. - * - * Take this example: - * ``` - * // java - * public class JavaClass { - * private int a = 1; - * public int getA() { return a; } - * public void setA(int a) { this.a = a; } - * } - * - * // kotlin - * class Bar : JavaClass() { - * fun foo() {} - * } - * ``` - * - * It should result in: - * - 1 regular function `foo` - * - Map a=[`getA`, `setA`] - */ -internal fun splitFunctionsAndInheritedAccessors( - properties: List<PropertyDescriptor>, - functions: List<FunctionDescriptor> -): DescriptorFunctionsHolder { - val (javaMethods, kotlinFunctions) = functions.partition { it is JavaMethodDescriptor } - if (javaMethods.isEmpty()) { - return DescriptorFunctionsHolder(regularFunctions = kotlinFunctions, emptyMap()) - } - - val propertiesByName = properties.associateBy { it.name.asString() } - val regularFunctions = ArrayList<FunctionDescriptor>(kotlinFunctions) - - val accessors = mutableMapOf<PropertyDescriptor, DescriptorAccessorHolder>() - javaMethods.forEach { function -> - val possiblePropertyNamesForFunction = function.toPossiblePropertyNames() - val property = possiblePropertyNamesForFunction.firstNotNullOfOrNull { propertiesByName[it] } - if (property != null && function.isAccessorFor(property)) { - accessors.compute(property) { prop, accessorHolder -> - if (function.isGetterFor(prop)) - accessorHolder?.copy(getter = function) ?: DescriptorAccessorHolder(getter = function) - else - accessorHolder?.copy(setter = function) ?: DescriptorAccessorHolder(setter = function) - } - } else { - regularFunctions.add(function) - } - } - - val accessorLookalikes = removeNonAccessorsReturning(accessors) - regularFunctions.addAll(accessorLookalikes) - - return DescriptorFunctionsHolder(regularFunctions, accessors) -} - -/** - * If a field has no getter, it's not accessible as a property from Kotlin's perspective, - * but it still might have a setter lookalike. In this case, this "setter" should be just a regular function - * - * @return removed elements - */ -private fun removeNonAccessorsReturning( - propertyAccessors: MutableMap<PropertyDescriptor, DescriptorAccessorHolder> -): List<FunctionDescriptor> { - val nonAccessors = mutableListOf<FunctionDescriptor>() - propertyAccessors.entries.removeIf { (_, accessors) -> - if (accessors.getter == null && accessors.setter != null) { - nonAccessors.add(accessors.setter) - true - } else { - false - } - } - return nonAccessors -} - -private fun FunctionDescriptor.toPossiblePropertyNames(): List<String> { - val stringName = this.name.asString() - return when { - JvmAbi.isSetterName(stringName) -> propertyNamesBySetMethodName(this.name).map { it.asString() } - JvmAbi.isGetterName(stringName) -> propertyNamesByGetMethod(this) - else -> listOf() - } -} - -private fun propertyNamesByGetMethod(functionDescriptor: FunctionDescriptor): List<String> { - val stringName = functionDescriptor.name.asString() - // In java, the convention for boolean property accessors is as follows: - // - `private boolean active;` - // - `private boolean isActive();` - // - // Whereas in Kotlin, because there are no explicit accessors, the convention is - // - `val isActive: Boolean` - // - // This makes it difficult to guess the name of the accessor property in case of Java - val javaPropName = if (functionDescriptor is JavaMethodDescriptor && JvmAbi.startsWithIsPrefix(stringName)) { - val javaPropName = stringName.removePrefix("is").let { newName -> - newName.replaceFirst(newName[0], newName[0].toLowerCase()) - } - javaPropName - } else { - null - } - val kotlinPropName = propertyNameByGetMethodName(functionDescriptor.name)?.asString() - return listOfNotNull(javaPropName, kotlinPropName) -} - -private fun FunctionDescriptor.isAccessorFor(property: PropertyDescriptor): Boolean { - return (this.isGetterFor(property) || this.isSetterFor(property)) - && !property.visibility.isPublicAPI - && this.visibility.isPublicAPI -} - -private fun FunctionDescriptor.isGetterFor(property: PropertyDescriptor): Boolean { - return this.returnType == property.returnType - && this.valueParameters.isEmpty() -} - -private fun FunctionDescriptor.isSetterFor(property: PropertyDescriptor): Boolean { - return this.valueParameters.size == 1 - && this.valueParameters[0].type == property.returnType -} - diff --git a/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt deleted file mode 100644 index a5385c46..00000000 --- a/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.dokka.base.translators.descriptors - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.model.DClasslike -import org.jetbrains.kotlin.descriptors.ClassDescriptor - -/** - * Service translating [ClassDescriptor]s of symbols defined outside of documented project to [DClasslike]s. - */ -fun interface ExternalClasslikesTranslator { - fun translateClassDescriptor(descriptor: ClassDescriptor, sourceSet: DokkaSourceSet): DClasslike -}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt b/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt deleted file mode 100644 index e6d499f4..00000000 --- a/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.jetbrains.dokka.base.translators.descriptors - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DClasslike - -/** - * Service that can be queried with [DRI] and source set to obtain a documentable for classlike. - * - * There are some cases when there is a need to process documentables of classlikes that were not defined - * in the project itself but are somehow related to the symbols defined in the documented project (e.g. are supertypes - * of classes defined in project). - */ -fun interface ExternalDocumentablesProvider { - - /** - * Returns [DClasslike] matching provided [DRI] in specified source set. - * - * Result is null if compiler haven't generated matching class descriptor. - */ - fun findClasslike(dri: DRI, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike? -}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt b/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt deleted file mode 100644 index c96b888a..00000000 --- a/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.jetbrains.dokka.base.translators.descriptors - -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.from -import org.jetbrains.dokka.base.parsers.MarkdownParser -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.DocumentationNode -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.resolve.DescriptorFactory - -private const val ENUM_VALUEOF_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValueOf.kt.template" -private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValues.kt.template" - - internal class SyntheticDescriptorDocumentationProvider( - private val resolutionFacade: DokkaResolutionFacade -) { - fun isDocumented(descriptor: DeclarationDescriptor): Boolean = descriptor is FunctionDescriptor - && (DescriptorFactory.isEnumValuesMethod(descriptor) || DescriptorFactory.isEnumValueOfMethod(descriptor)) - - fun getDocumentation(descriptor: DeclarationDescriptor): DocumentationNode? { - val function = descriptor as? FunctionDescriptor ?: return null - return when { - DescriptorFactory.isEnumValuesMethod(function) -> loadTemplate(descriptor, ENUM_VALUES_TEMPLATE_PATH) - DescriptorFactory.isEnumValueOfMethod(function) -> loadTemplate(descriptor, ENUM_VALUEOF_TEMPLATE_PATH) - else -> null - } - } - - private fun loadTemplate(descriptor: DeclarationDescriptor, filePath: String): DocumentationNode? { - val kdoc = loadContent(filePath) ?: return null - val parser = MarkdownParser({ link -> resolveLink(descriptor, link)}, filePath) - return parser.parse(kdoc) - } - - private fun loadContent(filePath: String): String? = javaClass.getResource(filePath)?.readText() - - private fun resolveLink(descriptor: DeclarationDescriptor, link: String): DRI? = - resolveKDocLink( - resolutionFacade.resolveSession.bindingContext, - resolutionFacade, - descriptor, - null, - link.split('.') - ).firstOrNull()?.let { DRI.from(it) } -} |