Skip to content

Commit

Permalink
[K2] Fix functional type and improve logging for unresolved link (#3157)
Browse files Browse the repository at this point in the history
* [K2] Fix functional type and improve logging for unresolved link

For example
`typealias CompletionHandler = (cause: Throwable?) -> Unit`
has a functional type with no type arguments in K2. In K1 we have a usual generic type
  • Loading branch information
vmishenev authored Sep 4, 2023
1 parent 8065a0d commit e9e8fbf
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.utilities.cast
import kotlin.test.Test
import kotlin.test.assertEquals
import utils.OnlyDescriptors
import utils.UsingJDK

class ExternalDocumentablesTest : BaseAbstractTest() {
Expand Down Expand Up @@ -61,10 +60,6 @@ class ExternalDocumentablesTest : BaseAbstractTest() {
}
}


// typealias CompletionHandler = (cause: Throwable?) -> Unit
// FunctionalTypeConstructor(dri=kotlinx.coroutines/CompletionHandler///PointingToDeclaration/, projections=[], isExtensionFunction=false, isSuspendable=false, presentableName=null, extra=PropertyContainer(map={}))
@OnlyDescriptors(reason = "FunctionType has not parameters") // TODO
@Test
fun `external documentable from dependency`() {
val coroutinesPath =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal fun KtAnalysisSession.getKDocDocumentationFrom(symbol: KtSymbol, logger

parseFromKDocTag(
kDocTag = kDocContent.contentTag,
externalDri = { link -> resolveKDocLink(link).logIfNotResolved(link.getLinkText(), logger) },
externalDri = { link -> resolveKDocLink(link).ifUnresolved { logger.logUnresolvedLink(link.getLinkText(), kdocLocation) } },
kdocLocation = kdocLocation
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,24 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
import org.jetbrains.kotlin.psi.KtPsiFactory

internal fun DRI?.logIfNotResolved(link: String, logger: DokkaLogger): DRI? {
if(this == null)
logger.warn("Couldn't resolve link for $link")
return this
/**
* Util to print a message about unresolved [link]
*/
internal fun DokkaLogger.logUnresolvedLink(link: String, location: String?) {
warn("Couldn't resolve link for $link" + if (location != null) " in $location" else "")
}

internal inline fun DRI?.ifUnresolved(action: () -> Unit): DRI? = this ?: run {
action()
null
}

/**
* It resolves KDoc link via creating PSI.
* Resolves KDoc link via creating PSI.
* If the [link] is ambiguous, i.e. leads to more than one declaration,
* it returns deterministically any declaration.
*
* @return [DRI] or null if the [link] is unresolved
*/
internal fun KtAnalysisSession.resolveKDocTextLink(link: String, context: PsiElement? = null): DRI? {
val psiFactory = context?.let { KtPsiFactory.contextual(it) } ?: KtPsiFactory(this.useSiteModule.project)
Expand All @@ -38,9 +47,15 @@ internal fun KtAnalysisSession.resolveKDocTextLink(link: String, context: PsiEle
return kDocLink?.let { resolveKDocLink(it) }
}

/**
* If the [link] is ambiguous, i.e. leads to more than one declaration,
* it returns deterministically any declaration.
*
* @return [DRI] or null if the [link] is unresolved
*/
internal fun KtAnalysisSession.resolveKDocLink(link: KDocLink): DRI? {
val lastNameSegment = link.children.filterIsInstance<KDocName>().lastOrNull()
val linkedSymbol = lastNameSegment?.mainReference?.resolveToSymbols()?.firstOrNull()
return if (linkedSymbol == null) null // logger.warn("Couldn't resolve link for $link")
return if (linkedSymbol == null) null
else getDRIFromSymbol(linkedSymbol)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import com.intellij.psi.PsiNamedElement
import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.analysis.java.doccomment.DocComment
import org.jetbrains.dokka.analysis.java.parsers.DocCommentParser
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.logIfNotResolved
import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.*
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.logUnresolvedLink
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.parseFromKDocTag
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.resolveKDocLink
import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.SymbolsAnalysisPlugin
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
Expand All @@ -37,10 +38,11 @@ internal class KotlinDocCommentParser(
?: sourceSets.first { it.analysisPlatform == Platform.jvm }
}
val kotlinAnalysis = context.plugin<SymbolsAnalysisPlugin>().querySingle { kotlinAnalysis }
val elementName = element.resolveDocContext.ktElement.name
return analyze(kotlinAnalysis[sourceSet].mainModule) {
parseFromKDocTag(
kDocTag = element.comment,
externalDri = { link -> resolveKDocLink(link).logIfNotResolved(link.getLinkText(), context.logger) },
externalDri = { link -> resolveKDocLink(link).ifUnresolved { context.logger.logUnresolvedLink(link.getLinkText(), elementName) } },
kdocLocation = null,
parseWithChildren = parseWithChildren
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
package org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs

import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.logIfNotResolved
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.ifUnresolved
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.logUnresolvedLink
import org.jetbrains.dokka.analysis.kotlin.symbols.plugin.KotlinAnalysis
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentation.Classifier.Module
import org.jetbrains.dokka.analysis.kotlin.symbols.kdoc.moduledocs.ModuleAndPackageDocumentation.Classifier.Package
Expand All @@ -32,20 +33,31 @@ internal fun ModuleAndPackageDocumentationParsingContext(
sourceSet: DokkaConfiguration.DokkaSourceSet? = null
) = ModuleAndPackageDocumentationParsingContext { fragment, sourceLocation ->

if(kotlinAnalysis == null || sourceSet == null) {
if (kotlinAnalysis == null || sourceSet == null) {
MarkdownParser(externalDri = { null }, sourceLocation)
} else {
val analysisContext = kotlinAnalysis[sourceSet]
analyze(analysisContext.mainModule) {
val contextPsi = analyze(analysisContext.mainModule) {
val contextSymbol = when (fragment.classifier) {
Module -> ROOT_PACKAGE_SYMBOL
Package -> getPackageSymbolIfPackageExists(FqName(fragment.name))
}

MarkdownParser(
externalDri = { resolveKDocTextLink(it, contextSymbol?.psi).logIfNotResolved(it, logger) },
sourceLocation
)
contextSymbol?.psi
}
MarkdownParser(
externalDri = { link ->
analyze(analysisContext.mainModule) {
resolveKDocTextLink(
link,
contextPsi
).ifUnresolved {
logger.logUnresolvedLink(link, fragment.name.ifBlank { "module documentation" })
}

}
},
sourceLocation
)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ internal class AnnotationTranslator {
DRI(packageName = "", classNames = ERROR_CLASS_NAME)
)

KtUnsupportedAnnotationValue -> TODO()
KtUnsupportedAnnotationValue -> ClassValue(
"<Unsupported Annotation Value>",
DRI(packageName = "", classNames = ERROR_CLASS_NAME)
)
}

private fun getDRIFrom(enumEntry: KtEnumEntryAnnotationValue): DRI {
Expand All @@ -129,6 +132,7 @@ internal class AnnotationTranslator {

/**
* Functional types can have **generated** [ParameterName] annotation
* @see ParameterName
*/
internal fun KtAnnotated.getPresentableName(): String? =
this.annotationsByClassId(parameterNameAnnotation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ internal class TypeTranslator(
throw IllegalStateException("Expected type alias symbol in type")
}

private fun KtAnalysisSession.toTypeConstructorFrom(classType: KtUsualClassType) =
private fun KtAnalysisSession.toTypeConstructorFrom(classType: KtNonErrorClassType) =
GenericTypeConstructor(
dri = getDRIFromNonErrorClassType(classType),
projections = classType.ownTypeArguments.map { toProjection(it) },
Expand Down Expand Up @@ -91,7 +91,18 @@ internal class TypeTranslator(
)

is KtClassErrorType -> UnresolvedBound(type.toString())
is KtFunctionalType -> toFunctionalTypeConstructorFrom(type)
is KtFunctionalType -> {
/**
* For example
* `typealias CompletionHandler = (cause: Throwable?) -> Unit`
* has functional type with no type arguments in K2
* In K1 we have a usual generic type
*/
if (type.ownTypeArguments.isEmpty())
toTypeConstructorFrom(type)
else
toFunctionalTypeConstructorFrom(type)
}
is KtDynamicType -> Dynamic
is KtDefinitelyNotNullType -> DefinitelyNonNullable(
toBoundFrom(type.original)
Expand Down

0 comments on commit e9e8fbf

Please sign in to comment.