[FIR/IR generator] Introduce ImportCollector

This class enables printing the import list in generated files
in a smarter way.

Also, refactor `Importable` interface hierarchy, namely, don't inherit
`TypeRef` from `Importable`, since we have types like `TypeRef.Star`
which are not really importable.

Replace the `Importable#typeWithArguments` property with
the `TypeRef#render` method to utilize `ImportCollector` while rendering
types.
This commit is contained in:
Sergej Jaskiewicz
2023-10-16 14:57:22 +02:00
committed by Space Team
parent bec6d38490
commit 7b7bcb8ffa
451 changed files with 1592 additions and 3017 deletions
@@ -447,7 +447,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
field = "attributes",
fieldPredicate = { it.typeRef == declarationAttributesType }
) {
default(it, "${declarationAttributesType.type}()")
default(it, "${declarationAttributesType.typeName}()")
}
configureFieldInAllLeafBuilders(
@@ -632,7 +632,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
configureFieldInAllImplementations(
"status",
implementationPredicate = { it.type in implementationsWithoutStatusAndTypeParameters }
implementationPredicate = { it.typeName in implementationsWithoutStatusAndTypeParameters }
) {
default(it, "FirResolvedDeclarationStatusImpl.DEFAULT_STATUS_FOR_STATUSLESS_DECLARATIONS")
useTypes(resolvedDeclarationStatusImplType)
@@ -640,7 +640,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
configureFieldInAllImplementations(
"typeParameters",
implementationPredicate = { it.type in implementationsWithoutStatusAndTypeParameters }
implementationPredicate = { it.typeName in implementationsWithoutStatusAndTypeParameters }
) {
defaultEmptyList(it)
useTypes(resolvedDeclarationStatusImplType)
@@ -650,7 +650,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
private fun configureAllImplementations() {
configureFieldInAllImplementations(
field = "controlFlowGraphReference",
implementationPredicate = { it.type != "FirAnonymousFunctionImpl" }
implementationPredicate = { it.typeName != "FirAnonymousFunctionImpl" }
) {
defaultNull(it)
}
@@ -682,7 +682,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
)
configureFieldInAllImplementations(
field = "typeRef",
implementationPredicate = { it.type !in implementationWithConfigurableTypeRef },
implementationPredicate = { it.typeName !in implementationWithConfigurableTypeRef },
fieldPredicate = { it.defaultValueInImplementation == null }
) {
default(it, "FirImplicitTypeRefImplWithoutSource")
@@ -691,7 +691,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
configureFieldInAllImplementations(
field = "lValueTypeRef",
implementationPredicate = { it.type in "FirVariableAssignmentImpl" },
implementationPredicate = { it.typeName in "FirVariableAssignmentImpl" },
fieldPredicate = { it.defaultValueInImplementation == null }
) {
default(it, "FirImplicitTypeRefImplWithoutSource")
@@ -50,7 +50,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
}
typeParameterRef.configure {
+symbol(typeParameterSymbolType.type)
+symbol(typeParameterSymbolType.typeName)
}
typeParametersOwner.configure {
@@ -85,7 +85,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
isMutable = true; isVolatile = true; isFinal = true; isLateinit = true
customInitializationCall = "resolvePhase.asResolveState()"
arbitraryImportables += phaseAsResolveStateExtentionImport
optInAnnotation = resolveStateAccessImport
optInAnnotation = resolveStateAccessAnnotation
}
+field("moduleData", firModuleDataType)
@@ -137,7 +137,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
expression.configure {
+field("coneTypeOrNull", coneKotlinTypeType, nullable = true, withReplace = true).apply {
optInAnnotation = unresolvedExpressionTypeAccessImport
optInAnnotation = unresolvedExpressionTypeAccessAnnotation
}
+annotations
}
@@ -115,3 +115,14 @@ val errorTypeRefImplType = type("fir.types.impl", "FirErrorTypeRefImpl")
val annotationResolvePhaseType = generatedType("expressions", "FirAnnotationResolvePhase")
val typeRefMarkerType = type("mpp", "TypeRefMarker")
val firVisitorType = type("fir.visitors", "FirVisitor", kind = TypeKind.Class)
val firVisitorVoidType = type("fir.visitors", "FirVisitorVoid", kind = TypeKind.Class)
val firDefaultVisitorType = type("fir.visitors", "FirDefaultVisitor", kind = TypeKind.Class)
val firDefaultVisitorVoidType = type("fir.visitors", "FirDefaultVisitorVoid", kind = TypeKind.Class)
val firTransformerType = type("fir.visitors", "FirTransformer", kind = TypeKind.Class)
val resolveStateAccessAnnotation = type("fir.declarations", "ResolveStateAccess", kind = TypeKind.Class)
val unresolvedExpressionTypeAccessAnnotation = type("fir.expressions", "UnresolvedExpressionTypeAccess", kind = TypeKind.Class)
val firBuilderDslAnnotation = type("fir.builder", "FirBuilderDsl", kind = TypeKind.Class)
val experimentalContractsAnnotation = type("kotlin.contracts", "ExperimentalContracts", exactPackage = true, kind = TypeKind.Class)
@@ -153,18 +153,18 @@ abstract class AbstractBuilderConfigurator<T : AbstractFirTreeBuilder>(val firTr
val message = buildString {
appendLine("${this@extractImplementation} has multiple implementations:")
for (implementation in allImplementations) {
appendLine(" - ${implementation.type}")
appendLine(" - ${implementation.typeName}")
}
appendLine("Please specify implementation is needed")
}
throw IllegalArgumentException(message)
}
} else {
allImplementations.firstOrNull { it.type == type } ?: this@AbstractBuilderConfigurator.run {
allImplementations.firstOrNull { it.typeName == type } ?: this@AbstractBuilderConfigurator.run {
val message = buildString {
appendLine("${this@extractImplementation} has not implementation $type. Existing implementations:")
for (implementation in allImplementations) {
appendLine(" - ${implementation.type}")
appendLine(" - ${implementation.typeName}")
}
appendLine("Please specify implementation is needed")
}
@@ -5,11 +5,11 @@
package org.jetbrains.kotlin.fir.tree.generator
import org.jetbrains.kotlin.fir.tree.generator.printer.VISITOR_PACKAGE
import org.jetbrains.kotlin.generators.tree.ArbitraryImportable
val phaseAsResolveStateExtentionImport = ArbitraryImportable("org.jetbrains.kotlin.fir.declarations", "asResolveState")
val resolvePhaseExtensionImport = ArbitraryImportable("org.jetbrains.kotlin.fir.declarations", "resolvePhase")
val resolveStateAccessImport = ArbitraryImportable("org.jetbrains.kotlin.fir.declarations", "ResolveStateAccess")
val resolvedDeclarationStatusImport = ArbitraryImportable("org.jetbrains.kotlin.fir.declarations.impl", "FirResolvedDeclarationStatusImpl")
val buildResolvedTypeRefImport = ArbitraryImportable("org.jetbrains.kotlin.fir.types.builder", "buildResolvedTypeRef")
@@ -21,4 +21,5 @@ val coneTypeOrNullImport = ArbitraryImportable("org.jetbrains.kotlin.fir.types",
val fakeSourceElementKindImport = ArbitraryImportable("org.jetbrains.kotlin", "KtFakeSourceElementKind")
val fakeElementImport = ArbitraryImportable("org.jetbrains.kotlin", "fakeElement")
val unresolvedExpressionTypeAccessImport = ArbitraryImportable("org.jetbrains.kotlin.fir.expressions","UnresolvedExpressionTypeAccess")
val transformInPlaceImport = ArbitraryImportable(VISITOR_PACKAGE, "transformInplace")
val toMutableOrEmptyImport = ArbitraryImportable("org.jetbrains.kotlin.fir.builder", "toMutableOrEmpty")
@@ -5,13 +5,11 @@
package org.jetbrains.kotlin.fir.tree.generator.model
import org.jetbrains.kotlin.generators.tree.FieldContainer
import org.jetbrains.kotlin.generators.tree.Importable
import org.jetbrains.kotlin.generators.tree.printer.generics
import org.jetbrains.kotlin.generators.tree.*
private const val DEFAULT_BUILDER_PACKAGE = "org.jetbrains.kotlin.fir.tree.builder"
sealed class Builder : FieldContainer, Importable {
sealed class Builder : FieldContainer, TypeRef, Importable {
val parents: MutableList<IntermediateBuilder> = mutableListOf()
val usedTypes: MutableList<Importable> = mutableListOf()
abstract override val allFields: List<FieldWithDefault>
@@ -21,7 +19,7 @@ sealed class Builder : FieldContainer, Importable {
override fun get(fieldName: String): FieldWithDefault {
return allFields.firstOrNull { it.name == fieldName }
?: throw IllegalArgumentException("Builder $type doesn't contains field $fieldName")
?: throw IllegalArgumentException("Builder $typeName doesn't contains field $fieldName")
}
private val fieldsFromParentIndex: Map<String, Boolean> by lazy {
@@ -33,18 +31,23 @@ sealed class Builder : FieldContainer, Importable {
}
fun isFromParent(field: Field): Boolean = fieldsFromParentIndex.getValue(field.name)
override fun substitute(map: TypeParameterSubstitutionMap) = this
}
class LeafBuilder(val implementation: Implementation) : Builder() {
override val type: String
override val typeName: String
get() = if (implementation.name != null) {
"${implementation.name}Builder"
} else {
"${implementation.element.type}Builder"
"${implementation.element.typeName}Builder"
}
override val typeWithArguments: String
get() = type + implementation.element.generics
context(ImportCollector)
override fun renderTo(appendable: Appendable) {
addImport(this)
appendable.append(typeName)
}
override val allFields: List<FieldWithDefault> by lazy { implementation.fieldsWithoutDefault }
@@ -59,7 +62,7 @@ class LeafBuilder(val implementation: Implementation) : Builder() {
var wantsCopy: Boolean = false
}
class IntermediateBuilder(override val type: String) : Builder() {
class IntermediateBuilder(override val typeName: String) : Builder() {
val fields: MutableList<FieldWithDefault> = mutableListOf()
var materializedElement: Element? = null
@@ -73,6 +76,9 @@ class IntermediateBuilder(override val type: String) : Builder() {
override val uselessFields: List<FieldWithDefault> = emptyList()
override var packageName: String = DEFAULT_BUILDER_PACKAGE
override val typeWithArguments: String
get() = type
context(ImportCollector)
override fun renderTo(appendable: Appendable) {
addImport(this)
appendable.append(typeName)
}
}
@@ -33,9 +33,9 @@ class Element(override val name: String, override val propertyName: String, kind
override var kDoc: String? = null
override val fields = mutableSetOf<Field>()
override val type: String = "Fir$name"
override val typeName: String = "Fir$name"
override val packageName: String = BASE_PACKAGE + kind.packageName.let { if (it.isBlank()) it else "." + it }
override val fullQualifiedName: String get() = super.fullQualifiedName!!
override val elementParents = mutableListOf<ElementRef>()
@@ -158,7 +158,7 @@ class Element(override val name: String, override val propertyName: String, kind
}
override fun toString(): String {
return typeWithArguments
return with(ImportCollector("")) { render() }
}
enum class Kind(val packageName: String) {
@@ -13,8 +13,8 @@ fun field(name: String, type: TypeRefWithNullability, nullable: Boolean = false,
return SimpleField(name, type.copy(nullable), withReplace)
}
fun field(type: TypeRefWithNullability, nullable: Boolean = false, withReplace: Boolean = false): Field {
return SimpleField(type.type.replaceFirstChar(Char::lowercaseChar), type.copy(nullable), withReplace)
fun field(type: ClassRef<*>, nullable: Boolean = false, withReplace: Boolean = false): Field {
return SimpleField(type.simpleName.replaceFirstChar(Char::lowercaseChar), type.copy(nullable), withReplace)
}
fun booleanField(name: String, withReplace: Boolean = false): Field {
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.fir.tree.generator.model
import org.jetbrains.kotlin.fir.tree.generator.printer.generics
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.generators.tree.ElementOrRef as GenericElementOrRef
@@ -104,7 +103,7 @@ class FieldWithDefault(val origin: Field) : Field() {
get() = origin.customInitializationCall
set(_) {}
override var optInAnnotation: ArbitraryImportable?
override var optInAnnotation: ClassRef<*>?
get() = origin.optInAnnotation
set(_) {}
@@ -5,18 +5,13 @@
package org.jetbrains.kotlin.fir.tree.generator.model
import org.jetbrains.kotlin.fir.tree.generator.printer.generics
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.generators.tree.printer.generics
class ImplementationWithArg(
val implementation: Implementation,
val argument: Importable?
) : FieldContainer by implementation, ImplementationKindOwner by implementation {
val element: Element get() = implementation.element
override val typeWithArguments: String
get() = type + generics
}
class Implementation(val element: Element, val name: String?) : FieldContainer, ImplementationKindOwner {
@@ -25,7 +20,8 @@ class Implementation(val element: Element, val name: String?) : FieldContainer,
override val allParents: List<ImplementationKindOwner> get() = listOf(element) + parents
val isDefault = name == null
override val type = name ?: element.type + "Impl"
override val typeName = name ?: (element.typeName + "Impl")
override val allFields = element.allFields.toMutableList().mapTo(mutableListOf()) {
FieldWithDefault(it)
}
@@ -42,12 +38,19 @@ class Implementation(val element: Element, val name: String?) : FieldContainer,
}
}
override val typeWithArguments: String
get() = type + element.generics
context(ImportCollector)
override fun renderTo(appendable: Appendable) {
addImport(this)
appendable.append(this.typeName)
if (element.params.isNotEmpty()) {
element.params.joinTo(appendable, prefix = "<", postfix = ">") { it.name }
}
}
override fun substitute(map: TypeParameterSubstitutionMap) = this
override val packageName = element.packageName + ".impl"
val usedTypes = mutableListOf<Importable>()
val arbitraryImportables = mutableListOf<ArbitraryImportable>()
var isPublic = false
var requiresOptIn = false
@@ -5,32 +5,38 @@
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.declarationAttributesType
import org.jetbrains.kotlin.fir.tree.generator.*
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.generators.tree.ImportCollector
import org.jetbrains.kotlin.generators.tree.StandardTypes
import org.jetbrains.kotlin.generators.tree.printer.GeneratedFile
import org.jetbrains.kotlin.generators.tree.printer.printGeneratedType
import org.jetbrains.kotlin.generators.tree.render
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.withIndent
import java.io.File
fun Builder.generateCode(generationPath: File): GeneratedFile =
printGeneratedType(generationPath, TREE_GENERATOR_README, packageName, type, fileSuppressions = listOf("DuplicatedCode", "unused")) {
val imports = collectImports()
imports.forEach { println("import $it") }
if (imports.isNotEmpty()) {
println()
}
printGeneratedType(
generationPath,
TREE_GENERATOR_README,
packageName,
typeName,
fileSuppressions = listOf("DuplicatedCode", "unused"),
) {
println()
addAllImports(usedTypes)
printBuilder(this@generateCode)
}
context(ImportCollector)
private fun SmartPrinter.printBuilder(builder: Builder) {
if (builder is LeafBuilder && builder.allFields.isEmpty()) {
printDslBuildFunction(builder, false)
return
}
println("@FirBuilderDsl")
println("@${firBuilderDslAnnotation.render()}")
when (builder) {
is IntermediateBuilder -> print("interface ")
is LeafBuilder -> {
@@ -40,9 +46,9 @@ private fun SmartPrinter.printBuilder(builder: Builder) {
print("class ")
}
}
print(builder.typeWithArguments)
print(builder.render())
if (builder.parents.isNotEmpty()) {
print(builder.parents.joinToString(separator = ", ", prefix = " : ") { it.type })
print(builder.parents.joinToString(separator = ", ", prefix = " : ") { it.render() })
}
var hasRequiredFields = false
println(" {")
@@ -58,11 +64,11 @@ private fun SmartPrinter.printBuilder(builder: Builder) {
println()
}
val buildType = when (builder) {
is LeafBuilder -> builder.implementation.element.typeWithArguments
is IntermediateBuilder -> builder.materializedElement!!.typeWithArguments.replace(Regex("<.>"), "<*>")
is LeafBuilder -> builder.implementation.element.render()
is IntermediateBuilder -> builder.materializedElement!!.withStarArgs().render()
}
if (builder is LeafBuilder && builder.implementation.isPublic) {
println("@OptIn(FirImplementationDetail::class)")
println("@OptIn(${firImplementationDetailType.render()}::class)")
}
if (builder.parents.isNotEmpty()) {
print("override ")
@@ -71,13 +77,16 @@ private fun SmartPrinter.printBuilder(builder: Builder) {
if (builder is LeafBuilder) {
println(" {")
withIndent {
println("return ${builder.implementation.type}(")
println("return ${builder.implementation.render()}(")
withIndent {
for (field in builder.allFields) {
if (field.invisibleField) continue
val name = field.name
print(name)
if (field.isMutableOrEmpty) print(".toMutableOrEmpty()")
if (field.isMutableOrEmpty) {
addImport(toMutableOrEmptyImport)
print(".toMutableOrEmpty()")
}
println(",")
}
}
@@ -132,15 +141,17 @@ private fun FieldWithDefault.needBackingField(fieldIsUseless: Boolean) =
private fun FieldWithDefault.needNotNullDelegate(fieldIsUseless: Boolean) =
needBackingField(fieldIsUseless) && (typeRef == StandardTypes.boolean || typeRef == StandardTypes.int)
private fun SmartPrinter.printFieldInBuilder(field: FieldWithDefault, builder: Builder, fieldIsUseless: Boolean): Pair<Boolean, Boolean> {
context(ImportCollector)
private fun SmartPrinter.printFieldInBuilder(
field: FieldWithDefault,
builder: Builder,
fieldIsUseless: Boolean,
): Pair<Boolean, Boolean> {
if (field.withGetter && !fieldIsUseless || field.invisibleField) return false to false
if (field.origin is FieldList) {
printFieldListInBuilder(field.origin, builder, fieldIsUseless)
return true to false
}
val name = field.name
val type = field.typeRef.typeWithArguments
val defaultValue = if (fieldIsUseless)
field.defaultValueInImplementation.also { requireNotNull(it) }
else
@@ -148,7 +159,7 @@ private fun SmartPrinter.printFieldInBuilder(field: FieldWithDefault, builder: B
printDeprecationOnUselessFieldIfNeeded(field, builder, fieldIsUseless)
printModifiers(builder, field, fieldIsUseless)
print("var $name: $type")
print("var ${field.name}: ${field.typeRef.render()}")
var hasRequiredFields = false
val needNewLine = when {
fieldIsUseless -> {
@@ -169,7 +180,7 @@ private fun SmartPrinter.printFieldInBuilder(field: FieldWithDefault, builder: B
false
}
field.needNotNullDelegate(fieldIsUseless) -> {
println(" by kotlin.properties.Delegates.notNull<${field.typeRef.type}>()")
println(" by kotlin.properties.Delegates.notNull<${field.typeRef.render()}>()")
hasRequiredFields = true
true
}
@@ -200,14 +211,19 @@ private fun SmartPrinter.printFieldInBuilder(field: FieldWithDefault, builder: B
private fun SmartPrinter.printDeprecationOnUselessFieldIfNeeded(field: Field, builder: Builder, fieldIsUseless: Boolean) {
if (fieldIsUseless) {
println("@Deprecated(\"Modification of '${field.name}' has no impact for ${builder.type}\", level = DeprecationLevel.HIDDEN)")
println("@Deprecated(\"Modification of '${field.name}' has no impact for ${builder.typeName}\", level = DeprecationLevel.HIDDEN)")
}
}
private fun SmartPrinter.printFieldListInBuilder(field: FieldList, builder: Builder, fieldIsUseless: Boolean) {
context(ImportCollector)
private fun SmartPrinter.printFieldListInBuilder(
field: FieldList,
builder: Builder,
fieldIsUseless: Boolean,
) {
printDeprecationOnUselessFieldIfNeeded(field, builder, fieldIsUseless)
printModifiers(builder, field, fieldIsUseless)
print("val ${field.name}: ${field.getMutableType(forBuilder = true).typeWithArguments}")
print("val ${field.name}: ${field.getMutableType(forBuilder = true).render()}")
if (builder is LeafBuilder) {
print(" = mutableListOf()")
}
@@ -228,69 +244,71 @@ private fun SmartPrinter.printModifiers(builder: Builder, field: Field, fieldIsU
}
}
context(ImportCollector)
private fun SmartPrinter.printDslBuildFunction(
builder: LeafBuilder,
hasRequiredFields: Boolean
) {
val isEmpty = builder.allFields.isEmpty()
if (!isEmpty) {
println("@OptIn(ExperimentalContracts::class)")
println("@OptIn(${experimentalContractsAnnotation.render()}::class)")
print("inline ")
} else if(builder.implementation.isPublic) {
println("@OptIn(FirImplementationDetail::class)")
} else if (builder.implementation.isPublic) {
println("@OptIn(${firImplementationDetailType.render()}::class)")
}
print("fun ")
builder.implementation.element.params.takeIf { it.isNotEmpty() }?.let {
print(it.joinToString(separator = ", ", prefix = "<", postfix = "> "))
}
val builderType = builder.typeWithArguments
val name = builder.implementation.name?.replaceFirst("Fir", "") ?: builder.implementation.element.name
print("build${name}(")
if (!isEmpty) {
print("init: $builderType.() -> Unit")
print("init: ${builder.render()}.() -> Unit")
if (!hasRequiredFields) {
print(" = {}")
}
}
println("): ${builder.implementation.element.typeWithArguments} {")
println("): ${builder.implementation.element.render()} {")
withIndent {
if (!isEmpty) {
addStarImport("kotlin.contracts")
println("contract {")
withIndent {
println("callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)")
println("callsInPlace(init, InvocationKind.EXACTLY_ONCE)")
}
println("}")
}
print("return ")
if (isEmpty) {
println("${builder.implementation.type}()")
println("${builder.implementation.render()}()")
} else {
println("$builderType().apply(init).build()")
println("${builder.render()}().apply(init).build()")
}
}
println("}")
}
context(ImportCollector)
private fun SmartPrinter.printDslBuildCopyFunction(
builder: LeafBuilder,
hasRequiredFields: Boolean,
) {
val optIns =
builder.allFields.filter { !it.invisibleField }.mapNotNullTo(mutableSetOf("ExperimentalContracts")) { it.optInAnnotation?.type }
println("@OptIn(${optIns.joinToString { "$it::class" }})")
builder.allFields.filter { !it.invisibleField }.mapNotNullTo(mutableSetOf(experimentalContractsAnnotation)) { it.optInAnnotation }
println("@OptIn(${optIns.joinToString { "${it.render()}::class" }})")
print("inline ")
print("fun ")
builder.implementation.element.params.takeIf { it.isNotEmpty() }?.let {
print(it.joinToString(separator = ", ", prefix = "<", postfix = "> "))
}
val builderType = builder.typeWithArguments
val builderType = builder.render()
val name = builder.implementation.name?.replaceFirst("Fir", "") ?: builder.implementation.element.name
print("build${name}Copy(")
print("original: ${builder.implementation.element.typeWithArguments}, init: $builderType.() -> Unit")
print("original: ${builder.implementation.element.render()}, init: $builderType.() -> Unit")
if (!hasRequiredFields) {
print(" = {}")
}
println("): ${builder.implementation.element.typeWithArguments} {")
println("): ${builder.implementation.element.render()} {")
withIndent {
println("contract {")
withIndent {
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.*
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.Element
import org.jetbrains.kotlin.fir.tree.generator.model.Field
import org.jetbrains.kotlin.fir.tree.generator.util.get
@@ -15,15 +17,12 @@ import org.jetbrains.kotlin.utils.withIndent
import java.io.File
fun Element.generateCode(generationPath: File): GeneratedFile =
printGeneratedType(generationPath, TREE_GENERATOR_README, packageName, type) {
val imports = collectImports()
imports.forEach { println("import $it") }
if (imports.isNotEmpty()) {
println()
}
printGeneratedType(generationPath, TREE_GENERATOR_README, packageName, this.typeName) {
println()
printElement(this@generateCode)
}
context(ImportCollector)
fun SmartPrinter.printElement(element: Element) {
with(element) {
val isInterface = kind == ImplementationKind.Interface || kind == ImplementationKind.SealedInterface
@@ -40,13 +39,13 @@ fun SmartPrinter.printElement(element: Element) {
}
printKDoc(element.extendedKDoc())
print("${kind!!.title} $type")
print("${kind!!.title} $typeName")
print(typeParameters())
val parentRefs = element.parentRefs
if (parentRefs.isNotEmpty()) {
print(
parentRefs.sortedBy { it.typeKind }.joinToString(prefix = " : ") { parent ->
parent.typeWithArguments + parent.inheritanceClauseParenthesis()
parent.render() + parent.inheritanceClauseParenthesis()
}
)
}
@@ -66,8 +65,9 @@ fun SmartPrinter.printElement(element: Element) {
if (allFields.isNotEmpty()) {
println()
}
override()
println("fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R =")
println("fun <R, D> accept(visitor: ${firVisitorType.render()}<R, D>, data: D): R =")
withIndent {
println("visitor.visit${element.name}(this, data)")
}
@@ -77,7 +77,13 @@ fun SmartPrinter.printElement(element: Element) {
println()
println("@Suppress(\"UNCHECKED_CAST\")")
override()
println("fun <E : FirElement, D> transform(transformer: FirTransformer<D>, data: D): E =")
println(
"fun <E : ",
AbstractFirTreeBuilder.baseFirElement.render(),
", D> transform(transformer: ",
firTransformerType.render(),
"<D>, data: D): E ="
)
withIndent {
println("transformer.transform$name(this, data) as E")
}
@@ -86,7 +92,7 @@ fun SmartPrinter.printElement(element: Element) {
fun Field.replaceDeclaration(override: Boolean, overridenType: TypeRefWithNullability? = null, forceNullable: Boolean = false) {
println()
if (name == "source") {
println("@FirImplementationDetail")
println("@${firImplementationDetailType.render()}")
}
abstract()
if (override) print("override ")
@@ -95,7 +101,7 @@ fun SmartPrinter.printElement(element: Element) {
allFields.filter { it.withReplace }.forEach {
val override = overridenFields[it, it] &&
!(it.name == "source" && fullQualifiedName.endsWith("FirQualifiedAccessExpression"))
!(it.name == "source" && element == FirTreeBuilder.qualifiedAccessExpression)
it.replaceDeclaration(override, forceNullable = it.useNullableForReplace)
for (overridenType in it.overridenTypes) {
it.replaceDeclaration(true, overridenType)
@@ -109,7 +115,7 @@ fun SmartPrinter.printElement(element: Element) {
if (field.fromParent && field.parentHasSeparateTransform) {
print("override ")
}
println(field.transformFunctionDeclaration(typeWithArguments))
println(field.transformFunctionDeclaration(element))
}
if (needTransformOtherChildren) {
println()
@@ -117,7 +123,7 @@ fun SmartPrinter.printElement(element: Element) {
if (element.elementParents.any { it.element.needTransformOtherChildren }) {
print("override ")
}
println(transformFunctionDeclaration("OtherChildren", typeWithArguments))
println(transformFunctionDeclaration("OtherChildren", element))
}
if (element.isRootElement) {
@@ -125,16 +131,16 @@ fun SmartPrinter.printElement(element: Element) {
"$element must be an interface"
}
println()
println("fun accept(visitor: FirVisitorVoid) = accept(visitor, null)")
println("fun accept(visitor: ${firVisitorVoidType.render()}) = accept(visitor, null)")
if (element.hasAcceptChildrenMethod) {
println()
println("fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D)")
println("fun <R, D> acceptChildren(visitor: ${firVisitorType.render()}<R, D>, data: D)")
}
println()
println("fun acceptChildren(visitor: FirVisitorVoid) = acceptChildren(visitor, null)")
println("fun acceptChildren(visitor: ${firVisitorVoidType.render()}) = acceptChildren(visitor, null)")
if (element.hasTransformChildrenMethod) {
println()
println("fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement")
println("fun <D> transformChildren(transformer: ${firTransformerType.render()}<D>, data: D): FirElement")
}
}
}
@@ -6,11 +6,13 @@
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.model.Field
import org.jetbrains.kotlin.generators.tree.ImportCollector
import org.jetbrains.kotlin.generators.tree.printer.printKDoc
import org.jetbrains.kotlin.generators.tree.render
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.withIndent
context(ImportCollector)
fun SmartPrinter.printField(
field: Field,
isImplementation: Boolean,
@@ -27,7 +29,8 @@ fun SmartPrinter.printField(
}
field.optInAnnotation?.let {
println(if (inConstructor) "@property:${it.type}" else "@${it.type}")
val rendered = it.render()
println(if (inConstructor) "@property:$rendered" else "@$rendered")
}
modifiers()
@@ -44,17 +47,18 @@ fun SmartPrinter.printField(
print("val")
}
val type = if (isImplementation) field.getMutableType() else field.typeRef
print(" ${field.name}: ${type.typeWithArguments}")
print(" ${field.name}: ${type.render()}")
if (inConstructor) print(",")
println()
}
context(ImportCollector)
fun SmartPrinter.printFieldWithDefaultInImplementation(field: Field) {
if (!field.isVal && field.isVolatile) {
println("@Volatile")
}
field.optInAnnotation?.let {
println("@OptIn(${it.type}::class)")
println("@OptIn(${it.render()}::class)")
}
val defaultValue = field.defaultValueInImplementation
print("override ")
@@ -63,7 +67,7 @@ fun SmartPrinter.printFieldWithDefaultInImplementation(field: Field) {
} else {
print("var")
}
print(" ${field.name}: ${field.getMutableType().typeWithArguments}")
print(" ${field.name}: ${field.getMutableType().render()}")
if (field.withGetter) {
println()
pushIndent()
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.*
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.fir.tree.generator.pureAbstractElementType
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.generators.tree.printer.GeneratedFile
import org.jetbrains.kotlin.generators.tree.printer.braces
@@ -18,15 +18,19 @@ import org.jetbrains.kotlin.utils.withIndent
import java.io.File
fun Implementation.generateCode(generationPath: File): GeneratedFile =
printGeneratedType(generationPath, TREE_GENERATOR_README, packageName, type, fileSuppressions = listOf("DuplicatedCode", "unused")) {
val imports = collectImports()
imports.forEach { println("import $it") }
if (imports.isNotEmpty()) {
println()
}
printGeneratedType(
generationPath,
TREE_GENERATOR_README,
packageName,
this.typeName,
fileSuppressions = listOf("DuplicatedCode", "unused"),
) {
println()
addAllImports(usedTypes)
printImplementation(this@generateCode)
}
context(ImportCollector)
fun SmartPrinter.printImplementation(implementation: Implementation) {
fun Field.transform() {
when (this) {
@@ -36,6 +40,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
println("$name = ${name}${call()}transform(transformer, data)")
is FieldList -> {
addImport(transformInPlaceImport)
println("${name}.transformInplace(transformer, data)")
}
@@ -46,20 +51,22 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
with(implementation) {
buildSet {
if (requiresOptIn) {
add("FirImplementationDetail")
add(firImplementationDetailType)
}
for (field in fieldsWithoutDefault) {
field.optInAnnotation?.let { add(it.type) }
field.optInAnnotation?.let {
add(it)
}
}
}.ifNotEmpty {
println("@OptIn(${joinToString { "$it::class" }})")
println("@OptIn(${joinToString { "${it.render()}::class" }})")
}
if (!isPublic) {
print("internal ")
}
print("${kind!!.title} $type")
print("${kind!!.title} ${this.typeName}")
print(element.typeParameters(end = " "))
val isInterface = kind == ImplementationKind.Interface || kind == ImplementationKind.SealedInterface
@@ -73,13 +80,13 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
if (!isInterface && !isAbstract && fieldsWithoutDefault.isNotEmpty()) {
if (isPublic) {
print(" @FirImplementationDetail constructor")
print(" @${firImplementationDetailType.render()} constructor")
}
println("(")
withIndent {
fieldsWithoutDefault.forEachIndexed { _, field ->
if (field.isParameter) {
print("${field.name}: ${field.typeRef.typeWithArguments}")
print("${field.name}: ${field.typeRef.render()}")
if (field.nullable) print("?")
println(",")
} else if (!field.isFinal) {
@@ -92,9 +99,9 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
print(" : ")
if (needPureAbstractElement) {
print("${pureAbstractElementType.type}(), ")
print("${pureAbstractElementType.render()}(), ")
}
print(allParents.joinToString { "${it.typeWithArguments}${it.kind.braces()}" })
print(allParents.joinToString { "${it.render()}${it.kind.braces()}" })
println(" {")
withIndent {
if (isInterface || isAbstract) {
@@ -114,13 +121,13 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
val bindingCalls = element.allFields.filter {
it.withBindThis && it.typeRef.type.contains("Symbol") && it !is FieldList && it.name != "companionObjectSymbol"
it.withBindThis && it.hasSymbolType && it !is FieldList && it.name != "companionObjectSymbol"
}.takeIf {
it.isNotEmpty() && !isInterface && !isAbstract &&
!element.type.contains("Reference")
&& !element.type.contains("ResolvedQualifier")
&& !element.type.endsWith("Ref")
&& !element.type.endsWith("AnnotationsContainer")
!element.typeName.contains("Reference")
&& !element.typeName.contains("ResolvedQualifier")
&& !element.typeName.endsWith("Ref")
&& !element.typeName.endsWith("AnnotationsContainer")
}.orEmpty()
val customCalls = fieldsWithoutDefault.filter { it.customInitializationCall != null }
@@ -132,6 +139,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
}
for (customCall in customCalls) {
addAllImports(customCall.arbitraryImportables)
println("${customCall.name} = ${customCall.customInitializationCall}")
}
}
@@ -141,7 +149,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
fun Field.acceptString(): String = "${name}${call()}accept(visitor, data)"
if (hasAcceptChildrenMethod) {
print("override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {")
print("override fun <R, D> acceptChildren(visitor: ${firVisitorType.render()}<R, D>, data: D) {")
val walkableFields = walkableChildren
if (walkableFields.isNotEmpty()) {
@@ -170,7 +178,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
}
else -> {
if (type == "FirWhenExpressionImpl" && field.name == "subject") {
if (this.typeName == "FirWhenExpressionImpl" && field.name == "subject") {
println(
"""
|val subjectVariable_ = subjectVariable
@@ -206,7 +214,12 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
if (hasTransformChildrenMethod) {
abstract()
print("override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
print(
"override fun <D> transformChildren(transformer: ",
firTransformerType.render(),
"<D>, data: D): ",
render(),
)
if (!isInterface && !isAbstract) {
println(" {")
withIndent {
@@ -269,7 +282,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
if (!field.needsSeparateTransform) continue
println()
abstract()
print("override ${field.transformFunctionDeclaration(typeWithArguments)}")
print("override ${field.transformFunctionDeclaration(this)}")
if (isInterface || isAbstract) {
println()
continue
@@ -278,7 +291,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
withIndent {
if (field.isMutable && field.isFirType) {
// TODO: replace with smth normal
if (type == "FirWhenExpressionImpl" && field.name == "subject") {
if (this.typeName == "FirWhenExpressionImpl" && field.name == "subject") {
println(
"""
|if (subjectVariable != null) {
@@ -301,7 +314,12 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
if (element.needTransformOtherChildren) {
println()
abstract()
print("override fun <D> transformOtherChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
print(
"override fun <D> transformOtherChildren(transformer: ",
firTransformerType.render(),
"<D>, data: D): ",
render(),
)
if (isInterface || isAbstract) {
println()
} else {
@@ -330,7 +348,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
) {
println()
if (field.name == "source") {
println("@FirImplementationDetail")
println("@${firImplementationDetailType.render()}")
}
abstract()
print("override ${field.replaceFunctionDeclaration(overridenType, forceNullable)}")
@@ -368,6 +386,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
}
print("${field.name} = $newValue")
if (field.origin is FieldList && field.isMutableOrEmpty) {
addImport(toMutableOrEmptyImport)
print(".toMutableOrEmpty()")
}
println()
@@ -377,7 +396,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
for (overridenType in field.overridenTypes) {
generateReplace(field, overridenType) {
println("require($newValue is ${field.typeRef.typeWithArguments})")
println("require($newValue is ${field.typeRef.render()})")
println("replace$capitalizedFieldName($newValue)")
}
}
@@ -386,3 +405,6 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
println("}")
}
}
private val Field.hasSymbolType: Boolean
get() = (typeRef as? ClassRef<*>)?.simpleName?.contains("Symbol") ?: false
@@ -11,12 +11,12 @@ import org.jetbrains.kotlin.generators.tree.printer.GeneratedFile
import org.jetbrains.kotlin.generators.tree.printer.multipleUpperBoundsList
import org.jetbrains.kotlin.generators.tree.printer.printGeneratedType
import org.jetbrains.kotlin.generators.tree.printer.typeParameters
import org.jetbrains.kotlin.generators.tree.render
import org.jetbrains.kotlin.utils.withIndent
import java.io.File
fun printTransformer(elements: List<Element>, generationPath: File): GeneratedFile =
printGeneratedType(generationPath, TREE_GENERATOR_README, VISITOR_PACKAGE, "FirTransformer") {
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
println("abstract class FirTransformer<in D> : FirVisitor<FirElement, D>() {")
println()
@@ -29,8 +29,16 @@ fun printTransformer(elements: List<Element>, generationPath: File): GeneratedFi
print("open fun ")
element.typeParameters(end = " ").takeIf { it.isNotBlank() }?.let { print(it) }
println(
"transform${element.name}($varName: ${element.typeWithArguments}, data: D): ${element.transformerType
.typeWithArguments}${element.multipleUpperBoundsList()} {",
"transform",
element.name,
"(",
varName,
": ",
element.render(),
", data: D): ",
element.transformerType.render(),
element.multipleUpperBoundsList(),
" {",
)
withIndent {
println("return transformElement($varName, data)")
@@ -45,8 +53,16 @@ fun printTransformer(elements: List<Element>, generationPath: File): GeneratedFi
element.typeParameters(end = " ").takeIf { it.isNotBlank() }?.let { print(it) }
println(
"visit${element.name}($varName: ${element.typeWithArguments}, data: D): ${element.transformerType
.typeWithArguments}${element.multipleUpperBoundsList()} {",
"visit",
element.name,
"(",
varName,
": ",
element.render(),
", data: D): ",
element.transformerType.render(),
element.multipleUpperBoundsList(),
" {"
)
withIndent {
println("return transform${element.name}($varName, data)")
@@ -5,129 +5,33 @@
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.firImplementationDetailType
import org.jetbrains.kotlin.fir.tree.generator.firTransformerType
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.fir.tree.generator.pureAbstractElementType
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
enum class ImportKind(val postfix: String) {
Element(""), Implementation(".impl"), Builder(".builder")
}
fun Builder.collectImports(): List<String> {
val parents = parents.mapNotNull { it.fullQualifiedName }
val builderDsl = "org.jetbrains.kotlin.fir.builder.FirBuilderDsl"
return when (this) {
is LeafBuilder -> implementation.collectImports(
parents,
ImportKind.Builder,
) + implementation.fullQualifiedName!! + usedTypes.mapNotNull { it.fullQualifiedName } + builderDsl + "kotlin.contracts.*"
is IntermediateBuilder -> {
val fqns = buildList {
addAll(parents)
usedTypes.mapNotNullTo(this) { it.fullQualifiedName }
for (field in allFields) {
if (field.invisibleField) continue
// TODO: Use recursive import collection for TypeRefs
field.typeRef.fullQualifiedName?.let(this::add)
(field.typeRef as? ParametrizedTypeRef<*, *>)?.args?.values?.mapNotNullTo(this) { it.fullQualifiedName }
}
add(materializedElement?.fullQualifiedName ?: throw IllegalStateException(type))
add(builderDsl)
}
fqns.filterRedundantImports(packageName, ImportKind.Builder)
}
}.sorted()
}
fun Implementation.collectImports(base: List<String> = emptyList(), kind: ImportKind = ImportKind.Implementation): List<String> {
return element.collectImportsInternal(
base + listOf(element.fullQualifiedName)
+ usedTypes.mapNotNull { it.fullQualifiedName }
+ arbitraryImportables.mapNotNull { it.fullQualifiedName }
+ parents.mapNotNull { it.fullQualifiedName }
+ listOfNotNull(
pureAbstractElementType.fullQualifiedName.takeIf { needPureAbstractElement },
firImplementationDetailType.fullQualifiedName.takeIf { isPublic || requiresOptIn },
),
kind,
)
}
fun Element.collectImports(): List<String> {
val baseTypes = elementParents.mapTo(mutableListOf()) { it.fullQualifiedName!! }
baseTypes += AbstractFirTreeBuilder.baseFirElement.fullQualifiedName
baseTypes += elementParents.flatMap { it.args.values }.mapNotNull { it.fullQualifiedName } // TODO: Use recursive import collection for TypeRefs
if (needPureAbstractElement) {
baseTypes += pureAbstractElementType.fullQualifiedName
}
baseTypes.addAll(otherParents.map { it.fullQualifiedName })
return collectImportsInternal(
baseTypes,
ImportKind.Element,
)
}
private fun Element.collectImportsInternal(base: List<String>, kind: ImportKind): List<String> {
val fqns = base + allFields.mapNotNull { it.typeRef.fullQualifiedName } +
allFields.flatMap { it.overridenTypes.mapNotNull { it.fullQualifiedName } + it.arbitraryImportables.mapNotNull { it.fullQualifiedName } } +
allFields.flatMap { (it.typeRef as? ParametrizedTypeRef<*, *>)?.args?.values?.mapNotNull { it.fullQualifiedName } ?: emptyList() } + // TODO: Use recursive import collection for TypeRefs
params.flatMap { it.bounds.mapNotNull { it.fullQualifiedName } }
val result = fqns.filterRedundantImports(packageName, kind).toMutableList()
if (allFields.any { it is FieldList && it.isMutableOrEmpty }) {
result += when (kind) {
ImportKind.Implementation -> listOf(
"org.jetbrains.kotlin.fir.MutableOrEmptyList",
"org.jetbrains.kotlin.fir.builder.toMutableOrEmpty"
)
ImportKind.Builder -> listOf("org.jetbrains.kotlin.fir.builder.toMutableOrEmpty")
else -> emptyList()
}
}
allFields.mapNotNull { it.optInAnnotation?.fullQualifiedName }.distinct().ifNotEmpty {
result += this
}
if (allFields.any { it.name == "source" && it.withReplace }) {
return (result + "org.jetbrains.kotlin.fir.FirImplementationDetail").distinct()
}
return result
}
private fun List<String>.filterRedundantImports(
packageName: String,
kind: ImportKind,
): List<String> {
val realPackageName = "$packageName.${kind.postfix}"
return filter { fqn ->
!fqn.startsWith("kotlin.") && fqn.dropLastWhile { it != '.' } != realPackageName
}.distinct().sorted() + "$VISITOR_PACKAGE.*"
}
val Field.isVal: Boolean
get() = (this is FieldList && !isMutableOrEmpty) || (this is FieldWithDefault && origin is FieldList && !origin.isMutableOrEmpty) || !isMutable
fun Field.transformFunctionDeclaration(returnType: String): String {
context(ImportCollector)
fun Field.transformFunctionDeclaration(returnType: TypeRef): String {
return transformFunctionDeclaration(name.replaceFirstChar(Char::uppercaseChar), returnType)
}
fun transformFunctionDeclaration(transformName: String, returnType: String): String {
return "fun <D> transform$transformName(transformer: FirTransformer<D>, data: D): $returnType"
context(ImportCollector)
fun transformFunctionDeclaration(transformName: String, returnType: TypeRef): String {
return "fun <D> transform$transformName(transformer: ${firTransformerType.render()}<D>, data: D): " +
returnType.render()
}
fun Field.replaceFunctionDeclaration(overridenType: TypeRefWithNullability? = null, forceNullable: Boolean = false): String {
context(ImportCollector)
fun Field.replaceFunctionDeclaration(
overridenType: TypeRefWithNullability? = null,
forceNullable: Boolean = false,
): String {
val capName = name.replaceFirstChar(Char::uppercaseChar)
val type = overridenType ?: typeRef
val typeWithNullable = if (forceNullable) type.copy(nullable = true) else type
return "fun replace$capName(new$capName: ${typeWithNullable.typeWithArguments})"
return "fun replace$capName(new$capName: ${typeWithNullable.render()})"
}
fun Field.getMutableType(forBuilder: Boolean = false): TypeRef = when (this) {
@@ -143,6 +47,3 @@ fun Field.getMutableType(forBuilder: Boolean = false): TypeRef = when (this) {
fun Field.call(): String = if (nullable) "?." else "."
val Element.safeDecapitalizedName: String get() = if (name == "Class") "klass" else name.replaceFirstChar(Char::lowercaseChar)
val ImplementationWithArg.generics: String
get() = argument?.let { "<${it.type}>" } ?: ""
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.generators.tree.printer.GeneratedFile
import org.jetbrains.kotlin.generators.tree.printer.multipleUpperBoundsList
import org.jetbrains.kotlin.generators.tree.printer.printGeneratedType
import org.jetbrains.kotlin.generators.tree.printer.typeParameters
import org.jetbrains.kotlin.generators.tree.render
import org.jetbrains.kotlin.utils.withIndent
import java.io.File
@@ -34,7 +35,6 @@ private fun Element.getNameOfSupertypeForDefaultVisiting(): String {
fun printVisitor(elements: List<Element>, generationPath: File, visitSuperTypeByDefault: Boolean): GeneratedFile {
val className = if (visitSuperTypeByDefault) "FirDefaultVisitor" else "FirVisitor"
return printGeneratedType(generationPath, TREE_GENERATOR_README, VISITOR_PACKAGE, className) {
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
print("abstract class $className<out R, in D> ")
@@ -57,7 +57,19 @@ fun printVisitor(elements: List<Element>, generationPath: File, visitSuperTypeBy
} else {
print("open")
}
print(" fun ${typeParameters(end = " ")}visit$name($varName: $typeWithArguments, data: D): R${multipleUpperBoundsList()} = visit")
print(
" fun ",
typeParameters(end = " "),
"visit",
name,
"(",
varName,
": ",
render(),
", data: D): R",
multipleUpperBoundsList(),
" = visit"
)
if (visitSuperTypeByDefault) {
print(element.getNameOfSupertypeForDefaultVisiting())
} else {
@@ -75,7 +87,6 @@ fun printVisitor(elements: List<Element>, generationPath: File, visitSuperTypeBy
fun printVisitorVoid(elements: List<Element>, generationPath: File): GeneratedFile =
printGeneratedType(generationPath, TREE_GENERATOR_README, VISITOR_PACKAGE, "FirVisitorVoid") {
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
println("abstract class FirVisitorVoid : FirVisitor<Unit, Nothing?>() {")
@@ -86,7 +97,19 @@ fun printVisitorVoid(elements: List<Element>, generationPath: File): GeneratedFi
if (element == AbstractFirTreeBuilder.baseFirElement) continue
with(element) {
val varName = safeDecapitalizedName
println("open fun ${typeParameters(end = " ")}visit$name($varName: $typeWithArguments)${multipleUpperBoundsList()} {")
println(
"open fun ",
typeParameters(end = " "),
"visit",
name,
"(",
varName,
": ",
render(),
")",
multipleUpperBoundsList(),
" {"
)
withIndent {
println("visitElement($varName)")
}
@@ -98,7 +121,19 @@ fun printVisitorVoid(elements: List<Element>, generationPath: File): GeneratedFi
for (element in elements) {
with(element) {
val varName = safeDecapitalizedName
println("final override fun ${typeParameters(end = " ")}visit$name($varName: $typeWithArguments, data: Nothing?)${multipleUpperBoundsList()} {")
println(
"final override fun ",
typeParameters(end = " "),
"visit",
name,
"(",
varName,
": ",
render(),
", data: Nothing?)",
multipleUpperBoundsList(),
" {"
)
withIndent {
println("visit$name($varName)")
}
@@ -112,7 +147,6 @@ fun printVisitorVoid(elements: List<Element>, generationPath: File): GeneratedFi
fun printDefaultVisitorVoid(elements: List<Element>, generationPath: File): GeneratedFile =
printGeneratedType(generationPath, TREE_GENERATOR_README, VISITOR_PACKAGE, "FirDefaultVisitorVoid") {
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
println("abstract class FirDefaultVisitorVoid : FirVisitorVoid() {")
@@ -121,7 +155,23 @@ fun printDefaultVisitorVoid(elements: List<Element>, generationPath: File): Gene
if (!element.isAcceptableForDefaultVisiting()) continue
with(element) {
val varName = safeDecapitalizedName
println("override fun ${typeParameters(end = " ")}visit$name($varName: $typeWithArguments)${multipleUpperBoundsList()} = visit${element.getNameOfSupertypeForDefaultVisiting()}($varName)")
println(
"override fun ",
typeParameters(end = " "),
"visit",
name,
"(",
varName,
": ",
render(),
")",
multipleUpperBoundsList(),
" = visit",
getNameOfSupertypeForDefaultVisiting(),
"(",
varName,
")"
)
println()
}
}
@@ -33,13 +33,13 @@ fun printHierarchyGraph(builder: AbstractFirTreeBuilder) {
with(printer) {
println("digraph FirTree {")
elements.forEach {
println(" ${it.type} [color=${it.kind!!.toColor()}]")
println(" ${it.typeName} [color=${it.kind!!.toColor()}]")
}
println()
val edges = mutableSetOf<Edge>()
elements.forEach { element ->
element.allParents.forEach { parent ->
edges += Edge(parent.type, element.type)
edges += Edge(parent.typeName, element.typeName)
}
}
edges.forEach {