Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[K2] Support [this] link for extensions #3445

Merged
merged 4 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ class SerializationGradleIntegrationTest : AbstractGradleIntegrationTest(), Test
copyAndApplyGitDiff(File("projects", "serialization/serialization.diff"))
}

@OnlyDescriptors // failed due to https://github.com/Kotlin/dokka/issues/3207
@ParameterizedTest(name = "{0}")
@ArgumentsSource(SerializationBuildVersionsArgumentsProvider::class)
fun execute(buildVersions: BuildVersions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithTypeParameters
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
import org.jetbrains.kotlin.analysis.api.types.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
Expand Down Expand Up @@ -93,6 +93,28 @@ internal fun KtAnalysisSession.getDRIFromValueParameter(symbol: KtValueParameter
return funDRI.copy(target = PointingToCallableParameters(index))
}

/**
* @return [DRI] to receiver type
*/
internal fun KtAnalysisSession.getDRIFromReceiverParameter(receiverParameterSymbol: KtReceiverParameterSymbol): DRI =
getDRIFromReceiverType(receiverParameterSymbol.type)

private fun KtAnalysisSession.getDRIFromReceiverType(type: KtType): DRI {
return when(type) {
is KtNonErrorClassType -> getDRIFromNonErrorClassType(type)
is KtTypeParameterType -> getDRIFromTypeParameter(type.symbol)
is KtDefinitelyNotNullType -> getDRIFromReceiverType(type.original)
is KtTypeErrorType -> DRI(packageName = "", classNames = "$ERROR_CLASS_NAME $type")
is KtClassErrorType -> DRI(packageName = "", classNames = "$ERROR_CLASS_NAME $type")
is KtDynamicType -> DRI(packageName = "", classNames = "$ERROR_CLASS_NAME $type") // prohibited by a compiler, but it's a possible input

is KtCapturedType -> throw IllegalStateException("Unexpected non-denotable type while creating DRI $type")
is KtFlexibleType -> throw IllegalStateException("Unexpected non-denotable type while creating DRI $type")
is KtIntegerLiteralType -> throw IllegalStateException("Unexpected non-denotable type while creating DRI $type")
is KtIntersectionType -> throw IllegalStateException("Unexpected non-denotable type while creating DRI $type")
}
}

internal fun KtAnalysisSession.getDRIFromSymbol(symbol: KtSymbol): DRI =
when (symbol) {
is KtEnumEntrySymbol -> getDRIFromEnumEntry(symbol)
Expand All @@ -103,6 +125,7 @@ internal fun KtAnalysisSession.getDRIFromSymbol(symbol: KtSymbol): DRI =
is KtFunctionLikeSymbol -> getDRIFromFunctionLike(symbol)
is KtClassLikeSymbol -> getDRIFromClassLike(symbol)
is KtPackageSymbol -> getDRIFromPackage(symbol)
is KtReceiverParameterSymbol -> getDRIFromReceiverParameter(symbol)
else -> throw IllegalStateException("Unknown symbol while creating DRI $symbol")
}

Expand Down
134 changes: 134 additions & 0 deletions dokka-subprojects/plugin-base/src/test/kotlin/markdown/LinkTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ package markdown
import org.jetbrains.dokka.analysis.kotlin.markdown.MARKDOWN_ELEMENT_FILE_NAME
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.model.DFunction
import org.jetbrains.dokka.model.WithGenerics
import org.jetbrains.dokka.model.dfs
import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.pages.ClasslikePageNode
import org.jetbrains.dokka.pages.ContentDRILink
import org.jetbrains.dokka.pages.MemberPageNode
import utils.OnlySymbols
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
Expand Down Expand Up @@ -121,6 +123,138 @@ class LinkTest : BaseAbstractTest() {
}
}

@Test
@OnlySymbols("#3207 - In Dokka K1 [this] has an incorrect link that leads to a page of containing package")
fun `link to this keyword with receiver to some class`() {
val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/")
}
}
}

testInline(
"""
|/src/main/kotlin/Test.kt
|package example
|
|/**
|* Link to [this]
|*/
|fun String.stop() {}
""".trimMargin(),
configuration
) {
documentablesMergingStage = { module ->
val fn = module.dfs { it.name == "stop" } as DFunction
val link = fn.documentation.values.single()
.dfs { it is DocumentationLink } as DocumentationLink

assertEquals(DRI("kotlin", "String"), link.dri)
}
}
}

@Test
@OnlySymbols("#3207 In Dokka K1 [this] has an incorrect link that leads to a page of containing package")
fun `link to this keyword with receiver to type parameter`() {
val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/")
}
}
}

testInline(
"""
|/src/main/kotlin/Test.kt
|package example
|
|/**
|* Link to [this]
|*/
|fun <T> T.stop() {}
""".trimMargin(),
configuration
) {
documentablesMergingStage = { module ->
val fn = module.dfs { it.name == "stop" } as DFunction
val link = fn.documentation.values.single()
.dfs { it is DocumentationLink } as DocumentationLink

assertEquals(fn.generics.first().dri, link.dri)
}
}
}

@Test
@OnlySymbols("#3207 In Dokka K1 [this] has an incorrect link that leads to a page of containing package")
fun `link to this keyword with receiver of dynamic that is prohibited by compiler`() {
val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/")
}
}
}

testInline(
"""
|/src/main/kotlin/Test.kt
|package example
|
|/**
|* Link to [this]
|*/
|fun dynamic.stop() {}
""".trimMargin(),
configuration
) {
documentablesMergingStage = { module ->
val fn = module.dfs { it.name == "stop" } as DFunction
val link = fn.documentation.values.single()
.dfs { it is DocumentationLink } as DocumentationLink

assertEquals(DRI(packageName = "", classNames = "<ERROR CLASS> dynamic"), link.dri)
}
}
}

@Test
@OnlySymbols("#3207 In Dokka K1 [this] has an incorrect link that leads to a page of containing package")
fun `link to this keyword with receiver of DNN-type`() {
val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/")
}
}
}

testInline(
"""
|/src/main/kotlin/Test.kt
|package example
|
|/**
|* Link to [this]
|*/
|fun <T> (T&Any).stop() {}
""".trimMargin(),
configuration
) {
documentablesMergingStage = { module ->
val fn = module.dfs { it.name == "stop" } as DFunction
val link = fn.documentation.values.single()
.dfs { it is DocumentationLink } as DocumentationLink

assertEquals(fn.generics.first().dri, link.dri)
}
}
}

@Test
fun `link with exclamation mark`() {
val configuration = dokkaConfiguration {
Expand Down
Loading