[IR generator] Don't use kotlinpoet for auto-generating IrFactory

KT-61970
This commit is contained in:
Sergej Jaskiewicz
2023-10-16 12:34:08 +03:00
committed by Space Team
parent ebe0a84dee
commit a84277782a
6 changed files with 190 additions and 165 deletions
@@ -8,26 +8,12 @@
package org.jetbrains.kotlin.ir.declarations
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.symbols.IrAnonymousInitializerSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrLocalDelegatedPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeAliasSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
@@ -362,10 +348,7 @@ interface IrFactory {
" This variant of the method will be removed when the 2024.2 IntelliJ platform is shipped (see KTIJ-26314).",
level = DeprecationLevel.HIDDEN,
)
fun createExpressionBody(expression: IrExpression): IrExpressionBody =
createExpressionBody(
expression,
)
fun createExpressionBody(expression: IrExpression): IrExpressionBody = createExpressionBody(expression)
@Deprecated(
message = "The method's parameters were reordered." +
@@ -32,6 +32,8 @@ val elementTransformerType = type(visitors, "IrElementTransformer")
val typeTransformerType = type(visitors, "IrTypeTransformer")
val mutableAnnotationContainerType = type(declarations, "IrMutableAnnotationContainer")
val irTypeType = type(types, "IrType")
val irFactoryType = type(declarations, "IrFactory")
val stageControllerType = type(declarations, "StageController", TypeKind.Class)
val symbolType = type(symbols, "IrSymbol")
val packageFragmentSymbolType = type(symbols, "IrPackageFragmentSymbol")
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.ir.generator.config.ListFieldConfig.Mutability.List
import org.jetbrains.kotlin.ir.generator.config.ListFieldConfig.Mutability.Var
import org.jetbrains.kotlin.ir.generator.config.SimpleFieldConfig
import org.jetbrains.kotlin.ir.generator.model.Element.Companion.elementName2typeName
import org.jetbrains.kotlin.ir.generator.print.IR_FACTORY_TYPE
import org.jetbrains.kotlin.ir.generator.print.toPoet
import org.jetbrains.kotlin.ir.generator.util.*
import org.jetbrains.kotlin.ir.generator.util.Import
@@ -69,7 +68,7 @@ object IrTree : AbstractTreeBuilder() {
}
}
private val factory: SimpleFieldConfig = field("factory", IR_FACTORY_TYPE, mutable = false) {
private val factory: SimpleFieldConfig = field("factory", irFactoryType, mutable = false) {
skipInIrFactory()
}
@@ -132,7 +131,7 @@ object IrTree : AbstractTreeBuilder() {
parent(declarationWithName)
+field("isExternal", boolean) {
useFieldInIrFactory(false)
useFieldInIrFactory(defaultValue = "false")
}
}
val symbolOwner: ElementConfig by element(Declaration) {
@@ -183,7 +182,7 @@ object IrTree : AbstractTreeBuilder() {
parent(declarationWithName)
+field("containerSource", type<DeserializedContainerSource>(), nullable = true, mutable = false) {
useFieldInIrFactory(defaultValue = code("null"))
useFieldInIrFactory(defaultValue = "null")
}
}
val valueDeclaration: ElementConfig by element(Declaration) {
@@ -256,25 +255,25 @@ object IrTree : AbstractTreeBuilder() {
+field("kind", type<ClassKind>())
+field("modality", type<Modality>())
+field("isCompanion", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+field("isInner", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+field("isData", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+field("isValue", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+field("isExpect", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+field("isFun", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+field("hasEnumEntries", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
kDoc = """
Returns true iff this is a class loaded from dependencies which has the `HAS_ENUM_ENTRIES` metadata flag set.
This flag is useful for Kotlin/JVM to determine whether an enum class from dependency actually has the `entries` property
@@ -283,7 +282,7 @@ object IrTree : AbstractTreeBuilder() {
""".trimIndent()
}
+field("source", type<SourceElement>(), mutable = false) {
useFieldInIrFactory(defaultValue = code("%T.NO_SOURCE", SourceElement::class))
useFieldInIrFactory(defaultValue = "SourceElement.NO_SOURCE")
}
+listField("superTypes", irTypeType, mutability = Var) {
skipInIrFactory()
@@ -334,7 +333,7 @@ object IrTree : AbstractTreeBuilder() {
+descriptor("ClassDescriptor") // TODO special descriptor for anonymous initializer blocks
+symbol(anonymousInitializerSymbolType)
+field("isStatic", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+field("body", blockBody, isChild = true)
}
@@ -440,7 +439,7 @@ object IrTree : AbstractTreeBuilder() {
additionalIrFactoryMethodParameters.add(
descriptor("DeclarationDescriptor", nullable = true).apply {
useFieldInIrFactory(defaultValue = code("null"))
useFieldInIrFactory(defaultValue = "null")
}
)
@@ -528,7 +527,7 @@ object IrTree : AbstractTreeBuilder() {
+field("isLateinit", boolean)
+field("isDelegated", boolean)
+field("isExpect", boolean) {
useFieldInIrFactory(defaultValue = false)
useFieldInIrFactory(defaultValue = "false")
}
+isFakeOverrideField()
+field("backingField", field, nullable = true, isChild = true)
@@ -537,12 +536,7 @@ object IrTree : AbstractTreeBuilder() {
}
private fun isFakeOverrideField() = field("isFakeOverride", boolean) {
useFieldInIrFactory(
defaultValue = code(
"origin == %T.FAKE_OVERRIDE",
type(Packages.declarations, "IrDeclarationOrigin").toPoet(),
),
)
useFieldInIrFactory(defaultValue = "origin == IrDeclarationOrigin.FAKE_OVERRIDE")
}
//TODO: make IrScript as IrPackageFragment, because script is used as a file, not as a class
@@ -827,7 +821,7 @@ object IrTree : AbstractTreeBuilder() {
+symbol(constructorSymbolType, mutable = true)
+field("source", type<SourceElement>()) {
useFieldInIrFactory(defaultValue = code("%T.NO_SOURCE", SourceElement::class))
useFieldInIrFactory(defaultValue = "SourceElement.NO_SOURCE")
}
+field("constructorTypeArgumentsCount", int)
}
@@ -10,6 +10,7 @@ import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.ir.generator.BASE_PACKAGE
import org.jetbrains.kotlin.ir.generator.model.Element
import org.jetbrains.kotlin.ir.generator.util.*
class Config(
@@ -73,9 +74,19 @@ class ElementConfig(
override fun toString() = element.name
override val packageName: String
get() = category.packageName
override val typeName: String
get() = Element.elementName2typeName(name)
context(ImportCollector)
override fun renderTo(appendable: Appendable) {
renderingIsNotSupported()
addImport(this)
appendable.append(typeName)
if (params.isNotEmpty()) {
params.joinTo(appendable, prefix = "<", postfix = ">") { it.name }
}
}
enum class Category(private val packageDir: String, val defaultVisitorParam: String) {
@@ -87,7 +98,7 @@ class ElementConfig(
}
}
sealed interface ElementConfigOrRef : ParametrizedTypeRef<ElementConfigOrRef, NamedTypeParameterRef>, TypeRefWithNullability {
sealed interface ElementConfigOrRef : ParametrizedTypeRef<ElementConfigOrRef, NamedTypeParameterRef>, TypeRefWithNullability, Importable {
val element: ElementConfig
}
@@ -96,6 +107,13 @@ class ElementConfigRef(
override val args: Map<NamedTypeParameterRef, TypeRef>,
override val nullable: Boolean
) : ElementConfigOrRef {
override val packageName: String
get() = element.packageName
override val typeName: String
get() = element.typeName
override fun copy(args: Map<NamedTypeParameterRef, TypeRef>) = ElementConfigRef(element, args, nullable)
override fun copy(nullable: Boolean) = ElementConfigRef(element, args, nullable)
@@ -103,7 +121,10 @@ class ElementConfigRef(
context(ImportCollector)
override fun renderTo(appendable: Appendable) {
renderingIsNotSupported()
addImport(element)
appendable.append(element.typeName)
renderArgsTo(appendable)
renderNullabilityTo(appendable)
}
}
@@ -111,7 +132,7 @@ sealed class UseFieldAsParameterInIrFactoryStrategy {
data object No : UseFieldAsParameterInIrFactoryStrategy()
data class Yes(val defaultValue: CodeBlock?) : UseFieldAsParameterInIrFactoryStrategy()
data class Yes(val defaultValue: String?) : UseFieldAsParameterInIrFactoryStrategy()
}
sealed class FieldConfig(
@@ -126,14 +147,10 @@ sealed class FieldConfig(
internal var useFieldInIrFactoryStrategy: UseFieldAsParameterInIrFactoryStrategy =
if (isChild) UseFieldAsParameterInIrFactoryStrategy.No else UseFieldAsParameterInIrFactoryStrategy.Yes(null)
fun useFieldInIrFactory(defaultValue: CodeBlock? = null) {
fun useFieldInIrFactory(defaultValue: String? = null) {
useFieldInIrFactoryStrategy = UseFieldAsParameterInIrFactoryStrategy.Yes(defaultValue)
}
fun useFieldInIrFactory(defaultValue: Boolean) {
useFieldInIrFactoryStrategy = UseFieldAsParameterInIrFactoryStrategy.Yes(code("%L", defaultValue))
}
fun skipInIrFactory() {
useFieldInIrFactoryStrategy = UseFieldAsParameterInIrFactoryStrategy.No
}
@@ -5,66 +5,48 @@
package org.jetbrains.kotlin.ir.generator.print
import com.squareup.kotlinpoet.*
import org.jetbrains.kotlin.generators.tree.*
import org.jetbrains.kotlin.generators.tree.printer.FunctionParameter
import org.jetbrains.kotlin.generators.tree.printer.GeneratedFile
import org.jetbrains.kotlin.generators.tree.type
import org.jetbrains.kotlin.generators.tree.printer.printFunctionDeclaration
import org.jetbrains.kotlin.generators.tree.printer.printGeneratedType
import org.jetbrains.kotlin.ir.generator.IrTree
import org.jetbrains.kotlin.ir.generator.Packages
import org.jetbrains.kotlin.ir.generator.config.UseFieldAsParameterInIrFactoryStrategy
import org.jetbrains.kotlin.ir.generator.irFactoryType
import org.jetbrains.kotlin.ir.generator.model.Element
import org.jetbrains.kotlin.ir.generator.model.Model
import org.jetbrains.kotlin.ir.generator.util.parameterizedByIfAny
import org.jetbrains.kotlin.ir.generator.util.tryParameterizedBy
import org.jetbrains.kotlin.ir.generator.stageControllerType
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import org.jetbrains.kotlin.utils.addToStdlib.applyIf
import org.jetbrains.kotlin.utils.SmartPrinter
import org.jetbrains.kotlin.utils.withIndent
import java.io.File
internal val IR_FACTORY_TYPE = type(Packages.declarations, "IrFactory")
@Suppress("DuplicatedCode")
internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
val visitorType = TypeSpec.interfaceBuilder(IR_FACTORY_TYPE.toPoet() as ClassName).apply {
addProperty("stageController", ClassName(Packages.declarations, "StageController"))
model.elements
internal fun printFactory(generationPath: File, model: Model): GeneratedFile = printGeneratedType(
generationPath,
TREE_GENERATOR_README,
irFactoryType.packageName,
irFactoryType.simpleName,
) {
println()
println("interface ", irFactoryType.simpleName, " {")
withIndent {
println("val stageController: ", stageControllerType.render())
val factoryMethods = model.elements
.filter { it.isLeaf && it.generateIrFactoryMethod }
.sortedWith(compareBy({ it.packageName }, { it.name }))
.forEach { element ->
val typeParams = element.params.map { it.toPoet() }
addFunction(
FunSpec.builder("create${element.name.capitalizeAsciiOnly()}")
.addTypeVariables(typeParams)
.apply {
val fields = (element.allFieldsRecursively() + element.additionalFactoryMethodParameters)
.filterNot { it.name in element.fieldsToSkipInIrFactoryMethod }
.mapNotNull { field ->
(field.useInIrFactoryStrategy as? UseFieldAsParameterInIrFactoryStrategy.Yes)?.let {
field to it.defaultValue
}
}
.sortedBy { (_, defaultValue) -> defaultValue != null } // All parameters with default values must go last
fields.forEach { (field, defaultValue) ->
addParameter(
ParameterSpec.builder(field.name, field.typeRef.toPoet().copy(nullable = field.nullable))
.defaultValue(defaultValue)
.build(),
)
}
}
.returns(element.toPoet().parameterizedByIfAny(typeParams))
.addModifiers(KModifier.ABSTRACT)
.build(),
)
}
.map(::FactoryMethod)
fun replacement(name: String) = funSpecs.find { it.name == name } ?: error("Method '$name' not found")
factoryMethods.forEach { printFactoryMethod(it) }
fun replacement(name: String) = factoryMethods.find { it.name == name } ?: error("Method '$name' not found")
addDeprecatedFunction(
replacement("createBlockBody")
.toBuilder()
.addParameter(
"initializer",
LambdaTypeName.get(receiver = IrTree.blockBody.toPoet(), returnType = UNIT)
)
.build()
Lambda(receiver = IrTree.blockBody, returnType = StandardTypes.unit),
),
) {
deprecationMessage = "This method was moved to an extension."
parameter("startOffset")
@@ -74,12 +56,10 @@ internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
addDeprecatedFunction(
replacement("createBlockBody")
.toBuilder()
.addParameter(
"statements",
LIST.tryParameterizedBy(IrTree.statement.toPoet())
)
.build()
StandardTypes.list.withArgs(IrTree.statement),
),
) {
deprecationMessage = "This method was moved to an extension."
parameter("startOffset")
@@ -104,7 +84,7 @@ internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
parameter("isExpect")
parameter("isFun")
parameter("source")
defaultValue["hasEnumEntries"] = false
defaultValue["hasEnumEntries"] = "false"
}
addDeprecatedFunction(replacement("createConstructor")) {
@@ -149,7 +129,7 @@ internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
}
addDeprecatedFunction(replacement("createFunctionWithLateBinding")) {
returnType = IrTree.simpleFunction.toPoet()
returnType = IrTree.simpleFunction
parameter("startOffset")
parameter("endOffset")
parameter("origin")
@@ -195,7 +175,7 @@ internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
}
addDeprecatedFunction(replacement("createPropertyWithLateBinding")) {
returnType = IrTree.property.toPoet()
returnType = IrTree.property
parameter("startOffset")
parameter("endOffset")
parameter("origin")
@@ -267,37 +247,64 @@ internal fun printFactory(generationPath: File, model: Model): GeneratedFile {
parameter("isHidden")
parameter("isAssignable")
}
}.build()
return printTypeCommon(generationPath, IR_FACTORY_TYPE.packageName, visitorType)
}
println("}")
}
private class DeprecatedFunctionBuilder(private val replacement: FunSpec) {
val deprecatedFunctionParameterSpecs = mutableListOf<ParameterSpec>()
private class FactoryMethod(val element: Element) {
val name = "create" + element.name.capitalizeAsciiOnly()
val parameters = (element.allFieldsRecursively() + element.additionalFactoryMethodParameters)
.filterNot { it.name in element.fieldsToSkipInIrFactoryMethod }
.mapNotNull { field ->
(field.useInIrFactoryStrategy as? UseFieldAsParameterInIrFactoryStrategy.Yes)?.let {
FunctionParameter(field.name, field.typeRef, it.defaultValue)
}
}
.sortedBy { it.defaultValue != null } // All parameters with default values must go last
.toMutableList()
fun addParameter(name: String, type: TypeRef): FactoryMethod {
parameters.add(FunctionParameter(name, type))
return this
}
}
private const val MAX_FUNCTION_PARAMETERS_ON_ONE_LINE = 2
context(ImportCollector)
private fun SmartPrinter.printFactoryMethod(factoryMethod: FactoryMethod) {
println()
printFunctionDeclaration(
name = factoryMethod.name,
parameters = factoryMethod.parameters,
returnType = factoryMethod.element,
typeParameters = factoryMethod.element.params,
allParametersOnSeparateLines = factoryMethod.parameters.size > MAX_FUNCTION_PARAMETERS_ON_ONE_LINE,
)
println()
}
private class DeprecatedFunctionBuilder(private val replacement: FactoryMethod) {
val deprecatedFunctionParameters = mutableListOf<FunctionParameter>()
var oldName = replacement.name
var returnType = replacement.returnType
var returnType: TypeRef = replacement.element
var deprecationMessage: String? = null
val defaultValue = mutableMapOf<String, Any?>()
val defaultValue = mutableMapOf<String, String>()
fun parameter(name: String, removeDefaultValue: Boolean = false) {
val replacementParameter =
replacement.parameters.find { it.name == name } ?: error("Parameter '$name' not found in $replacement")
deprecatedFunctionParameterSpecs.add(
replacementParameter
.toBuilder()
.applyIf(removeDefaultValue) {
defaultValue(null)
}
.build()
)
deprecatedFunctionParameters.add(if (removeDefaultValue) replacementParameter.copy(defaultValue = null) else replacementParameter)
}
}
private fun TypeSpec.Builder.addDeprecatedFunction(
replacement: FunSpec,
context(ImportCollector)
private fun SmartPrinter.addDeprecatedFunction(
replacement: FactoryMethod,
build: DeprecatedFunctionBuilder.() -> Unit
): TypeSpec.Builder {
) {
val builder = DeprecatedFunctionBuilder(replacement)
builder.build()
@@ -307,46 +314,39 @@ private fun TypeSpec.Builder.addDeprecatedFunction(
"The method's parameters were reordered."
}
return addFunction(
FunSpec.builder(builder.oldName)
.addTypeVariables(replacement.typeVariables)
.addCode(
CodeBlock.builder()
.add("return ")
.add("%N(\n", replacement)
.apply {
for (parameter in replacement.parameters) {
if (builder.deprecatedFunctionParameterSpecs.any { it.name == parameter.name }) {
indent()
add("%N,\n", parameter)
unindent()
} else {
val value = builder.defaultValue[parameter.name]
if (value != null) {
indent()
add("$value,\n")
unindent()
}
}
}
}
.add(")")
.build()
)
.addAnnotation(
AnnotationSpec.builder(Deprecated::class)
.addMember(
"message = %S + %S",
message,
" This variant of the method will be removed when the 2024.2 IntelliJ platform is shipped (see KTIJ-26314).",
)
.addMember("level = %T.%N", DeprecationLevel::class, DeprecationLevel.HIDDEN.name)
.build(),
)
.addParameters(builder.deprecatedFunctionParameterSpecs)
.apply {
builder.returnType?.let { returns(it) }
}
.build(),
println()
println("@Deprecated(")
withIndent {
println("message = \"", message, "\" +")
println(" \" This variant of the method will be removed when the 2024.2 IntelliJ platform is shipped (see KTIJ-26314).\",")
println("level = DeprecationLevel.HIDDEN,")
}
println(")")
val allParametersOnSeparateLines = builder.deprecatedFunctionParameters.size > MAX_FUNCTION_PARAMETERS_ON_ONE_LINE
printFunctionDeclaration(
name = builder.oldName,
parameters = builder.deprecatedFunctionParameters,
returnType = builder.returnType,
typeParameters = replacement.element.params,
allParametersOnSeparateLines = allParametersOnSeparateLines,
)
}
print(" = ", replacement.name, "(")
val renderedParameters = replacement.parameters.mapNotNull { parameter ->
if (builder.deprecatedFunctionParameters.any { it.name == parameter.name }) {
parameter.name
} else {
builder.defaultValue[parameter.name]
}
}
if (allParametersOnSeparateLines) {
println()
withIndent {
renderedParameters.forEach { println(it, ",") }
}
} else {
print(renderedParameters.joinToString())
}
println(")")
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.generators.tree
import org.jetbrains.kotlin.generators.tree.printer.braces
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.joinToWithBuffer
import java.util.*
import kotlin.reflect.KClass
@@ -168,6 +169,34 @@ data class ElementRef<Element : AbstractElement<Element, Field>, Field : Abstrac
}
}
data class Lambda(
val receiver: TypeRefWithNullability?,
val parameterTypes: List<TypeRefWithNullability> = emptyList(),
val returnType: TypeRefWithNullability,
override val nullable: Boolean = false,
) : TypeRefWithNullability {
override fun substitute(map: TypeParameterSubstitutionMap) =
Lambda(
receiver?.substitute(map) as TypeRefWithNullability?,
parameterTypes.map { it.substitute(map) as TypeRefWithNullability },
returnType.substitute(map) as TypeRefWithNullability,
nullable,
)
context(ImportCollector)
override fun renderTo(appendable: Appendable) {
if (nullable) appendable.append("(")
receiver?.let {
it.renderTo(appendable)
appendable.append('.')
}
parameterTypes.joinToWithBuffer(appendable, prefix = "(", postfix = ") -> ") { it.renderTo(this) }
returnType.renderTo(appendable)
if (nullable) appendable.append(")?")
}
override fun copy(nullable: Boolean) = Lambda(receiver, parameterTypes, returnType, nullable)
}
sealed interface TypeParameterRef : TypeRef, TypeRefWithNullability {
override fun substitute(map: TypeParameterSubstitutionMap): TypeRef = map[this] ?: this
@@ -216,7 +245,7 @@ interface TypeRefWithNullability : TypeRef {
fun copy(nullable: Boolean): TypeRefWithNullability
}
private fun TypeRefWithNullability.renderNullabilityTo(appendable: Appendable) {
fun TypeRefWithNullability.renderNullabilityTo(appendable: Appendable) {
if (nullable) {
appendable.append('?')
}
@@ -232,7 +261,7 @@ interface ParametrizedTypeRef<Self : ParametrizedTypeRef<Self, P>, P : TypeParam
}
context(ImportCollector)
private fun ParametrizedTypeRef<*, *>.renderArgsTo(appendable: Appendable) {
fun ParametrizedTypeRef<*, *>.renderArgsTo(appendable: Appendable) {
if (args.isNotEmpty()) {
args.values.joinTo(appendable, prefix = "<", postfix = ">") {
it.renderTo(appendable)