Skip to main content

oxilean_codegen/typescript_backend/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::types::{
6    TSAnalysisCache, TSConstantFoldingHelper, TSDepGraph, TSDominatorTree, TSLivenessInfo,
7    TSPassConfig, TSPassPhase, TSPassRegistry, TSPassStats, TSWorklist, TsClass, TsClassField,
8    TsClassMethod, TsDeclaration, TsEnum, TsEnumMember, TsExpr, TsExtConfig, TsExtDiagCollector,
9    TsExtDiagMsg, TsExtEmitStats, TsExtEventLog, TsExtFeatures, TsExtIdGen, TsExtIncrKey,
10    TsExtNameScope, TsExtPassTiming, TsExtProfiler, TsExtSourceBuffer, TsExtVersion, TsFunction,
11    TsImport, TsInterface, TsInterfaceMember, TsLit, TsModule, TsParam, TsStmt, TsTemplatePart,
12    TsType, TsTypeAlias, TypeScriptBackend,
13};
14
15pub(super) fn format_ts_stmt(stmt: &TsStmt, indent: usize) -> std::string::String {
16    let pad = " ".repeat(indent);
17    let _ipad = " ".repeat(indent + 2);
18    match stmt {
19        TsStmt::Expr(e) => format!("{};", e),
20        TsStmt::Const(name, ty, expr) => {
21            if let Some(t) = ty {
22                format!("const {}: {} = {};", name, t, expr)
23            } else {
24                format!("const {} = {};", name, expr)
25            }
26        }
27        TsStmt::Let(name, ty, expr) => {
28            if let Some(t) = ty {
29                format!("let {}: {} = {};", name, t, expr)
30            } else {
31                format!("let {} = {};", name, expr)
32            }
33        }
34        TsStmt::Var(name, ty, expr) => {
35            if let Some(t) = ty {
36                format!("var {}: {} = {};", name, t, expr)
37            } else {
38                format!("var {} = {};", name, expr)
39            }
40        }
41        TsStmt::If(cond, then_s, else_s) => {
42            let then_body = format_ts_stmts(then_s, indent + 2);
43            let mut s = format!("if ({}) {{\n{}\n{}}}", cond, then_body, pad);
44            if !else_s.is_empty() {
45                let else_body = format_ts_stmts(else_s, indent + 2);
46                s.push_str(&format!(" else {{\n{}\n{}}}", else_body, pad));
47            }
48            s
49        }
50        TsStmt::Switch(expr, cases, default) => {
51            let mut s = format!("switch ({}) {{\n", expr);
52            for (case_val, case_stmts) in cases {
53                s.push_str(&format!("{}  case {}:\n", pad, case_val));
54                for cs in case_stmts {
55                    s.push_str(&format!("{}    {}\n", pad, format_ts_stmt(cs, indent + 4)));
56                }
57                s.push_str(&format!("{}    break;\n", pad));
58            }
59            if !default.is_empty() {
60                s.push_str(&format!("{}  default:\n", pad));
61                for ds in default {
62                    s.push_str(&format!("{}    {}\n", pad, format_ts_stmt(ds, indent + 4)));
63                }
64            }
65            s.push_str(&format!("{}}}", pad));
66            s
67        }
68        TsStmt::For(init, cond, step, body) => {
69            let body_text = format_ts_stmts(body, indent + 2);
70            format!(
71                "for ({}; {}; {}) {{\n{}\n{}}}",
72                format_ts_stmt(init, 0).trim_end_matches(';'),
73                cond,
74                step,
75                body_text,
76                pad
77            )
78        }
79        TsStmt::ForOf(var, iter, body) => {
80            let body_text = format_ts_stmts(body, indent + 2);
81            format!(
82                "for (const {} of {}) {{\n{}\n{}}}",
83                var, iter, body_text, pad
84            )
85        }
86        TsStmt::ForIn(var, obj, body) => {
87            let body_text = format_ts_stmts(body, indent + 2);
88            format!(
89                "for (const {} in {}) {{\n{}\n{}}}",
90                var, obj, body_text, pad
91            )
92        }
93        TsStmt::While(cond, body) => {
94            let body_text = format_ts_stmts(body, indent + 2);
95            format!("while ({}) {{\n{}\n{}}}", cond, body_text, pad)
96        }
97        TsStmt::Return(e) => format!("return {};", e),
98        TsStmt::Throw(e) => format!("throw {};", e),
99        TsStmt::TryCatch(try_body, catch_var, catch_body, fin_body) => {
100            let try_text = format_ts_stmts(try_body, indent + 2);
101            let catch_text = format_ts_stmts(catch_body, indent + 2);
102            let mut s = format!(
103                "try {{\n{}\n{}}} catch ({}) {{\n{}\n{}}}",
104                try_text, pad, catch_var, catch_text, pad
105            );
106            if !fin_body.is_empty() {
107                let fin_text = format_ts_stmts(fin_body, indent + 2);
108                s.push_str(&format!(" finally {{\n{}\n{}}}", fin_text, pad));
109            }
110            s
111        }
112        TsStmt::Block(stmts) => {
113            let body = format_ts_stmts(stmts, indent + 2);
114            format!("{{\n{}\n{}}}", body, pad)
115        }
116        TsStmt::Break => "break;".to_string(),
117        TsStmt::Continue => "continue;".to_string(),
118    }
119}
120pub(super) fn format_ts_stmts(stmts: &[TsStmt], indent: usize) -> std::string::String {
121    let pad = " ".repeat(indent);
122    let mut out = std::string::String::new();
123    for stmt in stmts {
124        let text = format_ts_stmt(stmt, indent);
125        for line in text.lines() {
126            out.push_str(&pad);
127            out.push_str(line);
128            out.push('\n');
129        }
130    }
131    if out.ends_with('\n') {
132        out.pop();
133    }
134    out
135}
136#[cfg(test)]
137mod tests {
138    use super::*;
139    #[test]
140    pub(super) fn test_ts_type_primitives() {
141        assert_eq!(TsType::Number.to_string(), "number");
142        assert_eq!(TsType::String.to_string(), "string");
143        assert_eq!(TsType::Boolean.to_string(), "boolean");
144        assert_eq!(TsType::Void.to_string(), "void");
145        assert_eq!(TsType::Never.to_string(), "never");
146        assert_eq!(TsType::Unknown.to_string(), "unknown");
147        assert_eq!(TsType::Any.to_string(), "any");
148        assert_eq!(TsType::Null.to_string(), "null");
149        assert_eq!(TsType::Undefined.to_string(), "undefined");
150    }
151    #[test]
152    pub(super) fn test_ts_type_array() {
153        let t = TsType::Array(Box::new(TsType::Number));
154        assert_eq!(t.to_string(), "number[]");
155    }
156    #[test]
157    pub(super) fn test_ts_type_tuple() {
158        let t = TsType::Tuple(vec![TsType::Number, TsType::String, TsType::Boolean]);
159        assert_eq!(t.to_string(), "[number, string, boolean]");
160    }
161    #[test]
162    pub(super) fn test_ts_type_union() {
163        let t = TsType::Union(vec![TsType::Number, TsType::Null, TsType::Undefined]);
164        assert_eq!(t.to_string(), "number | null | undefined");
165    }
166    #[test]
167    pub(super) fn test_ts_type_intersection() {
168        let t = TsType::Intersection(vec![
169            TsType::Custom("Serializable".to_string()),
170            TsType::Custom("Loggable".to_string()),
171        ]);
172        assert_eq!(t.to_string(), "Serializable & Loggable");
173    }
174    #[test]
175    pub(super) fn test_ts_type_function() {
176        let t = TsType::Function {
177            params: vec![TsType::Number, TsType::String],
178            ret: Box::new(TsType::Boolean),
179        };
180        assert_eq!(t.to_string(), "(p0: number, p1: string) => boolean");
181    }
182    #[test]
183    pub(super) fn test_ts_type_generic() {
184        let t = TsType::Generic("Promise".to_string(), vec![TsType::String]);
185        assert_eq!(t.to_string(), "Promise<string>");
186    }
187    #[test]
188    pub(super) fn test_ts_type_generic_map() {
189        let t = TsType::Generic("Map".to_string(), vec![TsType::String, TsType::Number]);
190        assert_eq!(t.to_string(), "Map<string, number>");
191    }
192    #[test]
193    pub(super) fn test_ts_type_readonly() {
194        let t = TsType::ReadOnly(Box::new(TsType::Array(Box::new(TsType::Number))));
195        assert_eq!(t.to_string(), "readonly number[]");
196    }
197    #[test]
198    pub(super) fn test_ts_lit_number() {
199        assert_eq!(TsLit::Num(42.0).to_string(), "42");
200        assert_eq!(TsLit::Num(3.14).to_string(), "3.14");
201    }
202    #[test]
203    pub(super) fn test_ts_lit_string_escape() {
204        assert_eq!(TsLit::Str("hello".to_string()).to_string(), "\"hello\"");
205        assert_eq!(
206            TsLit::Str("say \"hi\"".to_string()).to_string(),
207            "\"say \\\"hi\\\"\""
208        );
209    }
210    #[test]
211    pub(super) fn test_ts_lit_bool_and_null() {
212        assert_eq!(TsLit::Bool(true).to_string(), "true");
213        assert_eq!(TsLit::Bool(false).to_string(), "false");
214        assert_eq!(TsLit::Null.to_string(), "null");
215        assert_eq!(TsLit::Undefined.to_string(), "undefined");
216    }
217    #[test]
218    pub(super) fn test_ts_expr_binop() {
219        let e = TsExpr::BinOp(
220            "+".to_string(),
221            Box::new(TsExpr::Lit(TsLit::Num(1.0))),
222            Box::new(TsExpr::Lit(TsLit::Num(2.0))),
223        );
224        assert_eq!(e.to_string(), "(1 + 2)");
225    }
226    #[test]
227    pub(super) fn test_ts_expr_ternary() {
228        let e = TsExpr::Ternary(
229            Box::new(TsExpr::Var("cond".to_string())),
230            Box::new(TsExpr::Lit(TsLit::Num(1.0))),
231            Box::new(TsExpr::Lit(TsLit::Num(0.0))),
232        );
233        assert_eq!(e.to_string(), "(cond) ? (1) : (0)");
234    }
235    #[test]
236    pub(super) fn test_ts_expr_as() {
237        let e = TsExpr::As(Box::new(TsExpr::Var("x".to_string())), TsType::Number);
238        assert_eq!(e.to_string(), "(x as number)");
239    }
240    #[test]
241    pub(super) fn test_ts_expr_satisfies() {
242        let e = TsExpr::Satisfies(
243            Box::new(TsExpr::Var("config".to_string())),
244            TsType::Custom("Config".to_string()),
245        );
246        assert_eq!(e.to_string(), "(config satisfies Config)");
247    }
248    #[test]
249    pub(super) fn test_ts_expr_template_literal() {
250        let e = TsExpr::Template(vec![
251            TsTemplatePart::Text("Hello, ".to_string()),
252            TsTemplatePart::Expr(TsExpr::Var("name".to_string())),
253            TsTemplatePart::Text("!".to_string()),
254        ]);
255        assert_eq!(e.to_string(), "`Hello, ${name}!`");
256    }
257    #[test]
258    pub(super) fn test_ts_expr_nullish() {
259        let e = TsExpr::Nullish(
260            Box::new(TsExpr::Var("a".to_string())),
261            Box::new(TsExpr::Lit(TsLit::Str("default".to_string()))),
262        );
263        assert_eq!(e.to_string(), "(a ?? \"default\")");
264    }
265    #[test]
266    pub(super) fn test_ts_expr_optchain() {
267        let e = TsExpr::OptChain(
268            Box::new(TsExpr::Var("user".to_string())),
269            "address".to_string(),
270        );
271        assert_eq!(e.to_string(), "user?.address");
272    }
273    #[test]
274    pub(super) fn test_ts_expr_object_lit() {
275        let e = TsExpr::ObjectLit(vec![
276            (
277                "kind".to_string(),
278                TsExpr::Lit(TsLit::Str("circle".to_string())),
279            ),
280            ("radius".to_string(), TsExpr::Lit(TsLit::Num(5.0))),
281        ]);
282        assert_eq!(e.to_string(), "{ kind: \"circle\", radius: 5 }");
283    }
284    #[test]
285    pub(super) fn test_ts_expr_array_lit() {
286        let e = TsExpr::ArrayLit(vec![
287            TsExpr::Lit(TsLit::Num(1.0)),
288            TsExpr::Lit(TsLit::Num(2.0)),
289            TsExpr::Lit(TsLit::Num(3.0)),
290        ]);
291        assert_eq!(e.to_string(), "[1, 2, 3]");
292    }
293    #[test]
294    pub(super) fn test_ts_interface_basic() {
295        let iface = TsInterface {
296            name: "Animal".to_string(),
297            extends: vec![],
298            members: vec![
299                TsInterfaceMember {
300                    name: "name".to_string(),
301                    ty: TsType::String,
302                    optional: false,
303                    readonly: true,
304                },
305                TsInterfaceMember {
306                    name: "age".to_string(),
307                    ty: TsType::Number,
308                    optional: true,
309                    readonly: false,
310                },
311            ],
312            type_params: vec![],
313        };
314        let src = iface.to_string();
315        assert!(src.contains("interface Animal {"));
316        assert!(src.contains("readonly name: string;"));
317        assert!(src.contains("age?: number;"));
318    }
319    #[test]
320    pub(super) fn test_ts_interface_extends() {
321        let iface = TsInterface {
322            name: "Dog".to_string(),
323            extends: vec!["Animal".to_string(), "Pet".to_string()],
324            members: vec![],
325            type_params: vec![],
326        };
327        let src = iface.to_string();
328        assert!(src.contains("extends Animal, Pet"));
329    }
330    #[test]
331    pub(super) fn test_ts_type_alias() {
332        let alias = TsTypeAlias {
333            name: "StringOrNumber".to_string(),
334            type_params: vec![],
335            definition: TsType::Union(vec![TsType::String, TsType::Number]),
336        };
337        assert_eq!(alias.to_string(), "type StringOrNumber = string | number;");
338    }
339    #[test]
340    pub(super) fn test_ts_type_alias_generic() {
341        let alias = TsTypeAlias {
342            name: "Nullable".to_string(),
343            type_params: vec!["T".to_string()],
344            definition: TsType::Union(vec![TsType::Custom("T".to_string()), TsType::Null]),
345        };
346        assert_eq!(alias.to_string(), "type Nullable<T> = T | null;");
347    }
348    #[test]
349    pub(super) fn test_ts_enum() {
350        let e = TsEnum {
351            name: "Direction".to_string(),
352            is_const: false,
353            members: vec![
354                TsEnumMember {
355                    name: "North".to_string(),
356                    value: None,
357                },
358                TsEnumMember {
359                    name: "South".to_string(),
360                    value: None,
361                },
362            ],
363        };
364        let src = e.to_string();
365        assert!(src.contains("enum Direction {"));
366        assert!(src.contains("North,"));
367        assert!(src.contains("South,"));
368    }
369    #[test]
370    pub(super) fn test_ts_const_enum_with_values() {
371        let e = TsEnum {
372            name: "Status".to_string(),
373            is_const: true,
374            members: vec![
375                TsEnumMember {
376                    name: "OK".to_string(),
377                    value: Some(TsLit::Num(200.0)),
378                },
379                TsEnumMember {
380                    name: "NotFound".to_string(),
381                    value: Some(TsLit::Num(404.0)),
382                },
383            ],
384        };
385        let src = e.to_string();
386        assert!(src.contains("const enum Status {"));
387        assert!(src.contains("OK = 200,"));
388        assert!(src.contains("NotFound = 404,"));
389    }
390    #[test]
391    pub(super) fn test_ts_function_emit() {
392        let f = TsFunction {
393            name: "add".to_string(),
394            params: vec![
395                TsParam {
396                    name: "a".to_string(),
397                    ty: TsType::Number,
398                    optional: false,
399                    rest: false,
400                },
401                TsParam {
402                    name: "b".to_string(),
403                    ty: TsType::Number,
404                    optional: false,
405                    rest: false,
406                },
407            ],
408            return_type: TsType::Number,
409            body: vec![TsStmt::Return(TsExpr::BinOp(
410                "+".to_string(),
411                Box::new(TsExpr::Var("a".to_string())),
412                Box::new(TsExpr::Var("b".to_string())),
413            ))],
414            is_async: false,
415            type_params: vec![],
416            is_exported: true,
417        };
418        let src = f.to_string();
419        assert!(src.contains("export function add(a: number, b: number): number {"));
420        assert!(src.contains("return (a + b);"));
421    }
422    #[test]
423    pub(super) fn test_backend_discriminated_union() {
424        let backend = TypeScriptBackend::new();
425        let alias = backend.make_discriminated_union(
426            "Shape",
427            &[
428                ("circle", vec![("radius", TsType::Number)]),
429                ("rect", vec![("w", TsType::Number), ("h", TsType::Number)]),
430            ],
431        );
432        let src = alias.to_string();
433        assert!(src.contains("type Shape ="));
434        assert!(src.contains("'circle'"));
435        assert!(src.contains("radius: number"));
436        assert!(src.contains("'rect'"));
437        assert!(src.contains("w: number"));
438    }
439    #[test]
440    pub(super) fn test_module_emit() {
441        let mut module = TsModule::new();
442        module.imports.push(TsImport {
443            names: vec!["readFileSync".to_string()],
444            from: "fs".to_string(),
445            is_type: false,
446        });
447        module.declarations.push(TsDeclaration::Const(
448            "VERSION".to_string(),
449            Some(TsType::String),
450            TsExpr::Lit(TsLit::Str("1.0.0".to_string())),
451        ));
452        let src = module.emit();
453        assert!(src.contains("import { readFileSync } from \"fs\";"));
454        assert!(src.contains("export const VERSION: string = \"1.0.0\";"));
455    }
456    #[test]
457    pub(super) fn test_module_emit_d_ts() {
458        let mut module = TsModule::new();
459        module
460            .declarations
461            .push(TsDeclaration::Function(TsFunction {
462                name: "greet".to_string(),
463                params: vec![TsParam {
464                    name: "name".to_string(),
465                    ty: TsType::String,
466                    optional: false,
467                    rest: false,
468                }],
469                return_type: TsType::String,
470                body: vec![],
471                is_async: false,
472                type_params: vec![],
473                is_exported: true,
474            }));
475        let dts = module.emit_d_ts();
476        assert!(dts.contains("declare function greet"));
477        assert!(dts.contains("name: string"));
478        assert!(dts.contains(": string;"));
479    }
480    #[test]
481    pub(super) fn test_ts_class_emit() {
482        let cls = TsClass {
483            name: "Counter".to_string(),
484            extends: None,
485            implements: vec!["ICounter".to_string()],
486            fields: vec![TsClassField {
487                name: "count".to_string(),
488                ty: TsType::Number,
489                readonly: false,
490                optional: false,
491                is_private: true,
492                is_static: false,
493            }],
494            methods: vec![TsClassMethod {
495                name: "increment".to_string(),
496                params: vec![],
497                return_type: TsType::Void,
498                body: vec![TsStmt::Expr(TsExpr::BinOp(
499                    "+=".to_string(),
500                    Box::new(TsExpr::Var("this.count".to_string())),
501                    Box::new(TsExpr::Lit(TsLit::Num(1.0))),
502                ))],
503                is_async: false,
504                is_static: false,
505                is_private: false,
506                is_getter: false,
507                is_setter: false,
508            }],
509            type_params: vec![],
510            is_exported: true,
511        };
512        let src = cls.to_string();
513        assert!(src.contains("export class Counter"));
514        assert!(src.contains("implements ICounter"));
515        assert!(src.contains("private count: number;"));
516        assert!(src.contains("increment()"));
517    }
518    #[test]
519    pub(super) fn test_ts_stmt_try_catch_finally() {
520        let stmt = TsStmt::TryCatch(
521            vec![TsStmt::Expr(TsExpr::Call(
522                Box::new(TsExpr::Var("risky".to_string())),
523                vec![],
524            ))],
525            "err".to_string(),
526            vec![TsStmt::Throw(TsExpr::Var("err".to_string()))],
527            vec![TsStmt::Expr(TsExpr::Call(
528                Box::new(TsExpr::Var("cleanup".to_string())),
529                vec![],
530            ))],
531        );
532        let src = stmt.to_string();
533        assert!(src.contains("try {"));
534        assert!(src.contains("catch (err)"));
535        assert!(src.contains("finally {"));
536    }
537    #[test]
538    pub(super) fn test_ts_stmt_for_of() {
539        let stmt = TsStmt::ForOf(
540            "item".to_string(),
541            TsExpr::Var("items".to_string()),
542            vec![TsStmt::Expr(TsExpr::Call(
543                Box::new(TsExpr::Var("process".to_string())),
544                vec![TsExpr::Var("item".to_string())],
545            ))],
546        );
547        let src = stmt.to_string();
548        assert!(src.contains("for (const item of items)"));
549    }
550    #[test]
551    pub(super) fn test_ts_stmt_switch() {
552        let stmt = TsStmt::Switch(
553            TsExpr::Var("x".to_string()),
554            vec![
555                (
556                    TsExpr::Lit(TsLit::Num(1.0)),
557                    vec![TsStmt::Return(TsExpr::Lit(TsLit::Str("one".to_string())))],
558                ),
559                (
560                    TsExpr::Lit(TsLit::Num(2.0)),
561                    vec![TsStmt::Return(TsExpr::Lit(TsLit::Str("two".to_string())))],
562                ),
563            ],
564            vec![TsStmt::Return(TsExpr::Lit(TsLit::Str("other".to_string())))],
565        );
566        let src = stmt.to_string();
567        assert!(src.contains("switch (x)"));
568        assert!(src.contains("case 1:"));
569        assert!(src.contains("case 2:"));
570        assert!(src.contains("default:"));
571    }
572}
573#[cfg(test)]
574mod tests_ts_ext_extra {
575    use super::*;
576    #[test]
577    pub(super) fn test_ts_ext_config() {
578        let mut cfg = TsExtConfig::new();
579        cfg.set("mode", "release");
580        cfg.set("verbose", "true");
581        assert_eq!(cfg.get("mode"), Some("release"));
582        assert!(cfg.get_bool("verbose"));
583        assert!(cfg.get_int("mode").is_none());
584        assert_eq!(cfg.len(), 2);
585    }
586    #[test]
587    pub(super) fn test_ts_ext_source_buffer() {
588        let mut buf = TsExtSourceBuffer::new();
589        buf.push_line("fn main() {");
590        buf.indent();
591        buf.push_line("println!(\"hello\");");
592        buf.dedent();
593        buf.push_line("}");
594        assert!(buf.as_str().contains("fn main()"));
595        assert!(buf.as_str().contains("    println!"));
596        assert_eq!(buf.line_count(), 3);
597        buf.reset();
598        assert!(buf.is_empty());
599    }
600    #[test]
601    pub(super) fn test_ts_ext_name_scope() {
602        let mut scope = TsExtNameScope::new();
603        assert!(scope.declare("x"));
604        assert!(!scope.declare("x"));
605        assert!(scope.is_declared("x"));
606        let scope = scope.push_scope();
607        assert_eq!(scope.depth(), 1);
608        let mut scope = scope.pop_scope();
609        assert_eq!(scope.depth(), 0);
610        scope.declare("y");
611        assert_eq!(scope.len(), 2);
612    }
613    #[test]
614    pub(super) fn test_ts_ext_diag_collector() {
615        let mut col = TsExtDiagCollector::new();
616        col.emit(TsExtDiagMsg::warning("pass_a", "slow"));
617        col.emit(TsExtDiagMsg::error("pass_b", "fatal"));
618        assert!(col.has_errors());
619        assert_eq!(col.errors().len(), 1);
620        assert_eq!(col.warnings().len(), 1);
621        col.clear();
622        assert!(col.is_empty());
623    }
624    #[test]
625    pub(super) fn test_ts_ext_id_gen() {
626        let mut gen = TsExtIdGen::new();
627        assert_eq!(gen.next_id(), 0);
628        assert_eq!(gen.next_id(), 1);
629        gen.skip(10);
630        assert_eq!(gen.next_id(), 12);
631        gen.reset();
632        assert_eq!(gen.peek_next(), 0);
633    }
634    #[test]
635    pub(super) fn test_ts_ext_incr_key() {
636        let k1 = TsExtIncrKey::new(100, 200);
637        let k2 = TsExtIncrKey::new(100, 200);
638        let k3 = TsExtIncrKey::new(999, 200);
639        assert!(k1.matches(&k2));
640        assert!(!k1.matches(&k3));
641    }
642    #[test]
643    pub(super) fn test_ts_ext_profiler() {
644        let mut p = TsExtProfiler::new();
645        p.record(TsExtPassTiming::new("pass_a", 1000, 50, 200, 100));
646        p.record(TsExtPassTiming::new("pass_b", 500, 30, 100, 200));
647        assert_eq!(p.total_elapsed_us(), 1500);
648        assert_eq!(
649            p.slowest_pass()
650                .expect("slowest pass should exist")
651                .pass_name,
652            "pass_a"
653        );
654        assert_eq!(p.profitable_passes().len(), 1);
655    }
656    #[test]
657    pub(super) fn test_ts_ext_event_log() {
658        let mut log = TsExtEventLog::new(3);
659        log.push("event1");
660        log.push("event2");
661        log.push("event3");
662        assert_eq!(log.len(), 3);
663        log.push("event4");
664        assert_eq!(log.len(), 3);
665        assert_eq!(
666            log.iter()
667                .next()
668                .expect("iterator should have next element"),
669            "event2"
670        );
671    }
672    #[test]
673    pub(super) fn test_ts_ext_version() {
674        let v = TsExtVersion::new(1, 2, 3).with_pre("alpha");
675        assert!(!v.is_stable());
676        assert_eq!(format!("{}", v), "1.2.3-alpha");
677        let stable = TsExtVersion::new(2, 0, 0);
678        assert!(stable.is_stable());
679        assert!(stable.is_compatible_with(&TsExtVersion::new(2, 0, 0)));
680        assert!(!stable.is_compatible_with(&TsExtVersion::new(3, 0, 0)));
681    }
682    #[test]
683    pub(super) fn test_ts_ext_features() {
684        let mut f = TsExtFeatures::new();
685        f.enable("sse2");
686        f.enable("avx2");
687        assert!(f.is_enabled("sse2"));
688        assert!(!f.is_enabled("avx512"));
689        f.disable("avx2");
690        assert!(!f.is_enabled("avx2"));
691        let mut g = TsExtFeatures::new();
692        g.enable("sse2");
693        g.enable("neon");
694        let union = f.union(&g);
695        assert!(union.is_enabled("sse2") && union.is_enabled("neon"));
696        let inter = f.intersection(&g);
697        assert!(inter.is_enabled("sse2"));
698    }
699    #[test]
700    pub(super) fn test_ts_ext_emit_stats() {
701        let mut s = TsExtEmitStats::new();
702        s.bytes_emitted = 50_000;
703        s.items_emitted = 500;
704        s.elapsed_ms = 100;
705        assert!(s.is_clean());
706        assert!((s.throughput_bps() - 500_000.0).abs() < 1.0);
707        let disp = format!("{}", s);
708        assert!(disp.contains("bytes=50000"));
709    }
710}
711#[cfg(test)]
712mod TS_infra_tests {
713    use super::*;
714    #[test]
715    pub(super) fn test_pass_config() {
716        let config = TSPassConfig::new("test_pass", TSPassPhase::Transformation);
717        assert!(config.enabled);
718        assert!(config.phase.is_modifying());
719        assert_eq!(config.phase.name(), "transformation");
720    }
721    #[test]
722    pub(super) fn test_pass_stats() {
723        let mut stats = TSPassStats::new();
724        stats.record_run(10, 100, 3);
725        stats.record_run(20, 200, 5);
726        assert_eq!(stats.total_runs, 2);
727        assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
728        assert!((stats.success_rate() - 1.0).abs() < 0.01);
729        let s = stats.format_summary();
730        assert!(s.contains("Runs: 2/2"));
731    }
732    #[test]
733    pub(super) fn test_pass_registry() {
734        let mut reg = TSPassRegistry::new();
735        reg.register(TSPassConfig::new("pass_a", TSPassPhase::Analysis));
736        reg.register(TSPassConfig::new("pass_b", TSPassPhase::Transformation).disabled());
737        assert_eq!(reg.total_passes(), 2);
738        assert_eq!(reg.enabled_count(), 1);
739        reg.update_stats("pass_a", 5, 50, 2);
740        let stats = reg.get_stats("pass_a").expect("stats should exist");
741        assert_eq!(stats.total_changes, 5);
742    }
743    #[test]
744    pub(super) fn test_analysis_cache() {
745        let mut cache = TSAnalysisCache::new(10);
746        cache.insert("key1".to_string(), vec![1, 2, 3]);
747        assert!(cache.get("key1").is_some());
748        assert!(cache.get("key2").is_none());
749        assert!((cache.hit_rate() - 0.5).abs() < 0.01);
750        cache.invalidate("key1");
751        assert!(!cache.entries["key1"].valid);
752        assert_eq!(cache.size(), 1);
753    }
754    #[test]
755    pub(super) fn test_worklist() {
756        let mut wl = TSWorklist::new();
757        assert!(wl.push(1));
758        assert!(wl.push(2));
759        assert!(!wl.push(1));
760        assert_eq!(wl.len(), 2);
761        assert_eq!(wl.pop(), Some(1));
762        assert!(!wl.contains(1));
763        assert!(wl.contains(2));
764    }
765    #[test]
766    pub(super) fn test_dominator_tree() {
767        let mut dt = TSDominatorTree::new(5);
768        dt.set_idom(1, 0);
769        dt.set_idom(2, 0);
770        dt.set_idom(3, 1);
771        assert!(dt.dominates(0, 3));
772        assert!(dt.dominates(1, 3));
773        assert!(!dt.dominates(2, 3));
774        assert!(dt.dominates(3, 3));
775    }
776    #[test]
777    pub(super) fn test_liveness() {
778        let mut liveness = TSLivenessInfo::new(3);
779        liveness.add_def(0, 1);
780        liveness.add_use(1, 1);
781        assert!(liveness.defs[0].contains(&1));
782        assert!(liveness.uses[1].contains(&1));
783    }
784    #[test]
785    pub(super) fn test_constant_folding() {
786        assert_eq!(TSConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
787        assert_eq!(TSConstantFoldingHelper::fold_div_i64(10, 0), None);
788        assert_eq!(TSConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
789        assert_eq!(
790            TSConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
791            0b1000
792        );
793        assert_eq!(TSConstantFoldingHelper::fold_bitnot_i64(0), -1);
794    }
795    #[test]
796    pub(super) fn test_dep_graph() {
797        let mut g = TSDepGraph::new();
798        g.add_dep(1, 2);
799        g.add_dep(2, 3);
800        g.add_dep(1, 3);
801        assert_eq!(g.dependencies_of(2), vec![1]);
802        let topo = g.topological_sort();
803        assert_eq!(topo.len(), 3);
804        assert!(!g.has_cycle());
805        let pos: std::collections::HashMap<u32, usize> =
806            topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
807        assert!(pos[&1] < pos[&2]);
808        assert!(pos[&1] < pos[&3]);
809        assert!(pos[&2] < pos[&3]);
810    }
811}