[IR] Add kdoc for IrElement

This commit is contained in:
Sergej Jaskiewicz
2023-03-16 15:49:28 +01:00
committed by Space Team
parent ba5ff7967c
commit de7de7b287
3 changed files with 176 additions and 25 deletions
@@ -12,20 +12,75 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
/**
* A non-leaf IR tree element.
* The root interface of the IR tree. Each IR node implements this interface.
*
* Generated from: [org.jetbrains.kotlin.ir.generator.IrTree.rootElement]
*/
interface IrElement {
/**
* The start offset of the syntax node from which this IR node was generated,
* in number of characters from the start of the source file. If there is no source information
* for this IR node,
* the [UNDEFINED_OFFSET] constant is used. In order to get the line number and the column
* number from this offset,
* [IrFileEntry.getLineNumber] and [IrFileEntry.getColumnNumber] can be used.
*
* @see IrFileEntry.getSourceRangeInfo
*/
val startOffset: Int
/**
* The end offset of the syntax node from which this IR node was generated,
* in number of characters from the start of the source file. If there is no source information
* for this IR node,
* the [UNDEFINED_OFFSET] constant is used. In order to get the line number and the column
* number from this offset,
* [IrFileEntry.getLineNumber] and [IrFileEntry.getColumnNumber] can be used.
*
* @see IrFileEntry.getSourceRangeInfo
*/
val endOffset: Int
/**
* Runs the provided [visitor] on the IR subtree with the root at this node.
*
* @param visitor The visitor to accept.
* @param data An arbitrary context to pass to each invocation of [visitor]'s methods.
* @return The value returned by the topmost `visit*` invocation.
*/
fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R
/**
* Runs the provided [transformer] on the IR subtree with the root at this node.
*
* @param transformer The transformer to use.
* @param data An arbitrary context to pass to each invocation of [transformer]'s methods.
* @return The transformed node.
*/
fun <D> transform(transformer: IrElementTransformer<D>, data: D): IrElement
/**
* Runs the provided [visitor] on subtrees with roots in this node's children.
*
* Basically, calls `accept(visitor, data)` on each child of this node.
*
* Does **not** run [visitor] on this node itself.
*
* @param visitor The visitor for children to accept.
* @param data An arbitrary context to pass to each invocation of [visitor]'s methods.
*/
fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D)
/**
* Recursively transforms this node's children *in place* using [transformer].
*
* Basically, executes `this.child = this.child.transform(transformer, data)` for each child
* of this node.
*
* Does **not** run [transformer] on this node itself.
*
* @param transformer The transformer to use for transforming the children.
* @param data An arbitrary context to pass to each invocation of [transformer]'s methods.
*/
fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D)
}
@@ -42,8 +42,21 @@ object IrTree : AbstractTreeBuilder() {
transform = true
transformByChildren = true
+field("startOffset", int, mutable = false)
+field("endOffset", int, mutable = false)
fun offsetField(prefix: String) = field(prefix + "Offset", int, mutable = false) {
kdoc = """
The $prefix offset of the syntax node from which this IR node was generated,
in number of characters from the start of the source file. If there is no source information for this IR node,
the [UNDEFINED_OFFSET] constant is used. In order to get the line number and the column number from this offset,
[IrFileEntry.getLineNumber] and [IrFileEntry.getColumnNumber] can be used.
@see IrFileEntry.getSourceRangeInfo
""".trimIndent()
}
+offsetField("start")
+offsetField("end")
kDoc = "The root interface of the IR tree. Each IR node implements this interface."
}
val statement: ElementConfig by element(Other)
@@ -76,32 +76,68 @@ fun printElements(generationPath: File, model: Model) = sequence {
}
val isRootElement = element.elementParents.isEmpty()
val acceptMethodName = "accept"
val transformMethodName = "transform"
if (element.accept) {
addFunction(FunSpec.builder("accept").apply {
addFunction(FunSpec.builder(acceptMethodName).apply {
addModifiers(if (isRootElement) KModifier.ABSTRACT else KModifier.OVERRIDE)
val r = TypeVariableName("R")
val d = TypeVariableName("D")
addTypeVariable(r)
addTypeVariable(d)
addParameter("visitor", elementVisitorType.toPoet().tryParameterizedBy(r, d))
addParameter("data", d)
val visitorParam = ParameterSpec.builder("visitor", elementVisitorType.toPoet().tryParameterizedBy(r, d))
.build()
.also(::addParameter)
val dataParam = ParameterSpec.builder("data", d)
.build()
.also(::addParameter)
returns(r)
if (!isRootElement) {
addStatement("return visitor.${element.visitFunName}(this, data)")
addStatement("return %N.%N(this, %N)", visitorParam, element.visitFunName, dataParam)
}
if (isRootElement) {
addKdoc(
"""
Runs the provided [%1N] on the IR subtree with the root at this node.
@param %1N The visitor to accept.
@param %2N An arbitrary context to pass to each invocation of [%1N]'s methods.
@return The value returned by the topmost `visit*` invocation.
""".trimIndent(),
visitorParam,
dataParam,
)
}
}.build())
}
if (element.transform) {
addFunction(FunSpec.builder("transform").apply {
addFunction(FunSpec.builder(transformMethodName).apply {
addModifiers(if (isRootElement) KModifier.ABSTRACT else KModifier.OVERRIDE)
val d = TypeVariableName("D")
addTypeVariable(d)
addParameter("transformer", elementTransformerType.toPoet().tryParameterizedBy(d))
addParameter("data", d)
val transformerParam = ParameterSpec.builder("transformer", elementTransformerType.toPoet().tryParameterizedBy(d))
.build()
.also(::addParameter)
val dataParam = ParameterSpec.builder("data", d)
.build()
.also(::addParameter)
returns(selfParametrizedElementName)
if (!isRootElement) {
addStatement("return accept(transformer, data) as %T", selfParametrizedElementName)
addStatement("return %N(%N, %N) as %T", acceptMethodName, transformerParam, dataParam, selfParametrizedElementName)
}
if (isRootElement) {
addKdoc(
"""
Runs the provided [%1N] on the IR subtree with the root at this node.
@param %1N The transformer to use.
@param %2N An arbitrary context to pass to each invocation of [%1N]'s methods.
@return The transformed node.
""".trimIndent(),
transformerParam,
dataParam,
)
}
}.build())
}
@@ -111,18 +147,41 @@ fun printElements(generationPath: File, model: Model) = sequence {
addModifiers(if (isRootElement) KModifier.ABSTRACT else KModifier.OVERRIDE)
val d = TypeVariableName("D")
addTypeVariable(d)
addParameter("visitor", elementVisitorType.toPoet().tryParameterizedBy(UNIT, d))
addParameter("data", d)
val visitorParam = ParameterSpec
.builder("visitor", elementVisitorType.toPoet().tryParameterizedBy(UNIT, d)).build()
.also(::addParameter)
val dataParam = ParameterSpec
.builder("data", d).build()
.also(::addParameter)
for (child in element.walkableChildren) {
addStatement(buildString {
append(child.name)
append("%N")
if (child.nullable) append("?")
when (child) {
is SingleField -> append(".accept(visitor, data)")
is ListField -> append(".forEach { it.accept(visitor, data) }")
is SingleField -> append(".%N(%N, %N)")
is ListField -> append(".forEach { it.%N(%N, %N) }")
}
})
}, child.name, acceptMethodName, visitorParam, dataParam)
}
if (isRootElement) {
addKdoc(
"""
Runs the provided [%1N] on subtrees with roots in this node's children.
Basically, calls `%3N(%1N, %2N)` on each child of this node.
Does **not** run [%1N] on this node itself.
@param %1N The visitor for children to accept.
@param %2N An arbitrary context to pass to each invocation of [%1N]'s methods.
""".trimIndent(),
visitorParam,
dataParam,
acceptMethodName,
)
}
}.build())
}
@@ -132,32 +191,38 @@ fun printElements(generationPath: File, model: Model) = sequence {
addModifiers(if (isRootElement) KModifier.ABSTRACT else KModifier.OVERRIDE)
val d = TypeVariableName("D")
addTypeVariable(d)
addParameter("transformer", elementTransformerType.toPoet().tryParameterizedBy(d))
addParameter("data", d)
val transformerParam =
ParameterSpec.builder("transformer", elementTransformerType.toPoet().tryParameterizedBy(d)).build()
.also(::addParameter)
val dataParam = ParameterSpec.builder("data", d).build().also(::addParameter)
for (child in element.transformableChildren) {
val args = mutableListOf<Any>()
val code = buildString {
append("%N")
args.add(child.name)
when (child) {
is SingleField -> {
append(child.name)
append(" = ")
append(child.name)
append(" = %N")
args.add(child.name)
if (child.nullable) append("?")
append(".transform(transformer, data)")
append(".%N(%N, %N)")
args.add(transformMethodName)
}
is ListField -> {
append(child.name)
if (child.mutable) {
append(" = ")
append(child.name)
if (child.nullable) append("?")
}
append(".%M(transformer, data)")
append(".%M(%N, %N)")
args.add(if (child.mutable) transformIfNeeded else transformInPlace)
}
}
args.add(transformerParam)
args.add(dataParam)
if (child is SingleField) {
val elRef = child.type as ElementRef
if (!elRef.element.transform) {
@@ -170,6 +235,24 @@ fun printElements(generationPath: File, model: Model) = sequence {
addStatement(code, *args.toTypedArray())
}
if (isRootElement) {
addKdoc(
"""
Recursively transforms this node's children *in place* using [%1N].
Basically, executes `this.child = this.child.%3N(%1N, %2N)` for each child of this node.
Does **not** run [%1N] on this node itself.
@param %1N The transformer to use for transforming the children.
@param %2N An arbitrary context to pass to each invocation of [%1N]'s methods.
""".trimIndent(),
transformerParam,
dataParam,
transformMethodName,
)
}
}.build())
}