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:
@@ -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 {
|
||||
}
|
||||
}
|
||||
+68
@@ -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)
|
||||
}
|
||||
|
||||
+6
@@ -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)
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import KotlinBridges
|
||||
|
||||
public class Foo {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
class Foo {
|
||||
class INSIDE_CLASS // this should be ignored currently
|
||||
}
|
||||
+28
@@ -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()
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package namespace
|
||||
|
||||
class NAMESPACED_CLASS
|
||||
+6
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user