Repository: JetBrains/kotlin
File: plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt (Kotlin)

Code before:
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
import org.jetbrains.kotlin.utils.getOrPutNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD

class SerializableIrGenerator(
    val irClass: IrClass,
    override val compilerContext: SerializationPluginContext,
    bindingContext: BindingContext
) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension {

    private val serialDescClass: ClassDescriptor = serializableDescriptor.module
        .getClassFromSerializationDescriptorsPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS)

    private val serialDescImplClass: ClassDescriptor = serializableDescriptor
        .getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL)

    private val addElementFun = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement)

    private val IrClass.isInternalSerializable: Boolean get() = this.descriptor.isInternalSerializable

    override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) =
        irClass.contributeConstructor(constructorDescriptor) { ctor ->
            val thiz = irClass.thisReceiver!!
            val serializableProperties = properties.serializableProperties

            val serialDescs = serializableProperties.map { it.descriptor }.toSet()

            val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? =
                createPropertyByParamReplacer(irClass, serializableProperties, thiz, bindingContext)

            val initializerAdapter: (IrExpressionBody) -> IrExpression = createInitializerAdapter(irClass, propertyByParamReplacer)


            var current: PropertyDescriptor? = null
            val statementsAfterSerializableProperty: MutableMap<PropertyDescriptor?, MutableList<IrStatement>> = mutableMapOf()
            irClass.declarations.asSequence().forEach {
                when {
                    // only properties with backing field
                    it is IrProperty && it.backingField != null -> {
                        if (it.descriptor in serialDescs) {
                            current = it.descriptor
                        } else if (it.backingField?.initializer != null) {
                            // skip transient lateinit or deferred properties (with null initializer)
                            val expression = initializerAdapter(it.backingField!!.initializer!!)

Review comment (bug):
In general, it's better to avoid using descriptors in IR transformations because it's considered deprecated and dangerous API, which can sometimes lead to 'unbound symbol' bugs. I think it's possible to modify `IrClass.hasSerializableAnnotationWithoutArgs(): Boolean` function so it would take into account meta-annotations, right?

Code after:
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
import org.jetbrains.kotlin.utils.getOrPutNullable
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.serializableAnnotationIsUseless
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.CACHED_DESCRIPTOR_FIELD_NAME
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MISSING_FIELD_EXC
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIAL_DESC_FIELD

class SerializableIrGenerator(
    val irClass: IrClass,
    override val compilerContext: SerializationPluginContext,
    bindingContext: BindingContext
) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension {

    private val serialDescClass: ClassDescriptor = serializableDescriptor.module
        .getClassFromSerializationDescriptorsPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS)

    private val serialDescImplClass: ClassDescriptor = serializableDescriptor
        .getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS_IMPL)

    private val addElementFun = serialDescImplClass.referenceFunctionSymbol(CallingConventions.addElement)

    private fun IrClass.hasSerializableOrMetaAnnotationWithoutArgs(): Boolean {
        val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName)
        if (annot != null) {
            for (i in 0 until annot.valueArgumentsCount) {
                if (annot.getValueArgument(i) != null) return false
            }
            return true
        }
        val metaAnnotation = annotations
            .flatMap { it.symbol.owner.constructedClass.annotations }
            .find { it.isAnnotation(SerializationAnnotations.metaSerializableAnnotationFqName) }
        return metaAnnotation != null
    }

    private val IrClass.isInternalSerializable: Boolean get() = kind == ClassKind.CLASS && hasSerializableOrMetaAnnotationWithoutArgs()

    override fun generateInternalConstructor(constructorDescriptor: ClassConstructorDescriptor) =
        irClass.contributeConstructor(constructorDescriptor) { ctor ->
            val thiz = irClass.thisReceiver!!
            val serializableProperties = properties.serializableProperties

            val serialDescs = serializableProperties.map { it.descriptor }.toSet()

            val propertyByParamReplacer: (ValueParameterDescriptor) -> IrExpression? =
                createPropertyByParamReplacer(irClass, serializableProperties, thiz, bindingContext)