[FIR generator] Factor out field printer into the common code

This is a step towards commonizing the code generator between
FIR and IR: KT-61970
This commit is contained in:
Sergej Jaskiewicz
2023-09-06 17:13:20 +02:00
committed by Space Team
parent bca35c0015
commit 84bd12c667
8 changed files with 133 additions and 111 deletions
@@ -23,8 +23,9 @@ sealed class Field : AbstractField() {
open var customInitializationCall: String? = null
open val defaultValueInImplementation: String? get() = null
abstract var isMutableOrEmpty: Boolean
open val isMutableOrEmptyList: Boolean
get() = false
open var isMutableInInterface: Boolean = false
open val fromDelegate: Boolean get() = false
@@ -110,7 +111,9 @@ class FieldWithDefault(val origin: Field) : Field() {
override var defaultValueInImplementation: String? = origin.defaultValueInImplementation
var defaultValueInBuilder: String? = null
override var isMutable: Boolean = origin.isMutable
override var isMutableOrEmpty: Boolean = origin.isMutableOrEmpty
override val isMutableOrEmptyList: Boolean
get() = origin.isMutableOrEmptyList
override var isMutableInInterface: Boolean = origin.isMutableInInterface
override var withGetter: Boolean = false
override var customSetter: String? = null
@@ -148,7 +151,6 @@ class SimpleField(
) : Field() {
override val isFirType: Boolean = false
override var isMutable: Boolean = withReplace
override var isMutableOrEmpty: Boolean = false
override fun internalCopy(): Field {
return SimpleField(
@@ -191,7 +193,6 @@ class FirField(
override val isFirType: Boolean = true
override var isMutable: Boolean = true
override var isMutableOrEmpty: Boolean = false
override var isLateinit: Boolean = false
override var isParameter: Boolean = false
@@ -221,7 +222,7 @@ class FieldList(
override var isVolatile: Boolean = false
override var isFinal: Boolean = false
override var isMutable: Boolean = true
override var isMutableOrEmpty: Boolean = useMutableOrEmpty
override val isMutableOrEmptyList: Boolean = useMutableOrEmpty
override var isLateinit: Boolean = false
override var isParameter: Boolean = false
@@ -230,7 +231,7 @@ class FieldList(
name,
baseType,
withReplace,
isMutableOrEmpty
isMutableOrEmptyList
)
}
@@ -83,7 +83,7 @@ private fun SmartPrinter.printBuilder(builder: Builder) {
if (field.invisibleField) continue
val name = field.name
print(name)
if (field.isMutableOrEmpty) {
if (field.isMutableOrEmptyList) {
addImport(toMutableOrEmptyImport)
print(".toMutableOrEmpty()")
}
@@ -22,6 +22,8 @@ fun Element.generateCode(generationPath: File): GeneratedFile =
printElement(this@generateCode)
}
private class ElementFieldPrinter(printer: SmartPrinter) : AbstractFieldPrinter<Field>(printer)
context(ImportCollector)
fun SmartPrinter.printElement(element: Element) {
with(element) {
@@ -52,9 +54,10 @@ fun SmartPrinter.printElement(element: Element) {
print(params.multipleUpperBoundsList())
println(" {")
withIndent {
val fieldPrinter = ElementFieldPrinter(this@printElement)
allFields.forEach { field ->
if (field.isFinal && field.fromParent || field.isParameter) return@forEach
printField(field, isImplementation = false, override = field.fromParent) {
fieldPrinter.printField(field, override = field.fromParent) {
if (!field.isFinal) {
abstract()
}
@@ -1,90 +0,0 @@
/*
* Copyright 2010-2023 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.
*/
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,
override: Boolean,
inConstructor: Boolean = false,
modifiers: SmartPrinter.() -> Unit = {},
) {
if (!override && field.kDoc != null) {
println()
printKDoc(field.kDoc)
}
if (!field.isVal && field.isVolatile) {
println("@Volatile")
}
field.optInAnnotation?.let {
val rendered = it.render()
println(if (inConstructor) "@property:$rendered" else "@$rendered")
}
modifiers()
if (override) {
print("override ")
}
if (field.isLateinit) {
print("lateinit ")
}
if (isImplementation && !field.isVal || field.isFinal && field.isMutable) {
print("var")
} else {
print("val")
}
val type = if (isImplementation) field.getMutableType() else field.typeRef
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.render()}::class)")
}
val defaultValue = field.defaultValueInImplementation
print("override ")
if (field.isVal) {
print("val")
} else {
print("var")
}
print(" ${field.name}: ${field.getMutableType().render()}")
if (field.withGetter) {
println()
pushIndent()
print("get()")
}
requireNotNull(defaultValue) {
"No default value for $field"
}
println(" = $defaultValue")
field.customSetter?.let {
println("set(value) {")
withIndent {
println(it)
}
println("}")
}
if (field.withGetter) {
popIndent()
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.tree.generator.*
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.generators.tree.*
@@ -30,6 +31,19 @@ fun Implementation.generateCode(generationPath: File): GeneratedFile =
printImplementation(this@generateCode)
}
private class ImplementationFieldPrinter(printer: SmartPrinter) : AbstractFieldPrinter<Field>(printer) {
private fun Field.isMutableOrEmptyIfList(): Boolean = when (this) {
is FieldList -> isMutableOrEmptyList
is FieldWithDefault -> origin.isMutableOrEmptyIfList()
else -> true
}
override fun forceMutable(field: Field): Boolean = field.isMutable && field.isMutableOrEmptyIfList()
override fun actualTypeOfField(field: Field) = field.getMutableType()
}
context(ImportCollector)
fun SmartPrinter.printImplementation(implementation: Implementation) {
fun Field.transform() {
@@ -78,6 +92,8 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
}
}
val fieldPrinter = ImplementationFieldPrinter(this@printImplementation)
if (!isInterface && !isAbstract && fieldsWithoutDefault.isNotEmpty()) {
if (isPublic) {
print(" @${firImplementationDetailType.render()} constructor")
@@ -90,7 +106,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
if (field.nullable) print("?")
println(",")
} else if (!field.isFinal) {
printField(field, isImplementation = true, override = true, inConstructor = true)
fieldPrinter.printField(field, override = true, inConstructor = true)
}
}
}
@@ -106,13 +122,11 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
withIndent {
if (isInterface || isAbstract) {
allFields.forEach {
abstract()
printField(it, isImplementation = true, override = true)
fieldPrinter.printField(it, override = true, modality = Modality.ABSTRACT.takeIf { isAbstract })
}
} else {
fieldsWithDefault.forEach {
printFieldWithDefaultInImplementation(it)
fieldPrinter.printField(it, override = true)
}
if (fieldsWithDefault.isNotEmpty()) {
println()
@@ -375,7 +389,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
when {
field.withGetter -> {}
field.origin is FieldList && !field.isMutableOrEmpty -> {
field.origin is FieldList && !field.isMutableOrEmptyList -> {
println("${field.name}.clear()")
println("${field.name}.addAll($newValue)")
}
@@ -385,7 +399,7 @@ fun SmartPrinter.printImplementation(implementation: Implementation) {
println("require($newValue != null)")
}
print("${field.name} = $newValue")
if (field.origin is FieldList && field.isMutableOrEmpty) {
if (field.origin is FieldList && field.isMutableOrEmptyList) {
addImport(toMutableOrEmptyImport)
print(".toMutableOrEmpty()")
}
@@ -9,9 +9,6 @@ import org.jetbrains.kotlin.fir.tree.generator.firTransformerType
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.generators.tree.*
val Field.isVal: Boolean
get() = (this is FieldList && !isMutableOrEmpty) || (this is FieldWithDefault && origin is FieldList && !origin.isMutableOrEmpty) || !isMutable
context(ImportCollector)
fun Field.transformFunctionDeclaration(returnType: TypeRef): String {
return transformFunctionDeclaration(name.replaceFirstChar(Char::uppercaseChar), returnType)
@@ -34,9 +31,9 @@ fun Field.replaceFunctionDeclaration(
return "fun replace$capName(new$capName: ${typeWithNullable.render()})"
}
fun Field.getMutableType(forBuilder: Boolean = false): TypeRef = when (this) {
fun Field.getMutableType(forBuilder: Boolean = false): TypeRefWithNullability = when (this) {
is FieldList -> when {
isMutableOrEmpty && !forBuilder -> type(BASE_PACKAGE, "MutableOrEmptyList", kind = TypeKind.Class)
isMutableOrEmptyList && !forBuilder -> type(BASE_PACKAGE, "MutableOrEmptyList", kind = TypeKind.Class)
isMutable -> StandardTypes.mutableList
else -> StandardTypes.list
}.withArgs(baseType).copy(nullable)
@@ -34,6 +34,8 @@ abstract class AbstractField {
var fromParent: Boolean = false
open val defaultValueInImplementation: String? get() = null
override fun toString(): String {
return name
}
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2023 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.
*/
package org.jetbrains.kotlin.generators.tree
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.generators.tree.printer.printKDoc
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.withIndent
abstract class AbstractFieldPrinter<Field : AbstractField>(
private val printer: SmartPrinter,
) {
/**
* Allows to forcibly make the field a `var` instead of `val`.
*/
protected open fun forceMutable(field: Field): Boolean = false
/**
* Allows to override the printed type of [field]. For example, for list fields we may want to use [MutableList] instead of [List]
* in implementation classes.
*/
protected open fun actualTypeOfField(field: Field): TypeRefWithNullability = field.typeRef
context(ImportCollector)
fun printField(
field: Field,
override: Boolean,
inConstructor: Boolean = false,
modality: Modality? = null,
) {
printer.run {
printKDoc(field.kDoc)
if (field.isVolatile) {
println("@", type<Volatile>().render())
}
val defaultValue = field.defaultValueInImplementation
field.optInAnnotation?.let {
val rendered = it.render()
when {
defaultValue != null -> println("@OptIn(", rendered, "::class)")
inConstructor -> println("@property:", rendered)
else -> println("@", rendered)
}
}
modality?.let {
print(it.name.toLowerCaseAsciiOnly(), " ")
}
if (override) {
print("override ")
}
if (field.isLateinit) {
print("lateinit ")
}
if (forceMutable(field) || field.isFinal && field.isMutable) {
print("var ")
} else {
print("val ")
}
print(field.name, ": ", actualTypeOfField(field).render())
if (inConstructor) {
print(",")
}
if (defaultValue == null) {
println()
return
}
if (field.withGetter) {
println()
pushIndent()
print("get()")
}
println(" = $defaultValue")
field.customSetter?.let {
println("set(value) {")
withIndent {
println(it)
}
println("}")
}
if (field.withGetter) {
popIndent()
}
}
}
}