[FIR generator] Extract the Implementation class to a common module

We want to use it to generate implementation classes for other
trees, just like we already do with elements (see `AbstractElement`).
This commit is contained in:
Sergej Jaskiewicz
2023-11-17 20:37:00 +01:00
committed by Space Team
parent 85cff98a38
commit 91b5a71f1a
27 changed files with 749 additions and 479 deletions
@@ -8,11 +8,12 @@ package org.jetbrains.kotlin.generators.tree
/**
* A class representing a FIR or IR tree element.
*/
abstract class AbstractElement<Element, Field>(
abstract class AbstractElement<Element, Field, Implementation>(
val name: String,
) : ElementOrRef<Element, Field>, FieldContainer<Field>, ImplementationKindOwner
where Element : AbstractElement<Element, Field>,
Field : AbstractField<Field> {
) : ElementOrRef<Element>, FieldContainer<Field>, ImplementationKindOwner
where Element : AbstractElement<Element, Field, Implementation>,
Field : AbstractField<Field>,
Implementation : AbstractImplementation<Implementation, Element, *> {
/**
* The fully-qualified name of the property in the tree generator that is used to configure this element.
@@ -25,7 +26,7 @@ abstract class AbstractElement<Element, Field>(
abstract val params: List<TypeVariable>
abstract val elementParents: List<ElementRef<Element, Field>>
abstract val elementParents: List<ElementRef<Element>>
abstract val otherParents: MutableList<ClassRef<*>>
@@ -144,6 +145,22 @@ abstract class AbstractElement<Element, Field>(
val transformerClass: Element
get() = transformerReturnType ?: baseTransformerType ?: element
var defaultImplementation: Implementation? = null
val customImplementations = mutableListOf<Implementation>()
var doesNotNeedImplementation: Boolean = false
val allImplementations: List<Implementation> by lazy {
if (doesNotNeedImplementation) {
emptyList()
} else {
val implementations = customImplementations.toMutableList()
defaultImplementation?.let { implementations += it }
implementations
}
}
final override fun get(fieldName: String): Field? {
return allFields.firstOrNull { it.name == fieldName }
}
@@ -159,5 +176,5 @@ abstract class AbstractElement<Element, Field>(
@Suppress("UNCHECKED_CAST")
override fun substitute(map: TypeParameterSubstitutionMap): Element = this as Element
fun withStarArgs(): ElementRef<Element, Field> = copy(params.associateWith { TypeRef.Star })
fun withStarArgs(): ElementRef<Element> = copy(params.associateWith { TypeRef.Star })
}
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.utils.withIndent
/**
* A common class for printing FIR or IR tree elements.
*/
abstract class AbstractElementPrinter<Element : AbstractElement<Element, Field>, Field : AbstractField<Field>>(
abstract class AbstractElementPrinter<Element : AbstractElement<Element, Field, *>, Field : AbstractField<Field>>(
private val printer: SmartPrinter,
) {
@@ -20,7 +20,7 @@ abstract class AbstractField<Field : AbstractField<Field>> {
abstract val isFinal: Boolean
abstract val isLateinit: Boolean
open var isLateinit: Boolean = false
abstract val isParameter: Boolean
@@ -42,7 +42,7 @@ abstract class AbstractField<Field : AbstractField<Field>> {
* Whether this field can contain an element either directly or e.g. in a list.
*/
open val containsElement: Boolean
get() = typeRef is ElementOrRef<*, *> || this is ListField && baseType is ElementOrRef<*, *>
get() = typeRef is ElementOrRef<*> || this is ListField && baseType is ElementOrRef<*>
open val defaultValueInImplementation: String? get() = null
@@ -90,6 +90,7 @@ abstract class AbstractField<Field : AbstractField<Field>> {
protected open fun updateFieldsInCopy(copy: Field) {
copy.kDoc = kDoc
copy.isLateinit = isLateinit
copy.arbitraryImportables += arbitraryImportables
copy.optInAnnotation = optInAnnotation
copy.isMutable = isMutable
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.withIndent
abstract class AbstractFieldPrinter<Field : AbstractField<Field>>(
abstract class AbstractFieldPrinter<Field : AbstractField<*>>(
private val printer: SmartPrinter,
) {
@@ -0,0 +1,14 @@
/*
* 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
interface AbstractFieldWithDefaultValue<OriginField : AbstractField<OriginField>> {
val origin: OriginField
var withGetter: Boolean
var customSetter: String?
var notNull: Boolean
var defaultValueInImplementation: String?
}
@@ -0,0 +1,74 @@
/*
* 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
/**
* A class representing a non-abstract implementation of an abstract class/interface of a tree node.
*/
@Suppress("LeakingThis")
abstract class AbstractImplementation<Implementation, Element, Field>(
val element: Element,
val name: String?,
) : FieldContainer<Field>, ImplementationKindOwner
where Implementation : AbstractImplementation<Implementation, Element, Field>,
Element : AbstractElement<Element, *, Implementation>,
Field : AbstractField<*> {
private val isDefault: Boolean
get() = name == null
override val allParents: List<ImplementationKindOwner>
get() = listOf(element)
override val typeName: String = name ?: (element.typeName + "Impl")
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>()
init {
@Suppress("UNCHECKED_CAST")
if (isDefault) {
element.defaultImplementation = this as Implementation
} else {
element.customImplementations += this as Implementation
}
}
override val hasAcceptChildrenMethod: Boolean
get() {
val isInterface = kind == ImplementationKind.Interface || kind == ImplementationKind.SealedInterface
val isAbstract = kind == ImplementationKind.AbstractClass || kind == ImplementationKind.SealedClass
return !isInterface && !isAbstract
}
override val hasTransformChildrenMethod: Boolean
get() = true
var isPublic = false
override fun get(fieldName: String): Field? {
return allFields.firstOrNull { it.name == fieldName }
}
private fun withDefault(field: Field) = !field.isFinal && (field.defaultValueInImplementation != null || field.isLateinit)
val fieldsWithoutDefault by lazy { allFields.filterNot(::withDefault) }
val fieldsWithDefault by lazy { allFields.filter(::withDefault) }
var requiresOptIn = false
}
@@ -0,0 +1,101 @@
/*
* 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.braces
import org.jetbrains.kotlin.generators.tree.printer.printBlock
import org.jetbrains.kotlin.generators.tree.printer.typeParameters
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import org.jetbrains.kotlin.utils.withIndent
abstract class AbstractImplementationPrinter<Implementation, Element, ImplementationField>(
private val printer: SmartPrinter,
)
where Implementation : AbstractImplementation<Implementation, Element, ImplementationField>,
Element : AbstractElement<Element, *, Implementation>,
ImplementationField : AbstractField<*>,
ImplementationField : AbstractFieldWithDefaultValue<*> {
protected abstract val implementationOptInAnnotation: ClassRef<*>
protected abstract val pureAbstractElementType: ClassRef<*>
protected abstract fun makeFieldPrinter(printer: SmartPrinter): AbstractFieldPrinter<ImplementationField>
context(ImportCollector)
protected open fun SmartPrinter.printAdditionalMethods(implementation: Implementation) {}
context(ImportCollector)
fun printImplementation(implementation: Implementation) {
printer.run {
buildSet {
if (implementation.requiresOptIn) {
add(implementationOptInAnnotation)
}
for (field in implementation.fieldsWithoutDefault) {
field.optInAnnotation?.let {
add(it)
}
}
}.ifNotEmpty {
println("@OptIn(", joinToString { "${it.render()}::class" }, ")")
}
if (!implementation.isPublic) {
print("internal ")
}
val kind = implementation.kind ?: error("Expected non-null element kind")
print("${kind.title} ${implementation.typeName}")
print(implementation.element.params.typeParameters(end = " "))
val isInterface = kind == ImplementationKind.Interface || kind == ImplementationKind.SealedInterface
val isAbstract = kind == ImplementationKind.AbstractClass || kind == ImplementationKind.SealedClass
val fieldPrinter = makeFieldPrinter(this)
if (!isInterface && !isAbstract && implementation.fieldsWithoutDefault.isNotEmpty()) {
if (implementation.isPublic) {
print(" @", implementationOptInAnnotation.render(), " constructor")
}
println("(")
withIndent {
implementation.fieldsWithoutDefault.forEachIndexed { _, field ->
if (field.isParameter) {
print(field.name, ": ", field.typeRef.render())
println(",")
} else if (!field.isFinal) {
fieldPrinter.printField(field, override = true, inConstructor = true)
}
}
}
print(")")
}
print(" : ")
if (implementation.needPureAbstractElement) {
print(pureAbstractElementType.render(), "(), ")
}
print(implementation.allParents.joinToString { "${it.render()}${it.kind.braces()}" })
printBlock {
if (isInterface || isAbstract) {
implementation.allFields.forEach {
fieldPrinter.printField(it, override = true, modality = Modality.ABSTRACT.takeIf { isAbstract })
}
} else {
implementation.fieldsWithDefault.forEach {
fieldPrinter.printField(it, override = true)
}
}
printAdditionalMethods(implementation)
}
}
}
}
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.withIndent
abstract class AbstractVisitorPrinter<Element : AbstractElement<Element, Field>, Field : AbstractField<Field>>(
abstract class AbstractVisitorPrinter<Element : AbstractElement<Element, Field, *>, Field : AbstractField<Field>>(
val printer: SmartPrinter,
) {
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.utils.SmartPrinter
abstract class AbstractVisitorVoidPrinter<Element, Field>(
printer: SmartPrinter,
) : AbstractVisitorPrinter<Element, Field>(printer)
where Element : AbstractElement<Element, Field>,
where Element : AbstractElement<Element, Field, *>,
Field : AbstractField<Field> {
final override val visitorTypeParameters: List<TypeVariable>
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.generators.util.solveGraphForClassVsInterface
*
* @property elements The list of elements of the tree to infer their [ImplementationKind].
*/
open class InterfaceAndAbstractClassConfigurator(val elements: List<ImplementationKindOwner>) {
class InterfaceAndAbstractClassConfigurator(val elements: List<ImplementationKindOwner>) {
private inner class NodeImpl(val element: ImplementationKindOwner) : Node {
override val parents: List<NodeImpl>
@@ -27,10 +27,8 @@ open class InterfaceAndAbstractClassConfigurator(val elements: List<Implementati
override fun hashCode(): Int = element.hashCode()
}
/**
* If [element]'s kind was inferred as abstract class, allows to forcibly make that element a final class instead.
*/
protected open fun shouldBeFinalClass(element: ImplementationKindOwner, allParents: Set<ImplementationKindOwner>): Boolean = false
private fun shouldBeFinalClass(element: ImplementationKindOwner, allParents: Set<ImplementationKindOwner>): Boolean =
element is AbstractImplementation<*, *, *> && element !in allParents
private fun updateKinds(nodes: List<NodeImpl>, solution: List<Boolean>) {
val allParents = nodes.flatMapTo(mutableSetOf()) { element -> element.parents.map { it.origin.element } }
@@ -59,7 +57,7 @@ open class InterfaceAndAbstractClassConfigurator(val elements: List<Implementati
private fun updateSealedKinds(nodes: Collection<NodeImpl>) {
for (node in nodes) {
val element = node.element
if (element is AbstractElement<*, *>) {
if (element is AbstractElement<*, *, *>) {
if (element.isSealed) {
element.kind = when (element.kind) {
ImplementationKind.AbstractClass -> ImplementationKind.SealedClass
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.generators.tree
data class Model<Element : AbstractElement<Element, *>>(
data class Model<Element : AbstractElement<Element, *, *>>(
val elements: List<Element>,
val rootElement: Element,
)
@@ -128,19 +128,18 @@ data class TypeRefWithVariance<out T : TypeRef>(val variance: Variance, val type
TypeRefWithVariance(variance, typeRef.substitute(map))
}
interface ElementOrRef<Element, Field> : ParametrizedTypeRef<ElementOrRef<Element, Field>, NamedTypeParameterRef>, ClassOrElementRef
where Element : AbstractElement<Element, Field>,
Field : AbstractField<Field> {
interface ElementOrRef<Element> : ParametrizedTypeRef<ElementOrRef<Element>, NamedTypeParameterRef>, ClassOrElementRef
where Element : AbstractElement<Element, *, *> {
val element: Element
override fun copy(nullable: Boolean): ElementRef<Element, Field>
override fun copy(nullable: Boolean): ElementRef<Element>
}
data class ElementRef<Element : AbstractElement<Element, Field>, Field : AbstractField<Field>>(
data class ElementRef<Element : AbstractElement<Element, *, *>>(
override val element: Element,
override val args: Map<NamedTypeParameterRef, TypeRef> = emptyMap(),
override val nullable: Boolean = false,
) : ElementOrRef<Element, Field> {
) : ElementOrRef<Element> {
override fun copy(args: Map<NamedTypeParameterRef, TypeRef>) = ElementRef(element, args, nullable)
override fun copy(nullable: Boolean) = ElementRef(element, args, nullable)
@@ -307,12 +306,12 @@ fun type(packageName: String, name: String, kind: TypeKind = TypeKind.Interface)
val ClassOrElementRef.typeKind: TypeKind
get() = when (this) {
is ElementOrRef<*, *> -> element.kind!!.typeKind
is ElementOrRef<*> -> element.kind!!.typeKind
is ClassRef<*> -> kind
}
fun ClassOrElementRef.inheritanceClauseParenthesis(): String = when (this) {
is ElementOrRef<*, *> -> element.kind.braces()
is ElementOrRef<*> -> element.kind.braces()
is ClassRef<*> -> when (kind) {
TypeKind.Class -> "()"
TypeKind.Interface -> ""
@@ -9,7 +9,7 @@ val ImplementationKindOwner.needPureAbstractElement: Boolean
get() = !(kind?.isInterface ?: false) &&
allParents.none { it.kind == ImplementationKind.AbstractClass || it.kind == ImplementationKind.SealedClass }
fun addPureAbstractElement(elements: List<AbstractElement<*, *>>, pureAbstractElement: ClassRef<*>) {
fun addPureAbstractElement(elements: List<AbstractElement<*, *, *>>, pureAbstractElement: ClassRef<*>) {
for (el in elements) {
if (el.needPureAbstractElement) {
el.otherParents.add(pureAbstractElement)
@@ -0,0 +1,403 @@
/*
* 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.config
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.generators.tree.printer.call
/**
* Provides a DSL to configure `Impl` classes for tree nodes.
*/
@Suppress("MemberVisibilityCanBePrivate", "unused")
abstract class AbstractImplementationConfigurator<Implementation, Element, ImplementationField>
where Implementation : AbstractImplementation<Implementation, Element, ImplementationField>,
Element : AbstractElement<Element, *, Implementation>,
ImplementationField : AbstractField<*>,
ImplementationField : AbstractFieldWithDefaultValue<*> {
private val elementsWithImpl = mutableSetOf<Element>()
protected abstract fun createImplementation(element: Element, name: String?): Implementation
fun configureImplementations(model: Model<Element>) {
configure()
generateDefaultImplementations(model.elements)
configureAllImplementations()
}
/**
* A customization point to fine-tune existing implementation classes or add new ones.
*
* Override this method and use [noImpl] or [impl] in it to configure implementations of tree nodes.
*/
protected abstract fun configure()
/**
* A customization point for batch-applying rules to existing implementations.
*
* Override this method and use [configureFieldInAllImplementations] to configure fields that are common to multiple implementation
* classes.
*/
protected abstract fun configureAllImplementations()
/**
* Disables generating any implementation classes for [element].
*/
protected fun noImpl(element: Element) {
element.doesNotNeedImplementation = true
}
/**
* Provides a way to fine-tune a single implementation class for [element].
*
* @param element The element whose implementation you want to configure.
* @param name The name of the implementation class, or `null` if you want to configure the default implementation class for this
* element. If an implementation with this name already exists, it will be used, otherwise a new implementation will be created.
* @param config The configuration block. See [ImplementationContext]'s documentation for description of its DSL methods.
* @return The configured implementation.
*/
protected fun impl(element: Element, name: String? = null, config: ImplementationContext.() -> Unit = {}): Implementation {
val implementation = if (name == null) {
element.defaultImplementation
} else {
element.customImplementations.firstOrNull { it.name == name }
} ?: createImplementation(element, name)
val context = ImplementationContext(implementation)
context.apply(config)
elementsWithImpl += element
return implementation
}
private fun generateDefaultImplementations(elements: List<Element>) {
collectLeafsWithoutImplementation(elements).forEach {
impl(it)
}
}
private fun collectLeafsWithoutImplementation(elements: List<Element>): Set<Element> {
val leafs = elements.toMutableSet()
elements.forEach { element ->
leafs.removeAll(element.elementParents.map { it.element }.toSet())
}
leafs.removeAll(elementsWithImpl)
return leafs
}
/**
* Allows to batch-apply [config] to certain fields in _all_ the implementations that satisfy the given
* [implementationPredicate].
*
* @param field The name of the field to configure across all `Impl` classes.
* @param implementationPredicate Only implementations satisfying this predicate will be used in this configuration.
* @param fieldPredicate Only fields satisfying this predicate will be configured
* @param config The configuration block. Accepts the field name as an argument.
* See [ImplementationContext]'s documentation for description of its DSL methods.
*/
protected fun configureFieldInAllImplementations(
field: String,
implementationPredicate: (Implementation) -> Boolean = { true },
fieldPredicate: (ImplementationField) -> Boolean = { true },
config: ImplementationContext.(field: String) -> Unit,
) {
for (element in elementsWithImpl) {
for (implementation in element.allImplementations) {
if (!implementationPredicate(implementation)) continue
if (!implementation.allFields.any { it.name == field }) continue
if (!fieldPredicate(implementation.getField(field))) continue
ImplementationContext(implementation).config(field)
}
}
}
private fun Implementation.getField(name: String): ImplementationField {
val result = this[name]
requireNotNull(result) {
"Field \"$name\" not found in fields of $element\nExisting fields:\n" +
allFields.joinToString(separator = "\n ", prefix = " ") { it.name }
}
return result
}
/**
* A DSL for configuring one or more implementation classes.
*/
protected inner class ImplementationContext(val implementation: Implementation) {
/**
* Call this function if you want this implementation class to be marked with an [OptIn] annotation.
*
* This is necessary if some code inside the implementation class requires that [OptIn] annotation.
*/
fun optInToInternals() {
implementation.requiresOptIn = true
}
/**
* By default, all implementation classes are generated with `internal` visibility.
*
* This method allows to forcibly make this implementation `public`.
*/
fun publicImplementation() {
implementation.isPublic = true
}
private fun getField(name: String): ImplementationField {
return implementation.getField(name)
}
/**
* Types/functions that you want to additionally import in the file with the implementation class.
*
* This is useful if, for example, default values of fields reference classes or functions from other packages.
*
* Note that classes referenced in field types will be imported automatically.
*/
fun additionalImports(vararg importables: Importable) {
importables.forEach { implementation.usedTypes += it }
}
/**
* Makes the specified fields in the implementation class mutable
* (even if they were not configured as mutable in the element configurator).
*/
fun isMutable(vararg fields: String) {
fields.forEach {
val field = getField(it)
field.isMutable = true
}
}
/**
* Makes the specified fields in the implementation class `lateinit`
* (even if they were not configured as `lateinit` in the element configurator).
*/
fun isLateinit(vararg fields: String) {
fields.forEach {
val field = getField(it)
field.isLateinit = true
}
}
/**
* Specifies the default value of [field] in this implementation class. The default value can be arbitrary code.
*
* Use [additionalImports] if the default value uses types/functions that are not otherwise imported.
*/
fun default(field: String, value: String) {
default(field) {
this.value = value
}
}
/**
* Specifies that the default value of each field of [fields] in this implementation class should be `true`.
*
* If [withGetter] is `true`, the fields will be generated as getter-only computed properties with their getter returning `true`,
* otherwise, as stored properties initialized to `true`.
*/
fun defaultTrue(vararg fields: String, withGetter: Boolean = false) {
for (field in fields) {
default(field) {
value = "true"
this.withGetter = withGetter
}
}
}
/**
* Specifies that the default value of each field of [fields] in this implementation class should be `false`.
*
* If [withGetter] is `true`, the fields will be generated as getter-only computed properties with their getter returning `false`,
* otherwise, as stored properties initialized to `false`.
*/
fun defaultFalse(vararg fields: String, withGetter: Boolean = false) {
for (field in fields) {
default(field) {
value = "false"
this.withGetter = withGetter
}
}
}
/**
* Specifies that the default value of each field of [fields] in this implementation class should be `null`.
*
* If [withGetter] is `true`, the fields will be generated as getter-only computed properties with their getter returning `null`,
* otherwise, as stored properties initialized to `null`.
*/
fun defaultNull(vararg fields: String, withGetter: Boolean = false) {
for (field in fields) {
default(field) {
value = "null"
this.withGetter = withGetter
}
require(getField(field).nullable) {
"$field is not nullable field"
}
}
}
/**
* Specifies that the default value of each field of [fields] in this implementation class should be [emptyList].
*
* Always forces generation of a getter-only computed property.
*/
fun defaultEmptyList(vararg fields: String) {
for (field in fields) {
require(getField(field).origin is ListField) {
"$field is list field"
}
default(field) {
value = "emptyList()"
withGetter = true
}
}
}
/**
* Allows to configure the default value of [field] in this implementation class.
*
* See the [DefaultValueContext] documentation for description of its DSL methods.
*/
fun default(field: String, init: DefaultValueContext.() -> Unit) {
DefaultValueContext(getField(field)).apply(init).applyConfiguration()
}
/**
* Specifies that for each field in the [fields] list its getter should be delegated to the [delegate]'s property of the same name.
*
* For example, `delegateFields(listOf("foo", "bar"), "myDelegate")` will result in generating the following properties in
* the implementation class (provided that there are fields with names "foo" and "bar" in this implementation):
* ```kotlin
* val foo: Foo
* get() = myDelegate.foo
*
* val bar: Bar
* get() = myDelegate.bar
* ```
*/
fun delegateFields(fields: List<String>, delegate: String) {
for (field in fields) {
default(field) {
this.delegate = delegate
}
}
}
/**
* Allows to customize this implementation class's kind (`open class`, `object` etc.).
*
* If set to `null`, will be chosen automatically by [InterfaceAndAbstractClassConfigurator].
*/
var kind: ImplementationKind?
get() = implementation.kind
set(value) {
implementation.kind = value
}
/**
* A DSL for configuring a field's default value.
*/
inner class DefaultValueContext(private val field: ImplementationField) {
/**
* The default value of this field in the implementation class. Can be arbitrary code.
*
* Use [additionalImports] if the default value uses types/functions that are not otherwise imported.
*/
var value: String? = null
/**
* The name of the field to which to delegate this field's getter.
*
* For example, setting [delegate] to `"myDelegate"` will result in generating the following in
* the implementation class (provided that we're configuring the field `foo`):
* ```kotlin
* val foo: Foo
* get() = myDelegate.foo
* ```
*
* If [delegateCall] is not null, then instead of calling `foo` on `myDelegate`, the value of [delegateCall] will be used to
* generate the call.
*/
var delegate: String? = null
set(value) {
field = value
if (value != null) {
withGetter = true
}
}
/**
* If [delegate] is not null, allows to specify a call that will be generated on [delegate].
*
* For example, setting [delegate] to `"myDelegate"` and [delegateCall] to `"getFoo()"` will result in generating the following
* in the implementation class (provided that we're configuring the field `foo`):
* ```kotlin
* val foo: Foo
* get() = myDelegate.getFoo()
* ```
*
* If [delegate] is `null`, setting this property has no effect.
*/
var delegateCall: String? = null
/**
* Forces the specified mutability on this field in the implementation class.
*
* If set to `null`, the mutability specified in the element configurator will be used.
*/
var isMutable: Boolean? = null
/**
* If `true`, the field will be generated as a computed property instead of stored one.
*/
var withGetter: Boolean = false
set(value) {
field = value
if (value) {
isMutable = customSetter != null
}
}
/**
* Specifies the value of this field's setter.
*
* If set to a non-null value, the generated property is automatically made mutable and computed.
*
* Can be arbitrary code. Use [additionalImports] if this code uses types/functions that are not otherwise imported.
*/
var customSetter: String? = null
set(value) {
field = value
isMutable = true
withGetter = true
}
/**
* Allows to exclude this field from visiting it by visitors in the generated `acceptChildren` and `transformChildren`
* methods.
*/
var needAcceptAndTransform: Boolean = true
fun applyConfiguration() {
field.withGetter = withGetter
field.customSetter = customSetter
isMutable?.let { field.isMutable = it }
field.needAcceptAndTransform = needAcceptAndTransform
when {
value != null -> field.defaultValueInImplementation = value
delegate != null -> {
val actualDelegateField = getField(delegate!!)
val name = delegateCall ?: field.name
field.defaultValueInImplementation = "${actualDelegateField.name}${actualDelegateField.call()}$name"
}
}
}
}
}
}
@@ -85,7 +85,7 @@ fun SmartPrinter.printKDoc(kDoc: String?) {
println(" */")
}
fun AbstractElement<*, *>.extendedKDoc(defaultKDoc: String? = null): String = buildString {
fun AbstractElement<*, *, *>.extendedKDoc(defaultKDoc: String? = null): String = buildString {
val doc = kDoc ?: defaultKDoc
if (doc != null) {
appendLine(doc)
@@ -201,7 +201,7 @@ private val dataParameter = FunctionParameter("data", dataTP)
context(ImportCollector)
fun SmartPrinter.printAcceptMethod(
element: AbstractElement<*, *>,
element: AbstractElement<*, *, *>,
visitorClass: ClassRef<PositionTypeParameterRef>,
hasImplementation: Boolean,
treeName: String,
@@ -239,7 +239,7 @@ fun SmartPrinter.printAcceptMethod(
context(ImportCollector)
fun SmartPrinter.printTransformMethod(
element: AbstractElement<*, *>,
element: AbstractElement<*, *, *>,
transformerClass: ClassRef<PositionTypeParameterRef>,
implementation: String?,
returnType: TypeRefWithNullability,
@@ -347,3 +347,5 @@ fun SmartPrinter.printTransformChildrenMethod(
override = override,
)
}
fun AbstractField<*>.call(): String = if (nullable) "?." else "."
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.generators.tree
/**
* Runs [block] on this element and all its parents recursively.
*/
fun <Element : AbstractElement<Element, *>> Element.traverseParents(block: (Element) -> Unit) {
fun <Element : AbstractElement<Element, *, *>> Element.traverseParents(block: (Element) -> Unit) {
traverseParentsUntil { block(it); false }
}
@@ -19,7 +19,7 @@ fun <Element : AbstractElement<Element, *>> Element.traverseParents(block: (Elem
*
* If [block] always returns `false`, visits all the parents and returns `false`.
*/
fun <Element : AbstractElement<Element, *>> Element.traverseParentsUntil(block: (Element) -> Boolean): Boolean {
fun <Element : AbstractElement<Element, *, *>> Element.traverseParentsUntil(block: (Element) -> Boolean): Boolean {
if (block(this)) return true
for (parent in elementParents) {
if (parent.element.traverseParentsUntil(block)) return true
@@ -32,13 +32,13 @@ fun <Element : AbstractElement<Element, *>> Element.traverseParentsUntil(block:
* a type of a field, except when that field is explicitly opted out of it via
* [AbstractField.useInBaseTransformerDetection].
*/
fun <Element : AbstractElement<Element, *>> detectBaseTransformerTypes(model: Model<Element>) {
val usedAsFieldType = hashSetOf<AbstractElement<*, *>>()
fun <Element : AbstractElement<Element, *, *>> detectBaseTransformerTypes(model: Model<Element>) {
val usedAsFieldType = hashSetOf<AbstractElement<*, *, *>>()
for (element in model.elements) {
for (field in element.allFields.filter { it.containsElement }) {
if (!field.useInBaseTransformerDetection) continue
val fieldElement = (field.typeRef as? ElementOrRef<*, *>)?.element
?: ((field as? ListField)?.baseType as? ElementOrRef<*, *>)?.element
val fieldElement = (field.typeRef as? ElementOrRef<*>)?.element
?: ((field as? ListField)?.baseType as? ElementOrRef<*>)?.element
?: continue
if (fieldElement.isRootElement) continue
usedAsFieldType.add(fieldElement)