|  | 
|  | 1 | +package com.sschr15.aoc.compiler.internal | 
|  | 2 | + | 
|  | 3 | +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext | 
|  | 4 | +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder | 
|  | 5 | +import org.jetbrains.kotlin.backend.common.lower.irIfThen | 
|  | 6 | +import org.jetbrains.kotlin.backend.common.lower.irThrow | 
|  | 7 | +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity | 
|  | 8 | +import org.jetbrains.kotlin.cli.jvm.compiler.report | 
|  | 9 | +import org.jetbrains.kotlin.config.CompilerConfiguration | 
|  | 10 | +import org.jetbrains.kotlin.ir.IrStatement | 
|  | 11 | +import org.jetbrains.kotlin.ir.builders.* | 
|  | 12 | +import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase | 
|  | 13 | +import org.jetbrains.kotlin.ir.declarations.IrFunction | 
|  | 14 | +import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration | 
|  | 15 | +import org.jetbrains.kotlin.ir.declarations.IrVariable | 
|  | 16 | +import org.jetbrains.kotlin.ir.expressions.IrSyntheticBody | 
|  | 17 | +import org.jetbrains.kotlin.ir.types.starProjectedType | 
|  | 18 | +import org.jetbrains.kotlin.ir.util.* | 
|  | 19 | +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid | 
|  | 20 | +import org.jetbrains.kotlin.name.FqName | 
|  | 21 | +import org.jetbrains.kotlin.name.SpecialNames | 
|  | 22 | + | 
|  | 23 | +class IrDestructuringFinder( | 
|  | 24 | +    val context: IrPluginContext, | 
|  | 25 | +    val config: CompilerConfiguration, | 
|  | 26 | +) : IrElementTransformerVoid() { | 
|  | 27 | +    override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement { | 
|  | 28 | +        if (declaration.annotations.any { it.isAnnotationWithEqualFqName(FqName("com.sschr15.aoc.annotations.SkipDestructuringChecks")) }) { | 
|  | 29 | +            return declaration | 
|  | 30 | +        } | 
|  | 31 | + | 
|  | 32 | +        return super.visitDeclaration(declaration) | 
|  | 33 | +    } | 
|  | 34 | + | 
|  | 35 | +    val extra = ExtraPluginStuff(context) | 
|  | 36 | +    val destructuringMap = config.getMap(DestructuringFinder) | 
|  | 37 | + | 
|  | 38 | +    override fun visitFunction(declaration: IrFunction): IrStatement { | 
|  | 39 | +        if (declaration.annotations.any { it.isAnnotationWithEqualFqName(FqName("com.sschr15.aoc.annotations.SkipDestructuringChecks")) }) { | 
|  | 40 | +            return declaration | 
|  | 41 | +        } | 
|  | 42 | + | 
|  | 43 | +        val destructs = destructuringMap[declaration.getPackageFragment().packageFqName.child(declaration.name)] | 
|  | 44 | +            ?: return super.visitFunction(declaration) | 
|  | 45 | + | 
|  | 46 | +        var currentDestruct = 0 | 
|  | 47 | +        if (declaration.body is IrSyntheticBody) return super.visitFunction(declaration) // synthetic bodies have no statements | 
|  | 48 | +        val statements = declaration.body?.statements?.toMutableList() ?: return super.visitFunction(declaration) | 
|  | 49 | + | 
|  | 50 | +        fun runOnStatement(stat: IrValueDeclaration): IrStatement { | 
|  | 51 | +            val expected = destructs[currentDestruct++] | 
|  | 52 | +            return context.irBuiltIns.createIrBuilder(stat.symbol, stat.startOffset, stat.endOffset).irBlock { | 
|  | 53 | +                +irIfThen( | 
|  | 54 | +                    condition = irCall(context.irBuiltIns.andandSymbol).apply { | 
|  | 55 | +                        putValueArgument(0, irIs(irGet(stat), context.irBuiltIns.collectionClass.starProjectedType)) | 
|  | 56 | +                        putValueArgument(1, irNotEquals( | 
|  | 57 | +                            irCall(extra.sizeFunctionSymbol).apply { | 
|  | 58 | +                                dispatchReceiver = irGet(stat) | 
|  | 59 | +                            }, | 
|  | 60 | +                            irInt(expected) | 
|  | 61 | +                        )) | 
|  | 62 | +                    }, | 
|  | 63 | +                    thenPart=irThrow(irCallConstructor(extra.illegalArgumentExceptionCtor, emptyList()).apply { | 
|  | 64 | +                        putValueArgument(0, irConcat().apply { | 
|  | 65 | +                            arguments.addAll(listOf( | 
|  | 66 | +                                irString("expected to destruct $expected elements, but "), | 
|  | 67 | +                                irCall(extra.sizeFunctionSymbol).apply { | 
|  | 68 | +                                    dispatchReceiver = irGet(stat) | 
|  | 69 | +                                }, | 
|  | 70 | +                                irString(" were provided") | 
|  | 71 | +                            )) | 
|  | 72 | +                        }) | 
|  | 73 | +                    }), | 
|  | 74 | +                ) | 
|  | 75 | +            } | 
|  | 76 | +        } | 
|  | 77 | + | 
|  | 78 | +        //TODO: handle destructuring in parameters | 
|  | 79 | +//        statements.addAll( | 
|  | 80 | +//            0, | 
|  | 81 | +//            declaration.valueParameters | 
|  | 82 | +//                .filter { it.name == SpecialNames.DESTRUCT } | 
|  | 83 | +//                .map { stat -> runOnStatement(stat) } | 
|  | 84 | +//        ) | 
|  | 85 | + | 
|  | 86 | +        statements.transformFlat { stat -> | 
|  | 87 | +            if (stat !is IrVariable) return@transformFlat null | 
|  | 88 | +            if (stat.name != SpecialNames.DESTRUCT) return@transformFlat null | 
|  | 89 | +            if (currentDestruct >= destructs.size) { | 
|  | 90 | +                config.report(CompilerMessageSeverity.STRONG_WARNING, "Destructuring ${stat.dumpKotlinLike()}, but no destructuring count was provided") | 
|  | 91 | +                return@transformFlat null | 
|  | 92 | +            } | 
|  | 93 | + | 
|  | 94 | +            listOf( | 
|  | 95 | +                stat, | 
|  | 96 | +                runOnStatement(stat), | 
|  | 97 | +            ) | 
|  | 98 | +        } | 
|  | 99 | +        return super.visitFunction(declaration) | 
|  | 100 | +    } | 
|  | 101 | +} | 
0 commit comments