From de7de7b287722baf189e11c8a876c645e27d620d Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Thu, 16 Mar 2023 15:49:28 +0100 Subject: [PATCH] [IR] Add kdoc for IrElement --- .../gen/org/jetbrains/kotlin/ir/IrElement.kt | 57 +++++++- .../jetbrains/kotlin/ir/generator/IrTree.kt | 17 ++- .../kotlin/ir/generator/print/Elements.kt | 127 +++++++++++++++--- 3 files changed, 176 insertions(+), 25 deletions(-) diff --git a/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/IrElement.kt b/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/IrElement.kt index 9fd73d54c11..fed5dfa9fd2 100644 --- a/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/IrElement.kt +++ b/compiler/ir/ir.tree/gen/org/jetbrains/kotlin/ir/IrElement.kt @@ -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 accept(visitor: IrElementVisitor, 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 transform(transformer: IrElementTransformer, 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 acceptChildren(visitor: IrElementVisitor, 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 transformChildren(transformer: IrElementTransformer, data: D) } diff --git a/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/IrTree.kt b/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/IrTree.kt index 5a7adcb506a..a2a9d33967d 100644 --- a/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/IrTree.kt +++ b/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/IrTree.kt @@ -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) diff --git a/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/print/Elements.kt b/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/print/Elements.kt index bb691fc8326..7ed60053bff 100644 --- a/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/print/Elements.kt +++ b/compiler/ir/ir.tree/tree-generator/src/org/jetbrains/kotlin/ir/generator/print/Elements.kt @@ -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() 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()) }