Add SIR builder and printer for classes #KT-65905 fixed

Merge-request: KT-MR-14478
Merged-by: Artem Olkov <artem.olkov@jetbrains.com>
This commit is contained in:
Artem Olkov
2024-02-26 14:30:07 +00:00
committed by Space Team
parent febac0dd5f
commit e4acb396ba
22 changed files with 366 additions and 9 deletions
@@ -6,15 +6,15 @@
package org.jetbrains.sir.passes.builder
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtNamedClassOrObjectSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isPublic
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildFunction
import org.jetbrains.kotlin.sir.builder.buildGetter
import org.jetbrains.kotlin.sir.builder.buildSetter
import org.jetbrains.kotlin.sir.builder.buildVariable
import org.jetbrains.kotlin.sir.builder.*
import org.jetbrains.kotlin.sir.util.SirSwiftModule
@@ -28,6 +28,17 @@ private class Visitor(
private val res: MutableList<SirDeclaration>,
private val analysisSession: KtAnalysisSession
) : KtTreeVisitorVoid() {
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
// we do not handle inner declarations of class currently. No need to go deeper.
// super.visitClassOrObject(classOrObject)
with(analysisSession) {
classOrObject.process {
buildSirClassFromPsi(classOrObject)
}
}
}
override fun visitNamedFunction(function: KtNamedFunction) {
super.visitNamedFunction(function)
with(analysisSession) {
@@ -46,13 +57,25 @@ private class Visitor(
}
}
private inline fun <T : KtDeclaration> T.process(converter: T.() -> SirDeclaration) {
private inline fun <T : KtDeclaration> T.process(converter: T.() -> SirDeclaration?) {
this.takeIf { it.isPublic }
?.let(converter)
?.let { res.add(it) }
}
}
context(KtAnalysisSession)
internal fun buildSirClassFromPsi(classOrObject: KtClassOrObject): SirNamedDeclaration? {
val symbol = classOrObject
.getNamedClassOrObjectSymbol()
?.takeIf { it.isConsumableBySirBuilder() }
?: return null // todo: error handling strategy: KT-65980
return buildClass {
name = classOrObject.name ?: "UNKNOWN_CLASS" // todo: error handling strategy: KT-65980
origin = KotlinSource(symbol)
}
}
context(KtAnalysisSession)
internal fun buildSirFunctionFromPsi(function: KtNamedFunction): SirFunction = buildFunction {
val symbol = function.getFunctionLikeSymbol()
@@ -134,3 +157,12 @@ private fun buildSirNominalType(it: KtType): SirNominalType = SirNominalType(
throw IllegalArgumentException("Swift Export does not support argument type: ${it.asStringForDebugging()}")
}
)
context(KtAnalysisSession)
private fun KtNamedClassOrObjectSymbol.isConsumableBySirBuilder(): Boolean =
classKind == KtClassKind.CLASS
&& (superTypes.count() == 1 && superTypes.first().isAny) // Every class has Any as a superclass
&& classIdIfNonLocal?.packageFqName?.isRoot != false
&& !isData
&& !isInline
&& modality == Modality.FINAL
@@ -41,9 +41,22 @@ public class SirAsSwiftSourcesPrinter(private val printer: SmartPrinter) : SirVi
println("import ${import.moduleName}")
}
override fun visitClass(klass: SirClass): Unit = with(printer) {
printVisibility(klass)
println(
"class ",
klass.name.swiftIdentifier,
" {"
)
withIndent {
klass.acceptChildren(this@SirAsSwiftSourcesPrinter)
}
println("}")
}
override fun visitVariable(variable: SirVariable): Unit = with(printer) {
printVisibility(variable)
print(
variable.visibility.takeIf { it != SirVisibility.INTERNAL }?.let { "${it.swift} " } ?: "",
if (variable.isStatic) "static " else "",
"var ",
variable.name.swiftIdentifier,
@@ -82,8 +95,8 @@ public class SirAsSwiftSourcesPrinter(private val printer: SmartPrinter) : SirVi
override fun visitFunction(function: SirFunction): Unit = with(printer) {
function.documentation?.let { println(it) }
printVisibility(function)
print(
function.visibility.takeIf { it != SirVisibility.INTERNAL }?.let { "${it.swift} " } ?: "",
if (function.isStatic) {
"static "
} else {
@@ -121,8 +134,8 @@ public class SirAsSwiftSourcesPrinter(private val printer: SmartPrinter) : SirVi
}
override fun visitEnum(enum: SirEnum): Unit = with(printer) {
printVisibility(enum)
println(
enum.visibility.takeIf { it != SirVisibility.INTERNAL }?.let { "${it.swift} " } ?: "",
"enum ",
enum.name.swiftIdentifier,
" {"
@@ -167,4 +180,10 @@ private val SirNamedDeclaration.swiftFqName: String
private val simpleIdentifierRegex = Regex("[_a-zA-Z][_a-zA-Z0-9]*")
private val String.swiftIdentifier get() = if (simpleIdentifierRegex.matches(this)) this else "`$this`"
private val String.swiftIdentifier get() = if (simpleIdentifierRegex.matches(this)) this else "`$this`"
internal fun SmartPrinter.printVisibility(decl: SirDeclaration) {
print(
decl.visibility.takeIf { it != SirVisibility.INTERNAL }?.let { "${it.swift} " } ?: ""
)
}
@@ -0,0 +1,2 @@
public class Foo {
}
@@ -0,0 +1,4 @@
public class OUTER_CLASS {
public class INNER_CLASS {
}
}
@@ -0,0 +1,4 @@
public enum MyEnum {
public class Foo {
}
}
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.sir.printer
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.builder.buildClass
import org.jetbrains.kotlin.sir.builder.buildEnum
import org.jetbrains.kotlin.sir.builder.buildFunction
import org.jetbrains.kotlin.sir.builder.buildModule
import org.jetbrains.kotlin.sir.util.SirSwiftModule
@@ -275,6 +277,72 @@ class SirAsSwiftSourcesPrinterTests {
)
}
@Test
fun `should print empty class`() {
val module = buildModule {
name = "Test"
declarations.add(
buildClass {
origin = SirOrigin.Unknown
visibility = SirVisibility.PUBLIC
name = "Foo"
}
)
}
runTest(
module,
"testData/empty_class"
)
}
@Test
fun `should print empty class inside enum`() {
val module = buildModule {
name = "Test"
declarations.add(
buildEnum {
origin = SirOrigin.Unknown
name = "MyEnum"
declarations.add(
buildClass {
origin = SirOrigin.Unknown
visibility = SirVisibility.PUBLIC
name = "Foo"
})})}
runTest(
module,
"testData/empty_class_inside_enum"
)
}
@Test
fun `should print empty class inside class`() {
val module = buildModule {
name = "Test"
declarations.add(
buildClass {
origin = SirOrigin.Unknown
name = "OUTER_CLASS"
declarations.add(
buildClass {
origin = SirOrigin.Unknown
visibility = SirVisibility.PUBLIC
name = "INNER_CLASS"
})})}
runTest(
module,
"testData/empty_class_inside_class"
)
}
private fun runTest(module: SirModule, goldenDataFile: String) {
val expectedSwiftSrc = File(KtTestUtil.getHomeDirectory()).resolve("$goldenDataFile.golden.swift")
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// This file was generated automatically. See native/swift/sir/tree-generator/Readme.md.
// DO NOT MODIFY IT MANUALLY.
package org.jetbrains.kotlin.sir
import org.jetbrains.kotlin.sir.visitors.SirTransformer
import org.jetbrains.kotlin.sir.visitors.SirVisitor
/**
* Generated from: [org.jetbrains.kotlin.sir.tree.generator.SwiftIrTree.class]
*/
abstract class SirClass : SirDeclarationContainer(), SirNamedDeclaration {
abstract override val origin: SirOrigin
abstract override val visibility: SirVisibility
abstract override var parent: SirDeclarationParent
abstract override val name: String
abstract override val declarations: List<SirDeclaration>
override fun <R, D> accept(visitor: SirVisitor<R, D>, data: D): R =
visitor.visitClass(this, data)
@Suppress("UNCHECKED_CAST")
override fun <E : SirElement, D> transform(transformer: SirTransformer<D>, data: D): E =
transformer.transformClass(this, data) as E
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// This file was generated automatically. See native/swift/sir/tree-generator/Readme.md.
// DO NOT MODIFY IT MANUALLY.
@file:Suppress("DuplicatedCode", "unused")
package org.jetbrains.kotlin.sir.builder
import kotlin.contracts.*
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.impl.SirClassImpl
@SirBuilderDsl
class SirClassBuilder {
var origin: SirOrigin = SirOrigin.Unknown
var visibility: SirVisibility = SirVisibility.PUBLIC
lateinit var name: String
val declarations: MutableList<SirDeclaration> = mutableListOf()
fun build(): SirClass {
return SirClassImpl(
origin,
visibility,
name,
declarations,
)
}
}
@OptIn(ExperimentalContracts::class)
inline fun buildClass(init: SirClassBuilder.() -> Unit): SirClass {
contract {
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
}
return SirClassBuilder().apply(init).build()
}
@OptIn(ExperimentalContracts::class)
inline fun buildClassCopy(original: SirClass, init: SirClassBuilder.() -> Unit): SirClass {
contract {
callsInPlace(init, InvocationKind.EXACTLY_ONCE)
}
val copyBuilder = SirClassBuilder()
copyBuilder.origin = original.origin
copyBuilder.visibility = original.visibility
copyBuilder.name = original.name
copyBuilder.declarations.addAll(original.declarations)
return copyBuilder.apply(init).build()
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// This file was generated automatically. See native/swift/sir/tree-generator/Readme.md.
// DO NOT MODIFY IT MANUALLY.
@file:Suppress("DuplicatedCode", "unused")
package org.jetbrains.kotlin.sir.impl
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.util.transformInPlace
import org.jetbrains.kotlin.sir.visitors.SirTransformer
import org.jetbrains.kotlin.sir.visitors.SirVisitor
internal class SirClassImpl(
override val origin: SirOrigin,
override val visibility: SirVisibility,
override val name: String,
override val declarations: MutableList<SirDeclaration>,
) : SirClass() {
override lateinit var parent: SirDeclarationParent
override fun <R, D> acceptChildren(visitor: SirVisitor<R, D>, data: D) {
declarations.forEach { it.accept(visitor, data) }
}
override fun <D> transformChildren(transformer: SirTransformer<D>, data: D) {
declarations.transformInPlace(transformer, data)
}
}
@@ -71,6 +71,14 @@ abstract class SirTransformer<in D> : SirVisitor<SirElement, D>() {
return transformStruct(struct, data)
}
open fun transformClass(klass: SirClass, data: D): SirDeclaration {
return transformNamedDeclaration(klass, data)
}
final override fun visitClass(klass: SirClass, data: D): SirDeclaration {
return transformClass(klass, data)
}
open fun transformCallable(callable: SirCallable, data: D): SirDeclaration {
return transformDeclaration(callable, data)
}
@@ -56,6 +56,12 @@ abstract class SirTransformerVoid : SirTransformer<Nothing?>() {
final override fun transformStruct(struct: SirStruct, data: Nothing?): SirDeclaration =
transformStruct(struct)
open fun transformClass(klass: SirClass): SirDeclaration =
transformNamedDeclaration(klass)
final override fun transformClass(klass: SirClass, data: Nothing?): SirDeclaration =
transformClass(klass)
open fun transformCallable(callable: SirCallable): SirDeclaration =
transformDeclaration(callable)
@@ -35,6 +35,9 @@ abstract class SirVisitor<out R, in D> {
open fun visitStruct(struct: SirStruct, data: D): R =
visitNamedDeclaration(struct, data)
open fun visitClass(klass: SirClass, data: D): R =
visitNamedDeclaration(klass, data)
open fun visitCallable(callable: SirCallable, data: D): R =
visitDeclaration(callable, data)
@@ -70,6 +70,14 @@ abstract class SirVisitorVoid : SirVisitor<Unit, Nothing?>() {
visitNamedDeclaration(struct)
}
final override fun visitClass(klass: SirClass, data: Nothing?) {
visitClass(klass)
}
open fun visitClass(klass: SirClass) {
visitNamedDeclaration(klass)
}
final override fun visitCallable(callable: SirCallable, data: Nothing?) {
visitCallable(callable)
}
@@ -68,6 +68,12 @@ object SwiftIrTree : AbstractSwiftIrTreeBuilder() {
parent(declarationContainer)
}
val `class`: Element by element {
customParentInVisitor = namedDeclaration
parent(namedDeclaration)
parent(declarationContainer)
}
val callable by sealedElement {
parent(declaration)
}
@@ -0,0 +1,4 @@
import KotlinBridges
public class Foo {
}
@@ -0,0 +1,4 @@
class Foo {
class INSIDE_CLASS // this should be ignored currently
}
@@ -0,0 +1,28 @@
public annotation class OptIn
enum ENUM {
class INSIDE_ENUM
}
interface OUTSIDE_PROTO {
class INSIDE_PROTO
}
class INHERITANCE_COUPLE : OUTSIDE_PROTO.INSIDE_PROTO, OUTSIDE_PROTO
class INHERITANCE_SINGLE_PROTO : OUTSIDE_PROTO.INSIDE_PROTO
open class OPEN_CLASS
class INHERITANCE_SINGLE_CLASS : OPEN_CLASS
object OBJECT
data class DATA_CLASS(val a: Int)
inline class INLINE_CLASS(val a: Int)
abstract class ABSTRACT_CLASS
sealed class SEALED {
object O : SEALED()
}
@@ -0,0 +1,3 @@
package namespace
class NAMESPACED_CLASS
@@ -24,6 +24,12 @@ public class SwiftExportRunnerTest extends AbstractSwiftRunnerTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/swift/swift-export-standalone/testData"), Pattern.compile("^([^\\.]+)$"), null, false);
}
@Test
@TestMetadata("classes")
public void testClasses() {
runTest("native/swift/swift-export-standalone/testData/classes/");
}
@Test
@TestMetadata("documentation")
public void testDocumentation() {