From e9e8fbf59efc5eefdaef2a432dbdef299a89fde1 Mon Sep 17 00:00:00 2001 From: Vadim Mishenev Date: Mon, 4 Sep 2023 17:18:55 +0300 Subject: [PATCH] [K2] Fix functional type and improve logging for unresolved link (#3157) * [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 --- .../translators/ExternalDocumentablesTest.kt | 5 ---- .../kotlin/symbols/kdoc/KDocProvider.kt | 2 +- .../kotlin/symbols/kdoc/ResolveKDocLink.kt | 27 ++++++++++++++---- .../kdoc/java/KotlinDocCommentParser.kt | 8 ++++-- ...leAndPackageDocumentationParsingContext.kt | 28 +++++++++++++------ .../translators/AnnotationTranslator.kt | 6 +++- .../symbols/translators/TypeTranslator.kt | 15 ++++++++-- 7 files changed, 65 insertions(+), 26 deletions(-) diff --git a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt index 198ffe8ea6..5dd62545d7 100644 --- a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt +++ b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt @@ -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() { @@ -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 = diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt index 9dd6857e46..d8a4e4769c 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/KDocProvider.kt @@ -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 ) } diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt index be06d595e6..9a0b81bd5a 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/ResolveKDocLink.kt @@ -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) @@ -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().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) } diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt index 7e2e2a595f..33cc43053f 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/java/KotlinDocCommentParser.kt @@ -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 @@ -37,10 +38,11 @@ internal class KotlinDocCommentParser( ?: sourceSets.first { it.analysisPlatform == Platform.jvm } } val kotlinAnalysis = context.plugin().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 ) diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt index 9b748da284..ef59aa339b 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/kdoc/moduledocs/ModuleAndPackageDocumentationParsingContext.kt @@ -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 @@ -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 + ) + } } diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt index db2d14b8d8..faae08e205 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/AnnotationTranslator.kt @@ -111,7 +111,10 @@ internal class AnnotationTranslator { DRI(packageName = "", classNames = ERROR_CLASS_NAME) ) - KtUnsupportedAnnotationValue -> TODO() + KtUnsupportedAnnotationValue -> ClassValue( + "", + DRI(packageName = "", classNames = ERROR_CLASS_NAME) + ) } private fun getDRIFrom(enumEntry: KtEnumEntryAnnotationValue): DRI { @@ -129,6 +132,7 @@ internal class AnnotationTranslator { /** * Functional types can have **generated** [ParameterName] annotation + * @see ParameterName */ internal fun KtAnnotated.getPresentableName(): String? = this.annotationsByClassId(parameterNameAnnotation) diff --git a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt index 66c699f7cd..7127cbdf3d 100644 --- a/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt +++ b/subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/TypeTranslator.kt @@ -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) }, @@ -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)