add Initial support for comments in SIR #KT-65174 Fixed

Merge-request: KT-MR-14007
Merged-by: Artem Olkov <artem.olkov@jetbrains.com>
This commit is contained in:
Artem Olkov
2024-01-29 14:22:42 +00:00
committed by Space Team
parent bb6f466162
commit b317a88536
19 changed files with 151 additions and 12 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.sir.analysisapi.transformers
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.sir.*
@@ -39,6 +40,9 @@ private class AAFunction(
AAKotlinType(name = function.returnType.toString())
}
override val documentation: SirKotlinOrigin.Documentation?
get() = originalFunction.docComment?.let { AADocumentation(it) }
}
private data class AAParameter(
override val name: String,
@@ -48,3 +52,10 @@ private data class AAParameter(
private data class AAKotlinType(
override val name: String
) : SirKotlinOrigin.Type
private data class AADocumentation(
private val kdoc: KDoc
) : SirKotlinOrigin.Documentation {
override val content: String
get() = kdoc.text
}
@@ -0,0 +1,8 @@
/**
* Function foo description
*
* @param p first Integer to consume
* @param p2 second Double to consume
* @return Short, constant 1
*/
fun foo(p: Int, p2: Double): Short = 1
@@ -0,0 +1 @@
[foo] commented
@@ -24,6 +24,12 @@ public class SirAnalysisGeneratedTests extends AbstractKotlinSirContextTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/swift/sir-analysis-api/testData"), Pattern.compile("^(.+)\\.kt$"), null, false);
}
@Test
@TestMetadata("function_with_comment.kt")
public void testFunction_with_comment() throws Exception {
runTest("native/swift/sir-analysis-api/testData/function_with_comment.kt");
}
@Test
@TestMetadata("functions_overload.kt")
public void testFunctions_overload() throws Exception {
@@ -43,9 +43,11 @@ abstract class AbstractKotlinSirContextTestBase : AbstractAnalysisApiBasedTest()
val actual = buildString {
module.declarations
.filterIsInstance<SirForeignFunction>()
.forEach {
val function = it.origin as SirKotlinOrigin.Function
appendLine("${function.path}")
.forEach { sirForeignFunction ->
val function = sirForeignFunction.origin as SirKotlinOrigin.Function
val functionPath = "${function.path}"
val isCommented = function.documentation?.let { " commented" } ?: ""
appendLine("$functionPath$isCommented")
}
}
@@ -41,6 +41,8 @@ public class ForeignIntoSwiftFunctionTranslationPass : SirPass<SirElement, Nothi
kotlinOrigin.parameters.mapTo(parameters) { it.toSir() }
returnType = kotlinOrigin.returnType.toSir()
documentation = kotlinOrigin.documentation?.content
}.apply {
parent = function.parent
}
@@ -6,14 +6,10 @@
package org.jetbrains.kotlin.sir.passes
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.sir.SirFunction
import org.jetbrains.kotlin.sir.SirNominalType
import org.jetbrains.kotlin.sir.SirParameter
import org.jetbrains.kotlin.sir.SirVisibility
import org.jetbrains.kotlin.sir.builder.buildEnum
import org.jetbrains.kotlin.sir.builder.buildForeignFunction
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.*
import org.jetbrains.kotlin.sir.constants.*
import org.jetbrains.kotlin.sir.mock.MockDocumentation
import org.jetbrains.kotlin.sir.mock.MockFunction
import org.jetbrains.kotlin.sir.mock.MockKotlinType
import org.jetbrains.kotlin.sir.mock.MockParameter
@@ -149,4 +145,51 @@ class SirPassTests {
)
assertSirFunctionsEquals(actual = result, expected = exp)
}
@Test
fun `foreign toplevel function with kdoc should be translated`() {
val module = buildModule {
name = "demo"
}
val mySirElement = buildForeignFunction {
origin = MockFunction(
fqName = FqName.fromSegments(listOf("foo")),
parameters = emptyList(),
returnType = MockKotlinType(BOOLEAN),
documentation = MockDocumentation(
"""
/**
* Function foo description
*
* @param p first Integer to consume
* @param p2 second Double to consume
* @return empty String
*/
""".trimIndent()
)
)
visibility = SirVisibility.PUBLIC
}
mySirElement.parent = module
val myPass = ForeignIntoSwiftFunctionTranslationPass()
val result = myPass.runWithAsserts(mySirElement, null) as? SirFunction
assertNotNull(result, "SirFunction should be produced")
val exp = MockSirFunction(
name = "foo",
parameters = emptyList(),
returnType = SirNominalType(SirSwiftModule.bool),
parent = module,
isStatic = false,
documentation = """
/**
* Function foo description
*
* @param p first Integer to consume
* @param p2 second Double to consume
* @return empty String
*/
""".trimIndent()
)
assertSirFunctionsEquals(actual = result, expected = exp)
}
}
@@ -29,4 +29,8 @@ fun assertSirFunctionsEquals(expected: SirFunction, actual: SirFunction) {
actual = actual.isStatic,
expected = expected.isStatic
)
assertEquals(
actual = actual.documentation,
expected = expected.documentation
)
}
@@ -18,6 +18,7 @@ class MockSirFunction(
override val name: String,
override val parameters: List<SirParameter>,
override val returnType: SirType,
override var documentation: String? = null,
) : SirFunction() {
override fun <R, D> acceptChildren(visitor: SirVisitor<R, D>, data: D) = Unit
override fun <D> transformChildren(transformer: SirTransformer<D>, data: D) = Unit
@@ -29,6 +29,7 @@ public class SirAsSwiftSourcesPrinter(private val printer: SmartPrinter) : SirVi
}
override fun visitFunction(function: SirFunction): Unit = with(printer) {
function.documentation?.let { println(it) }
print(
function.visibility.takeIf { it != SirVisibility.INTERNAL }?.let { "${it.swift} " } ?: "",
if (function.isStatic) { "static " } else { "" },
@@ -0,0 +1,10 @@
/// Function foo description.
/// - Parameters:
/// - p: first Integer to consume
/// - Returns: Bool
public func foo(
p: Swift.Int64
) -> Swift.Bool {
fatalError()
}
@@ -255,6 +255,40 @@ class SirAsSwiftSourcesPrinterTests {
)
}
@Test
fun `should print DocC comment`() {
val module = buildModule {
name = "Test"
declarations.add(
buildFunction {
origin = SirOrigin.Unknown
isStatic = false
visibility = SirVisibility.PUBLIC
name = "foo"
parameters.add(
SirParameter(
argumentName = "p",
type = SirNominalType(SirSwiftModule.int64)
)
)
returnType = SirNominalType(SirSwiftModule.bool)
documentation = """
/// Function foo description.
/// - Parameters:
/// - p: first Integer to consume
/// - Returns: Bool
""".trimIndent()
}
)
}
runTest(
module,
"testData/commented_function"
)
}
private fun runTest(module: SirModule, goldenDataFile: String) {
val expectedSwiftSrc = File(KtTestUtil.getHomeDirectory()).resolve("$goldenDataFile.golden.swift")
@@ -23,6 +23,7 @@ abstract class SirFunction : SirElementBase(), SirCallable {
abstract val parameters: List<SirParameter>
abstract val returnType: SirType
abstract var body: SirFunctionBody?
abstract var documentation: String?
override fun <R, D> accept(visitor: SirVisitor<R, D>, data: D): R =
visitor.visitFunction(this, data)
@@ -23,6 +23,7 @@ class SirFunctionBuilder {
val parameters: MutableList<SirParameter> = mutableListOf()
lateinit var returnType: SirType
var body: SirFunctionBody? = null
var documentation: String? = null
fun build(): SirFunction {
return SirFunctionImpl(
@@ -33,6 +34,7 @@ class SirFunctionBuilder {
parameters,
returnType,
body,
documentation,
)
}
@@ -59,5 +61,6 @@ inline fun buildFunctionCopy(original: SirFunction, init: SirFunctionBuilder.()
copyBuilder.parameters.addAll(original.parameters)
copyBuilder.returnType = original.returnType
copyBuilder.body = original.body
copyBuilder.documentation = original.documentation
return copyBuilder.apply(init).build()
}
@@ -22,6 +22,7 @@ internal class SirFunctionImpl(
override val parameters: MutableList<SirParameter>,
override val returnType: SirType,
override var body: SirFunctionBody?,
override var documentation: String?,
) : SirFunction() {
override lateinit var parent: SirDeclarationParent
@@ -14,6 +14,7 @@ sealed interface SirKotlinOrigin : SirOrigin.Foreign {
get() = fqName.pathSegments().map { it.asString() }
interface Function : SirKotlinOrigin {
val documentation: Documentation?
val parameters: List<Parameter>
val returnType: Type
}
@@ -26,4 +27,8 @@ sealed interface SirKotlinOrigin : SirOrigin.Foreign {
interface Type {
val name: String
}
interface Documentation {
val content: String
}
}
@@ -7,11 +7,13 @@ package org.jetbrains.kotlin.sir.mock
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.sir.SirKotlinOrigin
import org.jetbrains.kotlin.sir.SirOrigin
data class MockFunction(
override val fqName: FqName,
override val parameters: List<SirKotlinOrigin.Parameter>,
override val returnType: SirKotlinOrigin.Type,
override val documentation: SirKotlinOrigin.Documentation? = null,
) : SirKotlinOrigin.Function
data class MockParameter(
@@ -22,3 +24,5 @@ data class MockParameter(
data class MockKotlinType(
override val name: String
) : SirKotlinOrigin.Type
data class MockDocumentation(override val content: String) : SirKotlinOrigin.Documentation
@@ -17,7 +17,7 @@ typealias Model = org.jetbrains.kotlin.generators.tree.Model<Element>
fun main(args: Array<String>) {
val generationPath = args.firstOrNull()?.let { File(it) }
?: File("../../tree/gen").canonicalFile
?: File("./native/swift/sir/gen/").canonicalFile
val model = SwiftIrTree.build()
generateTree(
generationPath,
@@ -87,6 +87,8 @@ object SwiftIrTree : AbstractSwiftIrTreeBuilder() {
+listField("parameters", parameterType)
+field("returnType", typeType)
+field("body", functionBodyType, nullable = true, mutable = true)
+field(name = "documentation", string, nullable = true, mutable = true)
}
val foreignFunction by element {
@@ -96,4 +98,4 @@ object SwiftIrTree : AbstractSwiftIrTreeBuilder() {
visitorParameterName = "function"
}
}
}