Skip to main content

cure_engine/rules/
mod.rs

1//! 引擎内置通用规则:只依赖 `Lang` 抽象,任何语言 crate 均可复用。
2//!
3//! 每条规则文档化其语义前提;所有提案都必须严格降低成本(runner 校验)。
4
5
6
7use crate::analysis::{prefix_effects_readable, subtree_contains};
8use crate::effect::Effect;
9use crate::kind::{BinOp, LitRef, NodeKind, UnOp};
10use crate::pattern::{matches, Pat};
11use crate::rule::{Edit, RewriteCtx, Rule};
12use crate::lang::{Lang, ReassocOutcome, StmtKill};
13use crate::walk::Walk;
14
15// ---------------------------------------------------------------------------
16// 括号消除:Paren(x) → x(纯分组节点,删除永远安全)
17// ---------------------------------------------------------------------------
18
19pub struct ParenRemoval;
20
21impl<L: Lang> Rule<L> for ParenRemoval {
22    fn name(&self) -> &'static str {
23        "paren_removal"
24    }
25    fn kinds(&self) -> &'static [NodeKind] {
26        &[NodeKind::Paren]
27    }
28    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
29        let RewriteCtx { lang, walk } = ctx;
30        if lang.kind(id) != NodeKind::Paren {
31            return None;
32        }
33        let inner = *lang.children(id).first()?;
34        // 赋值目标位的括号可能语义显著(JS 具名求值)
35        if lang.assign_target_paren_required(inner) {
36            if let Some((p, i)) = walk.parents.get(&id) {
37                if lang.kind(*p) == NodeKind::Assign && *i == 0 {
38                    return None;
39                }
40            }
41        }
42        // 语法必需括号不删(Lang 钩子):JS 的 Paren(箭头/Raw/自定义
43        // 形态) 在运算数位由产生式强制——删了打印机要再加,形成
44        // 「删括号→打印补→再解析成 Paren→再删」的 AST 振荡(真实
45        // 语料二轮仍 ~900 编辑的根源)。Java 优先级模型完备 → 恒 false。
46        if lang.grammar_paren_required(inner) {
47            return None;
48        }
49        Some(Edit::Replace {
50            target: id,
51            with: inner,
52        })
53    }
54}
55
56// ---------------------------------------------------------------------------
57// 常量条件:if (true) {A} [else B] → A;if (false) {A} else B → 删除
58// ---------------------------------------------------------------------------
59
60/// 语句自身终结其所在**语句列表**的后续(return/throw/yield 终结方法/块,
61/// break/continue 终结所在块——JLS 14.21 语义)。
62/// javac 对 `if (true) 终结语句` 后跟语句有「条件编译豁免」——后续按可达
63/// 处理;剥掉 if 壳后豁免消失,同列表后续变硬不可达(javac 拒绝)。
64fn self_is_terminal<L: Lang>(lang: &L, n: L::Id) -> bool {
65    matches!(
66        lang.kind(n),
67        NodeKind::Return | NodeKind::Throw | NodeKind::Break | NodeKind::Continue | NodeKind::Yield
68    )
69}
70
71/// 子树是否含 switch 表达式产出(yield)。折叠丢弃含 yield 的分支会
72/// 缩小产出集 → switch 表达式静态类型改变(var 推断随之破坏)。
73fn contains_yield<L: Lang>(lang: &L, n: L::Id) -> bool {
74    subtree_contains(lang, n, |x| lang.kind(x) == NodeKind::Yield)
75}
76
77pub struct ConstCondition;
78
79impl<L: Lang> Rule<L> for ConstCondition {
80    fn name(&self) -> &'static str {
81        "const_condition"
82    }
83    fn kinds(&self) -> &'static [NodeKind] {
84        &[NodeKind::If]
85    }
86    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
87        let RewriteCtx { lang, walk } = ctx;
88        if lang.kind(id) != NodeKind::If {
89            return None;
90        }
91        let ch = lang.children(id);
92        let cond = *ch.first()?;
93        match lang.literal(cond) {
94            Some(LitRef::Bool(true)) => {
95                // then 分支必然执行:拼进父块(避免嵌套块);非块父级则整体替换。
96                // then 必然终结(return/break 等)→ 后续兄弟不可达,须一并
97                // 删除——否则产出 `return 1; return 2;` 不可编译
98                //(InputArrayTrailingComma 抓获;unreachable_after_terminal
99                // 是 --dead-code 门控,默认模式依赖本规则自洽)
100                let then = ch.get(1).copied()?;
101                // 可提升声明(JS 的 if (c) function f(){}——Annex B 条件
102                // 初始化)不可搬出分支(Test262 annexB 族抓获)
103                if lang.contains_hoisted_decl(then) {
104                    return None;
105                }
106                // R15 P0-1(o4 形态)抓获:被丢弃的 else 含 yield → switch
107                // 表达式产出集缩小 → 静态类型变化(var 推断破坏)→ 拒绝
108                if let Some(&els) = ch.get(2) {
109                    if contains_yield(&*lang, els) || lang.contains_hoisted_decl(els) {
110                        return None;
111                    }
112                }
113                let insert: Vec<L::Id> = if lang.kind(then) == NodeKind::Block {
114                    lang.children(then).to_vec()
115                } else {
116                    vec![then]
117                };
118                let terminates = insert.iter().any(|&s| self_is_terminal(lang, s));
119                if let (Some(parent), Some(index)) = (walk.parent(id), walk.index(id)) {
120                    if lang.kind(parent) == NodeKind::Block {
121                        let siblings = lang.children(parent).to_vec();
122                        let remove = if terminates && index + 1 < siblings.len() {
123                            siblings.len() - index
124                        } else {
125                            1
126                        };
127                        // R15 P0-1(o3 同类)抓获:删除的尾随兄弟含 yield →
128                        // 产出集缩小 → 静态类型变化 → 拒绝
129                        if remove > 1
130                            && siblings[index + 1..]
131                                .iter()
132                                .any(|&s| contains_yield(&*lang, s))
133                        {
134                            return None;
135                        }
136                        return Some(Edit::Splice {
137                            node: parent,
138                            index,
139                            remove,
140                            insert,
141                        });
142                    }
143                    // R15 P0-3(UnreachableVar)抓获:case 组(标签后的
144                    // 语句序列)中整体替换时,then 终结且 if 后还有尾随
145                    // 语句 → 折叠使尾随不可达(JLS 14.21 条件编译豁免
146                    // 消失)。case 组中声明的作用域覆盖整个 switch 块
147                    //(删 `int i=1` 会断 `default: i=2` 引用)——无法安全
148                    // 删除,唯一安全动作是拒绝折叠。其余非块父级(if/while
149                    // 等控制体的唯一语句孩子、else 分支、do-while 条件)
150                    // 不存在尾随语句——照常替换。
151                    if lang.kind(parent) == NodeKind::Case && terminates {
152                        let siblings = lang.children(parent).to_vec();
153                        if index + 1 < siblings.len() {
154                            return None;
155                        }
156                    }
157                }
158                Some(Edit::Replace {
159                    target: id,
160                    with: then,
161                })
162            }
163            Some(LitRef::Bool(false)) => {
164                // else 分支必然执行:保留之;无 else(或空 else)才可整删
165                // R15 P0-1(o3 形态)抓获:then 分支整体被丢弃(换 else 或
166                // 删除)——含 yield 则 switch 表达式产出集缩小 → 静态类型
167                // 变化(var 推断破坏)→ 拒绝
168                let then = *ch.get(1)?;
169                if contains_yield(&*lang, then) {
170                    return None;
171                }
172                // 选中的 else 含可提升声明 → 搬出分支改变初始化时机
173                //(Test262 annexB if-decl-else-decl 家族抓获)
174                if let Some(&els) = ch.get(2) {
175                    if lang.contains_hoisted_decl(els) {
176                        return None;
177                    }
178                }
179                let els = ch.get(2).copied();
180                if let (Some(parent), Some(index)) = (walk.parent(id), walk.index(id)) {
181                    if lang.kind(parent) == NodeKind::Block {
182                        let insert: Vec<L::Id> = match els {
183                            Some(e) => {
184                                if lang.kind(e) == NodeKind::Block {
185                                    lang.children(e).to_vec()
186                                } else {
187                                    vec![e]
188                                }
189                            }
190                            None => Vec::new(),
191                        };
192                        return Some(if insert.is_empty() {
193                            Edit::Delete { node: id }
194                        } else {
195                            Edit::Splice {
196                                node: parent,
197                                index,
198                                remove: 1,
199                                insert,
200                            }
201                        });
202                    }
203                }
204                match els {
205                    Some(e) => Some(Edit::Replace {
206                        target: id,
207                        with: e,
208                    }),
209                    None => Some(Edit::Delete { node: id }),
210                }
211            }
212            _ => None,
213        }
214    }
215}
216
217// ---------------------------------------------------------------------------
218// 布尔返回:if (c) return true else return false → return c(反极性 → return !c)
219// 前提:c 为原始 boolean。
220// ---------------------------------------------------------------------------
221
222// 裸分支:if (c) return true; else return false;
223static P_IF_RET_TF: Pat = Pat::Kind(
224    NodeKind::If,
225    &[Pat::Bind("cond"), Pat::Kind(NodeKind::Return, &[Pat::LitBool(true)]), Pat::Kind(NodeKind::Return, &[Pat::LitBool(false)])],
226);
227static P_IF_RET_FT: Pat = Pat::Kind(
228    NodeKind::If,
229    &[Pat::Bind("cond"), Pat::Kind(NodeKind::Return, &[Pat::LitBool(false)]), Pat::Kind(NodeKind::Return, &[Pat::LitBool(true)])],
230);
231// 块包裹分支(反编译器/规范形态):if (c) { return true; } else { return false; }
232static P_IF_BRET_TF: Pat = Pat::Kind(
233    NodeKind::If,
234    &[
235        Pat::Bind("cond"),
236        Pat::Kind(NodeKind::Block, &[Pat::Kind(NodeKind::Return, &[Pat::LitBool(true)])]),
237        Pat::Kind(NodeKind::Block, &[Pat::Kind(NodeKind::Return, &[Pat::LitBool(false)])]),
238    ],
239);
240static P_IF_BRET_FT: Pat = Pat::Kind(
241    NodeKind::If,
242    &[
243        Pat::Bind("cond"),
244        Pat::Kind(NodeKind::Block, &[Pat::Kind(NodeKind::Return, &[Pat::LitBool(false)])]),
245        Pat::Kind(NodeKind::Block, &[Pat::Kind(NodeKind::Return, &[Pat::LitBool(true)])]),
246    ],
247);
248
249pub struct BooleanReturn;
250
251impl<L: Lang> Rule<L> for BooleanReturn {
252    fn name(&self) -> &'static str {
253        "boolean_return"
254    }
255    fn kinds(&self) -> &'static [NodeKind] {
256        &[NodeKind::If]
257    }
258    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
259        let RewriteCtx { lang, walk: _ } = ctx;
260        if lang.kind(id) != NodeKind::If {
261            return None;
262        }
263        // 四种形态:裸/块分支 × 正/反极性。块分支整体被裸 return 替换。
264        let negated = |cond: L::Id, lang: &mut L| {
265            let nc = lang.build_unary(UnOp::Not, cond);
266            lang.build_return(Some(nc))
267        };
268        for (pat, invert) in [
269            (&P_IF_RET_TF, false),
270            (&P_IF_BRET_TF, false),
271            (&P_IF_RET_FT, true),
272            (&P_IF_BRET_FT, true),
273        ] {
274            if let Some(b) = matches(&*lang, id, pat) {
275                let cond = lookup(&b, "cond")?;
276                if !lang.is_bool(cond) {
277                    return None;
278                }
279                let ret = if invert {
280                    negated(cond, lang)
281                } else {
282                    lang.build_return(Some(cond))
283                };
284                return Some(Edit::Replace {
285                    target: id,
286                    with: ret,
287                });
288            }
289        }
290        None
291    }
292}
293
294fn lookup<Id: Copy>(binds: &[(&'static str, Id)], name: &str) -> Option<Id> {
295    binds.iter().find(|(n, _)| *n == name).map(|(_, id)| *id)
296}
297
298// ---------------------------------------------------------------------------
299// 空分支:if (c) {} else {B} → if (!c) {B};if (c) {A} else {} → if (c) {A};
300//         if (c) {} → 删除(仅当 c 无副作用)。
301// ---------------------------------------------------------------------------
302
303pub struct IfElseEmpty;
304
305impl<L: Lang> Rule<L> for IfElseEmpty {
306    fn name(&self) -> &'static str {
307        "if_else_empty"
308    }
309    fn kinds(&self) -> &'static [NodeKind] {
310        &[NodeKind::If]
311    }
312    fn structural(&self) -> bool {
313        // `if (c) {} else {B}` → `if (!c) {B}` 在代价模型上可能恰好
314        // 持平(空 Block 1 + cond 1 vs Unary 3)——这是可读性结构
315        // 改写,允许零代价应用(否则 debug 断言 panic)。
316        true
317    }
318    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
319        let RewriteCtx { lang, walk } = ctx;
320        if lang.kind(id) != NodeKind::If {
321            return None;
322        }
323        let ch = lang.children(id);
324        let cond = *ch.first()?;
325        let is_empty_block = |n: L::Id| -> bool {
326            lang.kind(n) == NodeKind::Block && lang.children(n).is_empty()
327        };
328        match ch.len() {
329            3 => {
330                let (then, els) = (ch[1], ch[2]);
331                if is_empty_block(then) {
332                    if is_empty_block(els) {
333                        // 两支全空:塌缩成 if(c){},交给 len==2 分支处理
334                        return Some(Edit::Splice {
335                            node: id,
336                            index: 0,
337                            remove: 3,
338                            insert: vec![cond, then],
339                        });
340                    }
341                    // then 空、else 有内容:取反条件交换分支(条件两侧都恰好求值一次,永远安全)
342                    let nc = lang.build_unary(UnOp::Not, cond);
343                    return Some(Edit::Splice {
344                        node: id,
345                        index: 0,
346                        remove: 3,
347                        insert: vec![nc, els],
348                    });
349                }
350                if is_empty_block(els) {
351                    return Some(Edit::Splice {
352                        node: id,
353                        index: 0,
354                        remove: 3,
355                        insert: vec![cond, then],
356                    });
357                }
358                None
359            }
360            2 => {
361                let then = ch[1];
362                // 【位置守卫】Delete 只在语句位置(父为 Block)合法:
363                // if 处于分支位置(if(c){ if(c2){} }——else-if 链化后的
364                // 常见形态)时,抽走分支会给父 If 留下"只有条件没有体"的
365                // 残骸(1-child If,打印机越界 panic——真实大语料抓获)。
366                // 分支位置的空体 if 由父 If 的规则吸收(塌缩后自身再走
367                // len==2/len==3 变换)。
368                let parent_is_block = walk
369                    .parent(id)
370                    .map(|p| lang.kind(p) == NodeKind::Block)
371                    .unwrap_or(false);
372                if is_empty_block(then)
373                    && lang.effect(cond) <= Effect::MayRead
374                    && parent_is_block
375                {
376                    // 删除整个 if 会丢掉 cond 的求值,仅当 cond 无副作用时安全
377                    return Some(Edit::Delete { node: id });
378                }
379                None
380            }
381            _ => None,
382        }
383    }
384}
385
386// ---------------------------------------------------------------------------
387// 布尔比较:x == true → x;x == false → !x;x != true → !x;x != false → x(两个操作数序)
388// 前提:x 为原始 boolean(装箱比较是引用比较,is_bool 必须排除)。
389// ---------------------------------------------------------------------------
390
391pub struct BoolCompare;
392
393impl<L: Lang> Rule<L> for BoolCompare {
394    fn name(&self) -> &'static str {
395        "bool_compare"
396    }
397    fn kinds(&self) -> &'static [NodeKind] {
398        &[NodeKind::Binary]
399    }
400    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
401        let RewriteCtx { lang, walk } = ctx;
402        if lang.kind(id) != NodeKind::Binary {
403            return None;
404        }
405        let op = lang.bin_op(id)?;
406        if !matches!(op, BinOp::Eq | BinOp::Ne) {
407            return None;
408        }
409        let ch = lang.children(id);
410        let (l, r) = (*ch.first()?, *ch.get(1)?);
411        let lit_bool = |n: L::Id| -> Option<bool> {
412            match lang.literal(n) {
413                Some(LitRef::Bool(b)) => Some(b),
414                _ => None,
415            }
416        };
417        // 统一成 (x, lit)
418        let (x, lit) = match (lit_bool(l), lit_bool(r)) {
419            (None, Some(b)) => (l, b),
420            (Some(b), None) => (r, b),
421            _ => return None,
422        };
423        if !lang.is_bool(x) {
424            // 条件位上下文松弛:if/while 的条件表达式必为 boolean
425            //(IteratorWildcard 抓获:`x == false` 的 x 是方法调用——
426            // 返回类型无类型信息即被拒)。仅当本节点恰是 If/While 的
427            // 条件孩子时放行
428            let in_cond = walk.parent(id).is_some_and(|p| {
429                matches!(lang.kind(p), NodeKind::If | NodeKind::While) && lang.children(p).first() == Some(&id)
430            });
431            if !in_cond {
432                return None;
433            }
434        }
435        // (op, lit) → 替换结果
436        let neg = match (op, lit) {
437            (BinOp::Eq, true) | (BinOp::Ne, false) => false,
438            (BinOp::Eq, false) | (BinOp::Ne, true) => true,
439            _ => return None,
440        };
441        let with = if neg {
442            lang.build_unary(UnOp::Not, x)
443        } else {
444            x
445        };
446        Some(Edit::Replace {
447            target: id,
448            with,
449        })
450    }
451}
452
453// ---------------------------------------------------------------------------
454// 双重否定:!!x → x
455// 安全性:`!x` 要能通过类型检查,x 必为 boolean,无需类型查询。
456// ---------------------------------------------------------------------------
457
458pub struct DoubleNot;
459
460impl<L: Lang> Rule<L> for DoubleNot {
461    fn name(&self) -> &'static str {
462        "double_not"
463    }
464    fn kinds(&self) -> &'static [NodeKind] {
465        &[NodeKind::Unary]
466    }
467    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
468        let RewriteCtx { lang, walk: _ } = ctx;
469        if lang.kind(id) != NodeKind::Unary || lang.un_op(id) != Some(UnOp::Not) {
470            return None;
471        }
472        let inner = *lang.children(id).first()?;
473        if lang.kind(inner) != NodeKind::Unary || lang.un_op(inner) != Some(UnOp::Not) {
474            return None;
475        }
476        let x = *lang.children(inner).first()?;
477        // 语言裁决(Lang 钩子):Java 的 !!x 蕴含 x:boolean(类型系统
478        // 保证,恒等式无条件成立——!!it.hasNext() 形态依赖);JS 的
479        // !x 是布尔胁迫(!!0 === false ≠ 0)——仅语法级布尔放行
480        if !lang.double_not_sound(x) {
481            return None;
482        }
483        Some(Edit::Replace {
484            target: id,
485            with: x,
486        })
487    }
488}
489
490// ---------------------------------------------------------------------------
491// 常量折叠:!true → false / !false → true
492// (局部传播把常量内联进 !x 后,由此规则收敛到自然形态)
493// ---------------------------------------------------------------------------
494
495pub struct BoolNotFold;
496
497impl<L: Lang> Rule<L> for BoolNotFold {
498    fn name(&self) -> &'static str {
499        "bool_not_fold"
500    }
501    fn kinds(&self) -> &'static [NodeKind] {
502        &[NodeKind::Unary]
503    }
504    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
505        let RewriteCtx { lang, walk: _ } = ctx;
506        if lang.kind(id) != NodeKind::Unary || lang.un_op(id) != Some(UnOp::Not) {
507            return None;
508        }
509        let operand = *lang.children(id).first()?;
510        match lang.literal(operand) {
511            Some(LitRef::Bool(b)) => {
512                let folded = lang.build_bool(!b);
513                Some(Edit::Replace {
514                    target: id,
515                    with: folded,
516                })
517            }
518            _ => None,
519        }
520    }
521}
522
523// ---------------------------------------------------------------------------
524// 自赋值:x = x → 删除(仅限语言层局部变量)
525// ---------------------------------------------------------------------------
526
527pub struct SelfAssign;
528
529impl<L: Lang> Rule<L> for SelfAssign {
530    fn name(&self) -> &'static str {
531        "self_assign"
532    }
533    fn kinds(&self) -> &'static [NodeKind] {
534        &[NodeKind::Assign]
535    }
536    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
537        let RewriteCtx { lang, walk } = ctx;
538        if lang.kind(id) != NodeKind::Assign {
539            return None;
540        }
541        if lang.assign_op(id).is_some() {
542            return None; // x += x 有读语义差异,不动
543        }
544        let ch = lang.children(id);
545        let (t, v) = (*ch.first()?, *ch.get(1)?);
546        if lang.kind(t) != NodeKind::VarRef || lang.kind(v) != NodeKind::VarRef {
547            return None;
548        }
549        if !lang.same_binding(t, v) || !lang.is_local_var(t) {
550            return None;
551        }
552        // 删除目标必须是**语句**:`x = x;` 的 AST 形态是 ExprStmt[Assign]
553        // ——删 Assign 留下空 ExprStmt 壳,后续规则按 children[0] 遍历
554        // 即 panic(ddc weixin gp0.d1 复现:clinit 里 R8 寄存器噪声
555        // `file = file;` 触发)。嵌套赋值位(`y = (x = x)` 的值孩子)删
556        // 中树节点直接留洞——拒绝。Block 直挂 Assign 的语言形态照旧删
557        // Assign 本身(Block 孩子删除安全)。
558        let (parent, _) = walk.parents.get(&id)?;
559        match lang.kind(*parent) {
560            NodeKind::ExprStmt => Some(Edit::Delete { node: *parent }),
561            NodeKind::Block => Some(Edit::Delete { node: id }),
562            _ => None,
563        }
564    }
565}
566
567// ---------------------------------------------------------------------------
568// 算术恒等式(仅精确整数,浮点有 -0.0/NaN 陷阱):
569// x+0 → x, 0+x → x, x-0 → x, x*1 → x, 1*x → x, x/1 → x
570// ---------------------------------------------------------------------------
571
572
573// ---------------------------------------------------------------------------
574// 死声明删除:`T x;`(零使用、零求值)或 `T x = V;`(V 纯/MayRead 且
575// 零使用)。声明之后整个区域对 x 零事件(读/写/遮蔽/不透明全无)。
576// 反编译器把寄存器噪声折叠后常残留零用途声明(如 PMD 噪声文件消解
577// 后的 `int a;`)。匿名类捕获由 opaque 守卫拒绝(对抗波 4 教训)。
578// ---------------------------------------------------------------------------
579
580pub struct DeadDecl;
581
582impl<L: Lang> Rule<L> for DeadDecl {
583    fn name(&self) -> &'static str {
584        "dead_decl"
585    }
586    fn kinds(&self) -> &'static [NodeKind] {
587        &[NodeKind::VarDecl]
588    }
589    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
590        let RewriteCtx { lang, walk } = ctx;
591        if lang.kind(id) != NodeKind::VarDecl {
592            return None;
593        }
594        if !lang.dead_decl_allowed(id) {
595            return None;
596        }
597        let ch = lang.children(id).to_vec();
598        // 有 init:V 的求值会被一并删除——仅纯/只读可删(MayThrow 不行:
599        // `int x = 1/0;` 删掉会失去异常)
600        if let Some(&v) = ch.first() {
601            if lang.effect(v) > Effect::MayRead {
602                return None;
603            }
604            if lang.value_may_throw_unresolved(v) {
605                return None;
606            }
607            // 自引用 init(JS TDZ:`let x = x + 1` 抛 ReferenceError)——
608            // 删除声明会吞掉异常(Test262 let/const use-before-init 抓获)。
609            let name_s = lang.var_name(id).unwrap_or("");
610            if subtree_contains(&*lang, v, |n| {
611                lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name_s)
612            }) {
613                return None;
614            }
615        }
616        let parent = walk.parent(id)?;
617        if lang.kind(parent) != NodeKind::Block {
618            return None;
619        }
620        let idx = walk.index(id)?;
621        let stmts = lang.children(parent).to_vec();
622        // **句法扫描**(任何同名 VarRef 引用 → 保留):事件模型对复合
623        // 赋值目标内的读有盲区(`arr[j] += 5` 的 j——scan_region 的
624        // Assign 分支只递归 RHS),零事件 ≠ 零引用(differential Adv5
625        // 抓获:删掉 j 后 arr[j] 悬空)。写目标本身也是 VarRef,同样被
626        // 句法扫描覆盖。
627        let name_str = lang.var_name(id)?.to_string();
628        let name_key = lang.var_key(id)?;
629        for &s in &stmts[idx + 1..] {
630            // 原文/类体可能读该名字(不可见)→ 不可判定零用途
631            if lang.stmt_has_opaque_region(s) {
632                return None;
633            }
634            match (lang.stmt_mentions(s, name_key), lang.stmt_opaque(s)) {
635                (Some(mentions), Some(opaque)) => {
636                    // Any VarRef (including event-blind write targets),
637                    // any same-name declaration, or an opaque region.
638                    if mentions || opaque {
639                        return None;
640                    }
641                }
642                _ => {
643                    // 遮蔽 / 不透明(匿名类捕获的原文引用无 AST 节点)
644                    // 仍走事件扫描
645                    if subtree_contains(&*lang, s, |n| {
646                        lang.kind(n) == NodeKind::VarRef
647                            && lang.var_name(n) == Some(name_str.as_str())
648                    }) {
649                        return None;
650                    }
651                    let mut w = Watch::<L>::new(name_key, &[]);
652                    scan_region(&*lang, s, &mut w);
653                    if w.opaque || w.shadowed {
654                        return None;
655                    }
656                }
657            }
658        }
659        Some(Edit::Delete { node: id })
660    }
661}
662
663// ---------------------------------------------------------------------------
664// 重赋值折回声明(寄存器累加器形态):
665//   T x = e; …(对 x 零事件)…; x = x op K;  →  T x = e op K;
666//   (复合形态 `x op= K` 与展开形态 `x = x op K` 均可)
667//
668// e 的求值位置**不动**(仍在声明处);被删除的只是后面那条纯 delta
669// 语句;声明与折回点之间对 x 零事件 ⇒ 中间值从未被观察 ⇒ 恒安全。
670// 语义:x 的终值 (e op K) 由同一算符作用于同一操作数计算——精确
671// (整数环绕/浮点舍入均逐位一致)。
672// 拒绝:Div/Rem 且 K==0(折回后异常从后面的语句提前到声明处——
673// 中间语句的副作用顺序改变);K 非字面量;op 为短路/比较。
674// 之后 local_propagation 可继续把 e op K 内联到唯一使用处。
675// ---------------------------------------------------------------------------
676
677pub struct AssignBackFold;
678
679impl<L: Lang> Rule<L> for AssignBackFold {
680    fn name(&self) -> &'static str {
681        "assign_back_fold"
682    }
683    fn kinds(&self) -> &'static [NodeKind] {
684        &[NodeKind::VarDecl]
685    }
686    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
687        let RewriteCtx { lang, walk } = ctx;
688        if lang.kind(id) != NodeKind::VarDecl {
689            return None;
690        }
691        let ch = lang.children(id).to_vec();
692        let (&init, has_init) = (ch.first()?, !ch.is_empty());
693        if !has_init {
694            return None; // 无 init 用 decl_assign_merge / dead_decl 处理
695        }
696        let name_key = lang.var_key(id)?;
697        // 窄类型(byte/short/char 等)复合赋值含隐式收窄——折回声明
698        // 产出超域非法常量或丢静态类型(differential Adv2 抓获:
699        // `char ch = 'a'; ch += 2` 折后 println 打 99 而非 'c')
700        if !lang.is_wide_decl(id) {
701            return None;
702        }
703        let parent = walk.parent(id)?;
704        if lang.kind(parent) != NodeKind::Block {
705            return None;
706        }
707        let idx = walk.index(id)?;
708        let stmts = lang.children(parent).to_vec();
709
710        // 扫描声明之后的语句:对 x 的首个引用必须恰是「重赋值」。
711        // 句法扫描兜底事件模型盲区(复合赋值目标内的读——`arr[j]+=5`
712        // 的 j 对事件索引不可见);首个引用非重赋值形态 → 中间值被
713        // 观察,拒绝。
714        let name_str = lang.var_name(id)?.to_string();
715        for &s in &stmts[idx + 1..] {
716            // Indexed: VarRef mention (including event-blind assignment
717            // targets) or a Shadow declaration, plus opaque subtrees.
718            let touched = match (lang.stmt_mentions(s, name_key), lang.stmt_opaque(s)) {
719                (Some(mentions), Some(opaque)) => mentions || opaque,
720                _ => {
721                    let mut w = Watch::<L>::new(name_key, &[]);
722                    scan_region(&*lang, s, &mut w);
723                    let referenced = subtree_contains(&*lang, s, |n| {
724                        lang.kind(n) == NodeKind::VarRef
725                            && lang.var_name(n) == Some(name_str.as_str())
726                    });
727                    referenced || w.opaque || w.shadowed
728                }
729            };
730            // 不透明区间(原文/类表达式/未知调用)可能读 x——不可折回
731            //(Test262 TypedArray speciesctor:class 体内读 flag,
732            // 折回把 `flag=false; …; flag=true` 变成声明即 true)
733            if lang.effect(s) == Effect::Unknown {
734                return None;
735            }
736            if !touched {
737                continue; // 与 x 无关的语句,跳过
738            }
739            // 首个触碰 x 的语句必须是重赋值 `x = x op K`
740            let assign = match lang.kind(s) {
741                NodeKind::Assign => s,
742                NodeKind::ExprStmt => {
743                    let c = lang.children(s);
744                    if c.len() == 1 && lang.kind(c[0]) == NodeKind::Assign {
745                        c[0]
746                    } else {
747                        return None;
748                    }
749                }
750                _ => return None, // 读/遮蔽/不透明先出现 → 中间值被观察
751            };
752            let ach = lang.children(assign).to_vec();
753            if ach.len() != 2 {
754                return None;
755            }
756            let (target, rhs) = (ach[0], ach[1]);
757            if lang.kind(target) != NodeKind::VarRef || !lang.same_binding(id, target) {
758                return None;
759            }
760            // (op, K):复合形态(RHS 即字面量操作数)/ 展开形态
761            // (RHS = Binary{x op K})
762            let (op, k) = match lang.assign_op(assign) {
763                Some(op) => (op, lang.literal(rhs).and_then(|x| x.as_int())?),
764                None => {
765                    if lang.kind(rhs) != NodeKind::Binary {
766                        return None;
767                    }
768                    let op = lang.bin_op(rhs)?;
769                    let rc = lang.children(rhs).to_vec();
770                    if rc.len() != 2 || lang.kind(rc[0]) != NodeKind::VarRef {
771                        return None;
772                    }
773                    // 左操作数必须是 x 自身(同一绑定);右操作数字面量
774                    if !lang.same_binding(id, rc[0]) {
775                        return None;
776                    }
777                    (op, lang.literal(rc[1]).and_then(|x| x.as_int())?)
778                }
779            };
780            if op.is_short_circuit() || op.is_comparison() {
781                return None;
782            }
783            // Div/Rem 且 K==0:折回后异常提前 → 中间语句副作用顺序改变
784            if matches!(op, BinOp::Div | BinOp::Rem) && k == 0 {
785                return None;
786            }
787            // long 域声明的 K 提升(H1 抓获:`long g=2147483647;
788            // g=g+1` 折成 int 字面量对 → i32 环绕 -2147483648,静默
789            // 错值)。init 若是 int 字面量也一并提升——保证 const_fold
790            // 走 i64 域
791            let lit = if lang.is_long_decl(id) {
792                lang.build_int(k, true)
793            } else {
794                lang.build_int(k, false)
795            };
796            let init = if lang.is_long_decl(id) {
797                match lang.literal(init) {
798                    Some(crate::kind::LitRef::Int(v)) => lang.build_int(v, true),
799                    _ => init,
800                }
801            } else {
802                init
803            };
804            let merged = lang.build_bin(op, init, lit);
805            // 声明原位换 init(Splice 孩子),语句删除
806            return Some(Edit::Multi(vec![
807                Edit::Delete { node: s },
808                Edit::Splice { node: id, index: 0, remove: ch.len(), insert: vec![merged] },
809            ]));
810        }
811        None
812    }
813}
814
815pub struct ArithIdentity;
816
817impl<L: Lang> Rule<L> for ArithIdentity {
818    fn name(&self) -> &'static str {
819        "arith_identity"
820    }
821    fn kinds(&self) -> &'static [NodeKind] {
822        &[NodeKind::Binary]
823    }
824    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
825        let RewriteCtx { lang, walk: _ } = ctx;
826        if lang.kind(id) != NodeKind::Binary {
827            return None;
828        }
829        let op = lang.bin_op(id)?;
830        let ch = lang.children(id);
831        let (l, r) = (*ch.first()?, *ch.get(1)?);
832        let int_lit = |n: L::Id, v: i64| -> bool {
833            matches!(lang.literal(n), Some(l) if l.as_int() == Some(v))
834        };
835        let x = match op {
836            BinOp::Add if int_lit(r, 0) => l,
837            BinOp::Add if int_lit(l, 0) => r,
838            BinOp::Sub if int_lit(r, 0) => l,
839            BinOp::Mul if int_lit(r, 1) => l,
840            BinOp::Mul if int_lit(l, 1) => r,
841            BinOp::Div if int_lit(r, 1) => l,
842            _ => return None,
843        };
844        if !lang.is_exact_int(x) {
845            return None;
846        }
847        Some(Edit::Replace {
848            target: id,
849            with: x,
850        })
851    }
852}
853
854// ---------------------------------------------------------------------------
855// 局部变量传播:
856//   T x = V; …; [唯一一次读 x,且区间内无对相关变量的写、无遮蔽]
857//   → 用 V 替换该次读,删除声明。
858//
859// 安全条件:
860//   - effect(V) ≤ MayRead(纯值/局部读):可自由移动,只要求区间内的
861//     显式局部写不与 V 读到的变量相交;
862//   - effect(V) > MayRead(可能抛/写/未知):仅当声明与唯一使用**相邻**、
863//     使用处于直线语句(return/expr-stmt/assign/throw)、路径无条件求值、
864//     且 use 之前求值的兄弟子树效果 ≤ MayRead 时才内联
865//     (保持 V 先于语句内其他效果求值)。
866// ---------------------------------------------------------------------------
867
868
869/// 语句区间内名字的**句法** VarRef 出现次数(逐节点计数;含赋值目标、
870/// 数组下标基——事件模型对这些位置的读不可见)。传播类规则的引用
871/// 计数兜底。注意与「语句级存在性」区分:同一语句里出现两次要计 2。
872///
873/// `key` 用于有索引语言的一次二分查询;未索引节点回退递归计数。
874fn count_name_refs<L: Lang>(
875    lang: &L,
876    stmts: &[L::Id],
877    key: L::NameKey,
878    name: &str,
879) -> usize {
880    let mut n: usize = 0;
881    for &s in stmts {
882        match lang.stmt_var_ref_count(s, key) {
883            Some(c) => n += c as usize,
884            None => count_var_refs(lang, s, name, &mut n),
885        }
886    }
887    n
888}
889
890/// null 字面量不可安全内联的使用位:沿 Paren 链向上(Paren 不改变静
891/// 态类型),首遇 Call/New(实参或 callee 位)或 Member/Index/InstanceOf
892/// 头位即拒绝——裸 null 在这些位置丢失声明类型带来的重载消歧依据
893///(`sb.append(null)` 对 append(CharSequence)/append(char[]) 二义),
894/// 或干脆非法(`null.m()`)。Cast 及其余节点重建类型语境 → 安全。
895/// 传播类规则(local/assign)共用的位置门。
896fn null_unsafe_position<L: Lang>(lang: &L, walk: &Walk<L>, use_id: L::Id) -> bool {
897    let mut cur = use_id;
898    while let Some(&(p, _)) = walk.parents.get(&cur) {
899        match lang.kind(p) {
900            NodeKind::Paren => cur = p,
901            NodeKind::Call
902            | NodeKind::New
903            | NodeKind::Member
904            | NodeKind::Index
905            | NodeKind::InstanceOf => return true,
906            _ => return false,
907        }
908    }
909    false
910}
911
912/// 有声明类型的值(VarRef)不可内联进**类型判定位**:cast 操作数与
913/// instanceof 被测式的可转换性依赖被替换变量的静态类型——`Object o =
914/// k3Var; String s = (String) o;` 合法,内联成 `(String) k3Var` 后引用
915/// 类型不相关 → 不可编译(ddc weixin dt/k 复现:R8 宽化赋值的 Object
916/// 中转变量被传播进 narrowing cast)。沿 Paren 链向上,首遇 Cast /
917/// InstanceOf 即命中;其余位置类型语境与变量无关 → 放行。字面量自带
918/// 类型不受此限(null 走 null_unsafe_position)。
919///
920/// 返回命中的 Cast/InstanceOf 节点(形状层);类型相容性由 Lang 钩子
921/// var_cast_position_ok 裁决——声明类型未解析(反编译自由名)或与目标
922/// 类型同名 → 恒可编译,放行(golden redundant_typed_cast:
923/// `String s = value; return (String) s;` → `(String) value`)。
924fn typed_position<L: Lang>(lang: &L, walk: &Walk<L>, use_id: L::Id) -> Option<L::Id> {
925    let mut cur = use_id;
926    while let Some(&(p, _)) = walk.parents.get(&cur) {
927        match lang.kind(p) {
928            NodeKind::Paren => cur = p,
929            NodeKind::Cast | NodeKind::InstanceOf => return Some(p),
930            _ => return None,
931        }
932    }
933    None
934}
935
936fn count_var_refs<L: Lang>(lang: &L, node: L::Id, name: &str, n: &mut usize) {
937    if lang.kind(node) == NodeKind::VarRef && lang.var_name(node) == Some(name) {
938        *n += 1;
939    }
940    for &c in lang.children(node) {
941        count_var_refs(lang, c, name, n);
942    }
943}
944
945pub struct LocalPropagation;
946
947impl<L: Lang> Rule<L> for LocalPropagation {
948    fn name(&self) -> &'static str {
949        "local_propagation"
950    }
951    fn kinds(&self) -> &'static [NodeKind] {
952        &[NodeKind::VarDecl]
953    }
954    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
955        let RewriteCtx { lang, walk } = ctx;
956        if lang.kind(id) != NodeKind::VarDecl {
957            return None;
958        }
959        let name = lang.var_name(id)?.to_string();
960        let ch = lang.children(id);
961        if ch.len() != 1 {
962            return None; // 必须有且仅有 init
963        }
964        let value = ch[0];
965        // 裸数组字面量只在声明/赋值 RHS 位置合法(Java/多数语言同此);
966        // 传播到任意表达式位置会产出非法源码,且多站点传播会破坏数组对象
967        // 同一性 → 拒绝(显式 new T[]{…} 形态可安全传播)
968        if lang.kind(value) == NodeKind::ArrayLit {
969            return None;
970        }
971        // lambda / 方法引用值:内联进 receiver 位产出非法 Java
972        //((() -> {}).run() / String::length.apply()——对抗波 2 抓获)。
973        // 参数位合法但罕见——统一保守拒绝。
974        if matches!(lang.kind(value), NodeKind::Lambda | NodeKind::MethodRef) {
975            return None;
976        }
977        // 自引用 init(int x = x + 1 之类)直接放弃
978        if subtree_contains(&*lang, value, |n| {
979            lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name.as_str())
980        }) {
981            return None;
982        }
983        let parent = walk.parent(id)?;
984        if lang.kind(parent) != NodeKind::Block {
985            return None;
986        }
987        let index = walk.index(id)?;
988        let stmts: Vec<L::Id> = lang.children(parent).to_vec();
989        if index + 1 > stmts.len() {
990            return None;
991        }
992
993        // 字符类型声明:内联值必须是 Char 字面量(其他 int 型值——字面量
994        // 或表达式——会让 println/拼接观察到 int 而非 char:
995        // `char c = 98; println(c)` 打 'b',内联后打 98)。int 字面量
996        // init 就地转换为 Char 字面量再传播(Java char 域 u16;转换
997        // 随声明删除获得正成本——纯归一化规则过不了严格降本门)。
998        let mut value = value;
999        if lang.is_char_decl(id) {
1000            match lang.literal(value) {
1001                Some(LitRef::Char(_)) => {}
1002                Some(LitRef::Int(v)) => {
1003                    // Java char = UTF-16 单元:域 [0, 0xFFFF](代理区也拒,
1004                    // 打印不合法)
1005                    let c = u32::try_from(v)
1006                        .ok()
1007                        .filter(|&u| (0..=0xFFFF).contains(&u) && !(0xD800..=0xDFFF).contains(&u))
1008                        .and_then(char::from_u32)?;
1009                    value = lang.build_char(c);
1010                }
1011                _ => return None,
1012            }
1013        }
1014        // long 域声明 + int 字面量 init:按 i32 域传播会让后续算术在
1015        // i32 环绕(`long n = 5; n + 2147483647` 曾折成 -2147483644,
1016        // 正确值 2147483652——边界攻击 t06d 抓获)。就地转 Long 字面量
1017        //(5L)传播,const_fold 走 i64 域
1018        if lang.is_long_decl(id) {
1019            if let Some(LitRef::Int(v)) = lang.literal(value) {
1020                value = lang.build_int(v, true);
1021            }
1022        }
1023        // byte/short 窄域声明:Int 字面量内联到**调用实参位**是非法收窄
1024        //(JLS 常量收窄仅限赋值上下文——t04h 抓获:eat(127, 1023) 进
1025        // byte/short 形参不可编译)。使用位置不可判 → 保守拒绝传播
1026        //(char 域已有 Int→Char 转换——Char 字面量到 char 形参合法)
1027        if !lang.is_wide_decl(id) && !lang.is_char_decl(id) {
1028            return None;
1029        }
1030
1031        if !lang.local_propagation_value_ok(id, value) {
1032            return None;
1033        }
1034        // 扫描 decl 之后的区域(用途/遮蔽全区间;写冲突窗口见下)。
1035        // 句法引用计数兜底事件盲区:赋值目标内的读(t[k]=… 的 t)
1036        // 对事件索引不可见——bd.java 差分抓获:load 被替换而 store
1037        // 目标残留,输出引用已删除的变量。
1038        let name_str = lang.var_name(id)?.to_string();
1039        let name_key = lang.var_key(id)?;
1040        // **λ 捕获守卫 + 句法引用计数**(jOOQ Tools.hasAmbiguousNames
1041        // 抓获:`Set names = new HashSet<>(); return anyMatch(fields, f ->
1042        // !names.add(...))` 曾被内联成 `f -> !new HashSet<>().add(...)`——
1043        // 每次谓词新建 Set,add 恒 true,重名检测恒 false)→ 拒绝传播。
1044        // Indexed languages answer both facts per statement in one binary
1045        // search; the recursive pair is the fallback for unindexed nodes.
1046        // 两个条件任一成立即拒绝,故可提前退出。
1047        let mut captured = false;
1048        let mut syntactic_refs = 0usize;
1049        for &st in &stmts[index + 1..] {
1050            let (refs, cap) = match (
1051                lang.stmt_var_ref_count(st, name_key),
1052                lang.stmt_lambda_captures(st, name_key),
1053            ) {
1054                (Some(refs), Some(cap)) => (refs as usize, cap),
1055                _ => {
1056                    let mut n = 0usize;
1057                    count_var_refs(&*lang, st, &name_str, &mut n);
1058                    let cap = subtree_contains(&*lang, st, |n| {
1059                        if lang.is_function_like(n) {
1060                            return subtree_contains(&*lang, n, |m| {
1061                                lang.kind(m) == NodeKind::VarRef
1062                                    && lang.var_name(m) == Some(name_str.as_str())
1063                            });
1064                        }
1065                        false
1066                    });
1067                    (n, cap)
1068                }
1069            };
1070            syntactic_refs += refs;
1071            captured |= cap;
1072            if captured || syntactic_refs > 1 {
1073                break;
1074            }
1075        }
1076        if captured || syntactic_refs != 1 {
1077            return None;
1078        }
1079        let mut wa = Watch::<L>::new(name_key, &[]);
1080        for &s in &stmts[index + 1..] {
1081            // 类体/原文里的读不可见——声明删除会让其悬挂
1082            if lang.stmt_has_opaque_region(s) {
1083                return None;
1084            }
1085            scan_region(&*lang, s, &mut wa);
1086        }
1087        if wa.opaque || wa.shadowed || wa.uses.len() != 1 {
1088            return None;
1089        }
1090        let use_id = wa.uses[0];
1091
1092        // 纯自赋值(x = x)形态拒绝传播:让 self_assign 先删。传播会把
1093        // RHS 换成声明值制造悬挂写 `i = 1`——store_kill 删声明后
1094        // dead_store 因锚点缺失无法清理(golden self_assign_removed
1095        // 抓获:期望 keep(); 实得 i = 1; keep();)
1096        if let Some(&(p, _)) = walk.parents.get(&use_id) {
1097            if lang.kind(p) == NodeKind::Assign && lang.assign_op(p).is_none() {
1098                let pch = lang.children(p);
1099                if pch.len() == 2
1100                    && lang.kind(pch[0]) == NodeKind::VarRef
1101                    && lang.same_binding(pch[0], use_id)
1102                    && pch[1] == use_id
1103                {
1104                    return None;
1105                }
1106            }
1107        }
1108        // raw 泛型接收位守卫(Lang 钩子):声明的静态类型与 init 的静态
1109        // 类型在成员解析上不等价(Java raw `List l = m(); l.add("x")`
1110        // 内联后 CAP#1 硬错——mockito 抓获)。使用位的父/头信息由
1111        // walk 提供(Lang 侧无父指针)
1112        {
1113            let (up, head) = match walk.parents.get(&use_id) {
1114                Some(&(p, _)) => {
1115                    (Some(p), lang.children(p).first() == Some(&use_id))
1116                }
1117                None => (None, false),
1118            };
1119            if lang.receiver_propagation_unsound(id, value, up, head) {
1120                return None;
1121            }
1122        }
1123        // null 字面量**与基本类型字面量**不得内联进需要引用类型的表达式
1124        // 位:调用接收者(`null.setArgName(...)` / `65.getClass()`——后者
1125        // R13 P0-2 抓获:`Object o = +c; o.getClass()` 常量传播产出
1126        // `65.getClass()` 非法 Java);for-each 可迭代位;数组访问基座
1127        //(`null[0]`);字段/数组基座同理。字符串字面量接收者合法
1128        //(`"a".length()`)不在拒绝域
1129        if matches!(
1130            lang.literal(value),
1131            Some(
1132                LitRef::Null
1133                    | LitRef::Int(_)
1134                    | LitRef::Long(_)
1135                    | LitRef::Float(_)
1136                    | LitRef::Double(_)
1137                    | LitRef::Char(_)
1138                    | LitRef::Bool(_)
1139            )
1140        ) {
1141            let mut cur = use_id;
1142            while let Some(&(p, _)) = walk.parents.get(&cur) {
1143                let at_head = lang.children(p).first() == Some(&cur);
1144                match lang.kind(p) {
1145                    // null.m() / null[i](Member 基座与 Index 基座)
1146                    NodeKind::Member | NodeKind::Index if at_head => return None,
1147                    // 静态调用 callee 位(防御)
1148                    NodeKind::Call if at_head => return None,
1149                    // instanceof 被测式(R14 族 C 抓获:`Object o = 3.5f;
1150                    // o instanceof Double` 曾内联成 `3.5f instanceof Double`
1151                    // ——基元模式预览语法不可编译)
1152                    NodeKind::InstanceOf if at_head => return None,
1153                    // for-each 可迭代位(ForEach 的第 1 个孩子 = iterable)
1154                    NodeKind::ForEach => {
1155                        let ch = lang.children(p);
1156                        if ch.first() == Some(&cur) {
1157                            return None;
1158                        }
1159                    }
1160                    // synchronized (null…) 锁位(唯一孩子)
1161                    NodeKind::Synchronized => {
1162                        let ch = lang.children(p);
1163                        if ch.len() == 1 && ch[0] == cur {
1164                            return None;
1165                        }
1166                    }
1167                    _ => {}
1168                }
1169                cur = p;
1170            }
1171        }
1172        // null 字面量实参位:`String s = null; sb.append(s)` 合法——s 的
1173        // 声明类型是重载消歧依据;内联成 `sb.append(null)` 后
1174        // append(CharSequence) 与 append(char[]) 二义,不可编译
1175        //(ddc 输出 PurchasesOrchestrator 复现:R8 null 复位赋值 + 单次
1176        // 使用恰好落在重载族调用上)。沿 Paren 链向上(Paren 不改变静
1177        // 态类型),首遇 Call/New 实参位即拒绝;Cast 及其余节点重建类
1178        // 型语境(`(String) s` 重新钉住重载)→ 放行
1179        if matches!(lang.literal(value), Some(LitRef::Null))
1180            && null_unsafe_position(&*lang, walk, use_id)
1181        {
1182            return None;
1183        }
1184        // VarRef 值不得内联进 cast/instanceof 类型判定位(声明类型即合法
1185        // 性依据——同 null 实参位的族);声明类型未解析或与目标类型
1186        // 同名时恒可编译(var_cast_position_ok 钩子裁决)
1187        if lang.kind(value) == NodeKind::VarRef
1188            && typed_position(&*lang, walk, use_id)
1189                .is_some_and(|t| !lang.var_cast_position_ok(value, t))
1190        {
1191            return None;
1192        }
1193        // value 读到的名字键(写冲突兴趣集;reads 含 name 自身——一票否决)
1194        let mut watch_keys: Vec<L::NameKey> = Vec::new();
1195        collect_read_keys(&*lang, value, &mut watch_keys);
1196        watch_keys.push(name_key);
1197        // 写冲突窗口 = [decl 后, 使用语句]:纯值移动到使用点,
1198        // 使用点之后的写不影响(值已被消费)。首个含 use 的语句即使用语句
1199        //(uses[0] 是遍历序首个读,与 subtree_contains 等价)
1200        let mut wb = Watch::<L>::new(name_key, &watch_keys);
1201        let mut use_stmt_idx = None;
1202        for (off, &s) in stmts[index + 1..].iter().enumerate() {
1203            let uses_before = wb.uses.len();
1204            scan_region(&*lang, s, &mut wb);
1205            if wb.uses.len() > uses_before {
1206                use_stmt_idx = Some(index + 1 + off);
1207                break; // 使用语句之后的写不参与冲突判定
1208            }
1209        }
1210        // 字段读移动守卫:值中含**非局部** VarRef(可变字段——跨调用/跨
1211        // 线程可变):把读从声明点移动到使用点会改变读时机(d.java 抓获:
1212        // `boolean var2 = b; for (a var1 : a()) { var1.i(); if (var2)… }` 折
1213        // 成 if (b)——入口快照变成每次调用后现读,b 有跨文件写点)。
1214        // 窗口内语句须全 Pure(无调用/无写/无读)。
1215        // Call 的 callee VarRef 是方法名(不同命名空间)——不算字段读
1216        if subtree_contains(&*lang, value, |n| {
1217            if lang.kind(n) != NodeKind::VarRef || lang.is_local_var(n) {
1218                return false;
1219            }
1220            // 仅**本单元声明的字段**(局部/参数/未解析名——如手工 AST
1221            // 无作用域——不算)
1222            if !lang.var_name(n).is_some_and(|nm| lang.is_field_name(nm)) {
1223                return false;
1224            }
1225            let is_callee = match walk.parent(n) {
1226                Some(p) => {
1227                    matches!(lang.kind(p), NodeKind::Call) && lang.children(p).first() == Some(&n)
1228                }
1229                None => false,
1230            };
1231            !is_callee
1232        }) {
1233            if let Some(ui) = use_stmt_idx {
1234                for &s in &stmts[index + 1..=ui] {
1235                    if lang.effect(s) != Effect::Pure {
1236                        return None;
1237                    }
1238                }
1239            }
1240        }
1241        // 声明删除前提:use 之后不得再出现对该名字的任何引用(读/写/遮蔽)。
1242        // 值的传播只关心窗口内写,但**删声明**要求名字彻底无残留引用——
1243        // 使用点之后的死写(如 v = "y")同样引用声明,残留会让输出失去声明。
1244        if let Some(ui) = use_stmt_idx {
1245            // 句法版:任何残留引用(含事件不可见的目标位)都拒绝删声明
1246            if count_name_refs(&*lang, &stmts[ui + 1..], name_key, &name_str) != 0 {
1247                return None;
1248            }
1249            let mut wc = Watch::<L>::new(name_key, &[name_key]);
1250            for &s in &stmts[ui + 1..] {
1251                scan_region(&*lang, s, &mut wc);
1252                if wc.opaque || !wc.uses.is_empty() || wc.wrote(name_key) || wc.shadowed {
1253                    return None;
1254                }
1255            }
1256        }
1257        let ve = lang.effect(value);
1258
1259        if ve <= Effect::MayRead {
1260            // 自由移动:区间内的显式局部写不得触碰 V 读到的变量
1261            // (对声明变量自身的写也包含在 writes 里,一票否决)
1262            if let Some(target_key) =
1263                use_stmt_target_write_key(&*lang, &stmts[index + 1..], use_id)
1264            {
1265                wb.clear_wrote(target_key);
1266            }
1267            if wb.opaque || wb.any_write() {
1268                return None;
1269            }
1270            return Some(Edit::Multi(vec![
1271                Edit::Replace {
1272                    target: use_id,
1273                    with: value,
1274                },
1275                Edit::Delete { node: id },
1276            ]));
1277        }
1278
1279        // 有副作用的 V:只允许相邻 + 直线 + 前缀可读
1280        if index + 1 >= stmts.len() {
1281            return None;
1282        }
1283        let stmt = stmts[index + 1];
1284        if !subtree_contains(&*lang, stmt, |n| n == use_id) {
1285            return None;
1286        }
1287        match lang.kind(stmt) {
1288            // VarDecl 同样合法:init 就是语句的全部求值,V 内联进 init 的
1289            // 求值时机与原声明位置一致(相邻保证无插入效果)
1290            NodeKind::Return
1291            | NodeKind::ExprStmt
1292            | NodeKind::Assign
1293            | NodeKind::Throw
1294            | NodeKind::VarDecl => {}
1295            _ => return None,
1296        }
1297        if !straight_path(&*lang, stmt, use_id) {
1298            return None;
1299        }
1300        if !prefix_effects_readable(&*lang, stmt, use_id) {
1301            return None;
1302        }
1303        Some(Edit::Multi(vec![
1304            Edit::Replace {
1305                target: use_id,
1306                with: value,
1307            },
1308            Edit::Delete { node: id },
1309        ]))
1310    }
1311}
1312
1313/// 区域扫描观察:主名字的读 + 小兴趣集的写命中 + 同名遮蔽。
1314///
1315/// 规则预先声明感兴趣的**写名字**(通常 ≤4:主名字 + value 读到的变量),
1316/// 收集期直接判中——零哈希、零集合重建。对比旧版:每次检查重建
1317/// `HashSet<&str>`(分配/重哈希/释放)曾占 release 运行时间 ~50%。
1318pub(crate) struct Watch<L: Lang> {
1319    /// 区域含不透明(Raw)内容:读/写集不可证明——所有守卫保守拒绝
1320    opaque: bool,
1321    /// 主名字键:读收集 + 遮蔽判定(整数等值比较)
1322    name: L::NameKey,
1323    /// 写冲突兴趣名字键(典型 2~4 个)
1324    watch: Vec<L::NameKey>,
1325    /// 与 watch 一一对应:窗口内是否出现过该名字的写
1326    writes_hit: Vec<bool>,
1327    shadowed: bool,
1328    /// 主名字的读(遍历序)
1329    uses: Vec<L::Id>,
1330}
1331
1332impl<L: Lang> Watch<L> {
1333    pub(crate) fn new(name: L::NameKey, watch: &[L::NameKey]) -> Self {
1334        Watch {
1335            opaque: false,
1336            name,
1337            watch: watch.to_vec(),
1338            writes_hit: vec![false; watch.len()],
1339            shadowed: false,
1340            uses: Vec::new(),
1341        }
1342    }
1343    /// 名字键 `k` 在窗口内是否被写(聚合判断)
1344    pub(crate) fn wrote(&self, k: L::NameKey) -> bool {
1345        self.watch
1346            .iter()
1347            .zip(&self.writes_hit)
1348            .any(|(w, h)| *h && *w == k)
1349    }
1350    /// 任一兴趣名字被写
1351    pub(crate) fn any_write(&self) -> bool {
1352        self.writes_hit.iter().any(|h| *h)
1353    }
1354    /// 清除对 `k` 的写命中(use 语句自身对目标的写不参与冲突判定)
1355    pub(crate) fn clear_wrote(&mut self, k: L::NameKey) {
1356        for (w, h) in self.watch.iter().zip(self.writes_hit.iter_mut()) {
1357            if *w == k {
1358                *h = false;
1359            }
1360        }
1361    }
1362}
1363
1364/// 扫描一个语句子树:主名字的**读**、兴趣名字的**写命中**、同名声明遮蔽。
1365fn scan_region<L: Lang>(lang: &L, node: L::Id, w: &mut Watch<L>) {
1366    lang.debug_verify_events(node);
1367    if let Some(events) = lang.region_events(node) {
1368        // 主名字区间:Use 收集 + Shadow 命中 + Write 命中(name 可能在 watch 里)
1369        let lo = events.partition_point(|e| e.key < w.name);
1370        let hi = events.partition_point(|e| e.key <= w.name);
1371        for e in &events[lo..hi] {
1372            match e.kind {
1373                crate::kind::EventKind::Use => w.uses.push(e.node),
1374                crate::kind::EventKind::Shadow => w.shadowed = true,
1375                crate::kind::EventKind::Write => {
1376                    for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1377                        if *wn == w.name {
1378                            *hit = true;
1379                        }
1380                    }
1381                }
1382                // 防御:索引层保证含 Raw 的语句不建索引(键排序分区看不见
1383                // Opaque 键),此处理论上不可达
1384                crate::kind::EventKind::Opaque => w.opaque = true,
1385            }
1386        }
1387        // 其余 watch 键区间:只找 Write
1388        for (i, wk) in w.watch.iter().enumerate() {
1389            if *wk == w.name {
1390                continue; // 已随主名字区间处理
1391            }
1392            let lo = events.partition_point(|e| e.key < *wk);
1393            let hi = events.partition_point(|e| e.key <= *wk);
1394            for e in &events[lo..hi] {
1395                if e.kind == crate::kind::EventKind::Write {
1396                    w.writes_hit[i] = true;
1397                    break; // 区间内任一 Write 即命中
1398                }
1399            }
1400        }
1401        return;
1402    }
1403    // 递归路径也查不透明(索引被失效后落到此处的防御)
1404    if lang.is_opaque(node) {
1405        w.opaque = true;
1406        return;
1407    }
1408    // 无索引语言:原递归遍历(事件序一致;键比较同上)
1409    match lang.kind(node) {
1410        NodeKind::Assign => {
1411            let ch = lang.children(node);
1412            if let Some(&t) = ch.first() {
1413                if lang.kind(t) == NodeKind::VarRef {
1414                    if let Some(k) = lang.var_key(t) {
1415                        for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1416                            if *wn == k {
1417                                *hit = true;
1418                            }
1419                        }
1420                    }
1421                }
1422            }
1423            if let Some(&v) = ch.get(1) {
1424                scan_region(lang, v, w);
1425            }
1426        }
1427        NodeKind::Unary if lang.un_op(node).is_some_and(|o| o.is_incdec()) => {
1428            let ch = lang.children(node);
1429            if let Some(&t) = ch.first() {
1430                if lang.kind(t) == NodeKind::VarRef {
1431                    if let Some(k) = lang.var_key(t) {
1432                        for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1433                            if *wn == k {
1434                                *hit = true;
1435                            }
1436                        }
1437                    }
1438                }
1439            }
1440        }
1441        NodeKind::VarDecl => {
1442            if let Some(k) = lang.var_key(node) {
1443                if k == w.name {
1444                    w.shadowed = true;
1445                }
1446                for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1447                    if *wn == k {
1448                        *hit = true;
1449                    }
1450                }
1451            }
1452            for &c in lang.children(node) {
1453                scan_region(lang, c, w);
1454            }
1455        }
1456        // ForEach 循环变量 / Catch 绑定:在子作用域声明了同名变量 → 视为遮蔽;
1457        // ForEach 每轮给循环变量赋值 → 也算显式写。
1458        NodeKind::ForEach | NodeKind::Catch => {
1459            if let Some(k) = lang.var_key(node) {
1460                if k == w.name {
1461                    w.shadowed = true;
1462                }
1463                if lang.kind(node) == NodeKind::ForEach {
1464                    for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1465                        if *wn == k {
1466                            *hit = true;
1467                        }
1468                    }
1469                }
1470            }
1471            for &c in lang.children(node) {
1472                scan_region(lang, c, w);
1473            }
1474        }
1475        NodeKind::VarRef => {
1476            if lang.var_key(node) == Some(w.name) {
1477                w.uses.push(node);
1478            }
1479        }
1480        NodeKind::Raw => {
1481            // 不可解析原文:读/写集未知
1482            w.opaque = true;
1483        }
1484        _ => {
1485            for &c in lang.children(node) {
1486                scan_region(lang, c, w);
1487            }
1488        }
1489    }
1490}
1491
1492/// root → target 的路径上不得有条件求值结构(Ternary / If / 短路右支)。
1493fn straight_path<L: Lang>(lang: &L, root: L::Id, target: L::Id) -> bool {
1494    fn go<L: Lang>(lang: &L, node: L::Id, target: L::Id) -> bool {
1495        if node == target {
1496            return true;
1497        }
1498        match lang.kind(node) {
1499            NodeKind::Ternary | NodeKind::If => return false,
1500            NodeKind::Binary if lang.bin_op(node).is_some_and(|o| o.is_short_circuit()) => {
1501                return false
1502            }
1503            _ => {}
1504        }
1505        for &c in lang.children(node) {
1506            if subtree_contains(lang, c, |n| n == target) {
1507                return go(lang, c, target);
1508            }
1509        }
1510        false
1511    }
1512    go(lang, root, target)
1513}
1514
1515
1516// ---------------------------------------------------------------------------
1517// 短路折叠:
1518//   false && x → false / true || x → true(x 原本就不求值,恒安全)
1519//   true && x → x / false || x → x / x && true → x / x || false → x(x 保留,恒安全)
1520//   x && false → false / x || true → true(x 的求值被丢弃,需 effect ≤ MayRead)
1521// ---------------------------------------------------------------------------
1522
1523/// id 是否位于标签模板 tag 位(Custom(Template) 的 child 0)。
1524fn in_tag_position<L: Lang>(lang: &L, walk: &crate::walk::Walk<L>, id: L::Id) -> bool {
1525    let Some((p, i)) = walk.parents.get(&id) else { return false };
1526    *i == 0
1527        && lang.kind(*p) == NodeKind::Custom(1) // custom::TEMPLATE
1528        && lang.is_tagged_template(*p)
1529}
1530
1531pub struct BoolShortCircuit;
1532
1533impl<L: Lang> Rule<L> for BoolShortCircuit {
1534    fn name(&self) -> &'static str {
1535        "bool_short_circuit"
1536    }
1537    fn kinds(&self) -> &'static [NodeKind] {
1538        &[NodeKind::Binary]
1539    }
1540    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1541        let RewriteCtx { lang, walk } = ctx;
1542        if lang.kind(id) != NodeKind::Binary {
1543            return None;
1544        }
1545        let op = lang.bin_op(id)?;
1546        if !op.is_short_circuit() {
1547            return None;
1548        }
1549        // `delete (a && b)` 的 delete 语义是「非引用 → true」,折叠成
1550        // `delete b` 会真的尝试删除(返回值改变);
1551        // 标签位折臂会改变 tag 的 Reference/this 绑定。
1552        if in_tag_position(&*lang, &walk, id) {
1553            return None;
1554        }
1555        if let Some((p, i)) = walk.parents.get(&id) {
1556            if *i == 0 && lang.kind(*p) == NodeKind::Unary && lang.un_op(*p) == Some(UnOp::Delete) {
1557                return None;
1558            }
1559        }
1560        let ch = lang.children(id);
1561        let (l, r) = (*ch.first()?, *ch.get(1)?);
1562        let is_lit = |n: L::Id, v: bool| matches!(lang.literal(n), Some(LitRef::Bool(b)) if b == v);
1563        // 左侧字面量:短路后 x 可能根本不求值 → 直接取字面量结果
1564        let with = match (op, is_lit(l, true), is_lit(l, false)) {
1565            (BinOp::And, _, true) => l,  // false && x → false(x 不求值)
1566            (BinOp::Or, true, _) => l,   // true || x → true(x 不求值)
1567            (BinOp::And, true, _) => r,  // true && x → x
1568            (BinOp::Or, _, true) => r,   // false || x → x
1569            _ => {
1570                // 右侧字面量:x && true → x / x || false → x(x 保留求值,恒安全);
1571                // x && false → false / x || true → true(x 的求值被丢弃,需可丢弃)
1572                // x && true → x / x || false → x **仅当 x 是布尔值**:
1573                // JS 里 `sym && true` 对 truthy 的非布尔 x 返回 true 而不是
1574                // x(Test262 logical-and/symbol 抓获;Java 类型系统恒真)。
1575                match (op, is_lit(r, true), is_lit(r, false)) {
1576                    (BinOp::And, true, _) if lang.is_bool(l) => l,
1577                    (BinOp::Or, _, true) if lang.is_bool(l) => l,
1578                    // x && false → false / x || true → true **仅当 x 布尔**:
1579                    // JS 里 truthy/falsy 非布尔 x 返回 x(`null && false`
1580                    // === null),且未解析引用求值会抛 ReferenceError
1581                    //(Test262 logical 家族抓获)
1582                    (BinOp::And, _, true) | (BinOp::Or, true, _)
1583                        if lang.is_bool(l) && lang.effect(l) <= Effect::MayRead => {
1584                            r
1585                        }
1586                    _ => return None,
1587                }
1588            }
1589        };
1590        Some(Edit::Replace {
1591            target: id,
1592            with,
1593        })
1594    }
1595}
1596
1597// ---------------------------------------------------------------------------
1598// 比较取反:!(a == b) → a != b(恒安全,含 NaN/引用);
1599// !(a < b) → a >= b 等次序比较仅精确整数(浮点 NaN 下不成立)。
1600// ---------------------------------------------------------------------------
1601
1602pub struct NotCompare;
1603
1604impl<L: Lang> Rule<L> for NotCompare {
1605    fn name(&self) -> &'static str {
1606        "not_compare"
1607    }
1608    fn kinds(&self) -> &'static [NodeKind] {
1609        &[NodeKind::Unary]
1610    }
1611    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1612        let RewriteCtx { lang, walk: _ } = ctx;
1613        if lang.kind(id) != NodeKind::Unary || lang.un_op(id) != Some(UnOp::Not) {
1614            return None;
1615        }
1616        let inner = *lang.children(id).first()?;
1617        if lang.kind(inner) != NodeKind::Binary {
1618            return None;
1619        }
1620        let op = lang.bin_op(inner)?;
1621        let neg = match op {
1622            BinOp::Eq => BinOp::Ne,
1623            BinOp::Ne => BinOp::Eq,
1624            BinOp::Lt => BinOp::Ge,
1625            BinOp::Le => BinOp::Gt,
1626            BinOp::Gt => BinOp::Le,
1627            BinOp::Ge => BinOp::Lt,
1628            _ => return None,
1629        };
1630        if op.is_short_circuit() {
1631            return None;
1632        }
1633        // 次序比较的取反仅对精确整数成立(NaN: !(a<b)=true 但 a>=b=false)
1634        if matches!(op, BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
1635            let ch = lang.children(inner);
1636            if !lang.is_exact_int(*ch.first()?) || !lang.is_exact_int(*ch.get(1)?) {
1637                return None;
1638            }
1639        }
1640        let ch = lang.children(inner).to_vec();
1641        let rebuilt = lang.build_bin(neg, ch[0], ch[1]);
1642        Some(Edit::Replace {
1643            target: id,
1644            with: rebuilt,
1645        })
1646    }
1647}
1648
1649// ---------------------------------------------------------------------------
1650// 三元折叠:true ? a : b → a;false ? a : b → b(另一支原本不求值,恒安全);
1651//           c ? a : a → a(c 的求值被丢弃,需 effect ≤ MayRead)
1652// ---------------------------------------------------------------------------
1653
1654/// 折臂敏感位:从 `id` 向上穿过 Paren/Ternary(类型透明链),最终落在
1655/// **推断声明**(Java 10 `var`)的 init 上 → true。var 的静态类型由整个
1656/// init 表达式推断(三元 = lub(两臂))——折臂改类型。其余容器(实参/
1657/// 二元操作数/Cast/λ 体)的静态类型由外层结构决定,内层折臂不影响推断。
1658fn in_inferred_var_init<L: Lang>(lang: &L, walk: &Walk<L>, mut id: L::Id) -> bool {
1659    while let Some(&(p, _)) = walk.parents.get(&id) {
1660        match lang.kind(p) {
1661            NodeKind::Paren | NodeKind::Ternary => id = p,
1662            NodeKind::VarDecl => return lang.is_inferred_decl(p),
1663            _ => return false,
1664        }
1665    }
1666    false
1667}
1668
1669pub struct TernaryFold;
1670
1671impl<L: Lang> Rule<L> for TernaryFold {
1672    fn name(&self) -> &'static str {
1673        "ternary_fold"
1674    }
1675    fn kinds(&self) -> &'static [NodeKind] {
1676        &[NodeKind::Ternary]
1677    }
1678    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1679        let RewriteCtx { lang, walk } = ctx;
1680        if lang.kind(id) != NodeKind::Ternary {
1681            return None;
1682        }
1683        if in_tag_position(&*lang, &walk, id) {
1684            return None;
1685        }
1686        let ch = lang.children(id);
1687        let (c, a, b) = (*ch.first()?, *ch.get(1)?, *ch.get(2)?);
1688        match lang.literal(c) {
1689            // R15 P0-1(ClassNotFoundExceptionDueToPrunedCodeTest o1/o2)
1690            // 抓获:`var o1 = false ? new Object(){} : null` 曾折成
1691            // `var o1 = null`——var 推断从匿名类类型变 null 类型,
1692            // `o1 == o1` 不可编译。推断声明 init 位(类型透明链上)
1693            // 拒绝折臂。
1694            Some(LitRef::Bool(true)) => {
1695                if !in_inferred_var_init(&*lang, walk, id) {
1696                    return Some(Edit::Replace { target: id, with: a });
1697                }
1698            }
1699            Some(LitRef::Bool(false))
1700                if !in_inferred_var_init(&*lang, walk, id) => {
1701                    return Some(Edit::Replace { target: id, with: b });
1702                }
1703            _ => {}
1704        }
1705        // c ? a : a:两臂结构全同 → 折叠不改变静态类型(lub(a,a)=a),
1706        // var init 位同样安全
1707        if crate::analysis::structurally_equal(&*lang, a, b) && lang.effect(c) <= Effect::MayRead {
1708            return Some(Edit::Replace { target: id, with: a });
1709        }
1710        None
1711    }
1712}
1713
1714// ---------------------------------------------------------------------------
1715// 三元布尔:c ? true : false → c;c ? false : true → !c(c 为原始 boolean)
1716// ---------------------------------------------------------------------------
1717
1718pub struct TernaryBool;
1719
1720impl<L: Lang> Rule<L> for TernaryBool {
1721    fn name(&self) -> &'static str {
1722        "ternary_bool"
1723    }
1724    fn kinds(&self) -> &'static [NodeKind] {
1725        &[NodeKind::Ternary]
1726    }
1727    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1728        let RewriteCtx { lang, walk: _ } = ctx;
1729        if lang.kind(id) != NodeKind::Ternary {
1730            return None;
1731        }
1732        let ch = lang.children(id);
1733        let (c, a, b) = (*ch.first()?, *ch.get(1)?, *ch.get(2)?);
1734        let lb = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if v);
1735        let is_false = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if !v);
1736        if !lang.is_bool(c) {
1737            return None;
1738        }
1739        let with = if lb(a) && is_false(b) {
1740            c
1741        } else if is_false(a) && lb(b) {
1742            lang.build_unary(UnOp::Not, c)
1743        } else {
1744            return None;
1745        };
1746        Some(Edit::Replace {
1747            target: id,
1748            with,
1749        })
1750    }
1751}
1752
1753// ---------------------------------------------------------------------------
1754// 整数常量折叠 + 字符串拼接:形态匹配在引擎(双字面量 + 非短路/比较
1755// 算符),数值/拼接**语义**全权在 Lang::fold_lit_bin(Java:JLS i32/i64
1756// 环绕 + 移位掩码 + 除零不折 + "a"+1 拼接;其他语言各自定义)。
1757// ---------------------------------------------------------------------------
1758
1759pub struct ConstFoldBin;
1760
1761impl<L: Lang> Rule<L> for ConstFoldBin {
1762    fn name(&self) -> &'static str {
1763        "const_fold_bin"
1764    }
1765    fn kinds(&self) -> &'static [NodeKind] {
1766        &[NodeKind::Binary]
1767    }
1768    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1769        let RewriteCtx { lang, walk: _ } = ctx;
1770        if lang.kind(id) != NodeKind::Binary {
1771            return None;
1772        }
1773        let op = lang.bin_op(id)?;
1774        if op.is_short_circuit() || op.is_comparison() {
1775            return None;
1776        }
1777        let ch = lang.children(id);
1778        let (l, r) = (*ch.first()?, *ch.get(1)?);
1779        // 双字面量才考虑折叠(具体哪些组合可折由语言语义决定)
1780        lang.literal(l)?;
1781        lang.literal(r)?;
1782        let with = lang.fold_lit_bin(op, l, r)?;
1783        Some(Edit::Replace {
1784            target: id,
1785            with,
1786        })
1787    }
1788}
1789
1790// ---------------------------------------------------------------------------
1791// 零元素 / 平凡算术:
1792//   0 * x → 0、x * 0 → 0(x 求值被丢弃:需 effect ≤ MayRead 且精确整数——
1793//     浮点 0.0*NaN=NaN)
1794//   x - x → 0(同名局部整型变量)
1795//   x << 0 / x >> 0 / x >>> 0 → x(位移 0 恒等,x 保留,恒安全)
1796// ---------------------------------------------------------------------------
1797
1798pub struct ArithZero;
1799
1800impl<L: Lang> Rule<L> for ArithZero {
1801    fn name(&self) -> &'static str {
1802        "arith_zero"
1803    }
1804    fn kinds(&self) -> &'static [NodeKind] {
1805        &[NodeKind::Binary]
1806    }
1807    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1808        let RewriteCtx { lang, walk: _ } = ctx;
1809        if lang.kind(id) != NodeKind::Binary {
1810            return None;
1811        }
1812        let op = lang.bin_op(id)?;
1813        let ch = lang.children(id);
1814        let (l, r) = (*ch.first()?, *ch.get(1)?);
1815        let int_lit = |n: L::Id, v: i64| lang.literal(n).and_then(|x| x.as_int()) == Some(v);
1816
1817        // 移位 0:x << 0 → x(恒等)。**需精确整数域**:JS 的移位是
1818        // ToInt32 胁迫——`-4294967295 << 0 === 1` 而非原值(Test262
1819        // left-shift 族抓获)。Java 的 int/long 解析为真 → 中性。
1820        if matches!(op, BinOp::Shl | BinOp::Shr | BinOp::UShr) && int_lit(r, 0) {
1821            if !lang.is_exact_int(l) {
1822                return None;
1823            }
1824            return Some(Edit::Replace {
1825                target: id,
1826                with: l,
1827            });
1828        }
1829        // 0 * x → 0 / x * 0 → 0
1830        if op == BinOp::Mul {
1831            let (zero, other) = if int_lit(l, 0) {
1832                (l, r)
1833            } else if int_lit(r, 0) {
1834                (r, l)
1835            } else {
1836                return None;
1837            };
1838            if !lang.is_exact_int(other) || lang.effect(other) > Effect::MayRead {
1839                return None;
1840            }
1841            return Some(Edit::Replace {
1842                target: id,
1843                with: zero,
1844            });
1845        }
1846        // x - x → 0(同名局部变量,整型)
1847        if op == BinOp::Sub
1848            && lang.kind(l) == NodeKind::VarRef
1849            && lang.kind(r) == NodeKind::VarRef
1850            && lang.same_binding(l, r)
1851            && lang.is_local_var(l)
1852            && lang.is_exact_int(l)
1853        {
1854            let with = lang.build_int(0, false);
1855            return Some(Edit::Replace {
1856                target: id,
1857                with,
1858            });
1859        }
1860        None
1861    }
1862}
1863
1864// ---------------------------------------------------------------------------
1865// 死赋值:x = a; x = b; → x = b;(a 的求值被丢弃:局部变量 x + effect(a) ≤ MayRead)
1866// 含声明形:int x = a; x = b; → int x = b;
1867// ---------------------------------------------------------------------------
1868
1869
1870// ---------------------------------------------------------------------------
1871// 逆运算对抵消(寄存器噪声):`x op= K;` 紧跟 `x op⁻¹= K;`(同局部变量、
1872// 同字面量常量、相邻语句、精确整型)→ 两条全删。
1873//   `x += K; x -= K` → 删(模 2^w 加法群恒等)
1874//   `x ^= K; x ^= K` → 删(XOR 自逆)
1875//   `x -= K; x += K` → 删(对称)
1876// 展开形态 `x = x + K; x = x - K`(RHS = Binary(x op K))与混合形态
1877// (`x += K; x = x - K`)同样处理——反编译器常输出展开形态。
1878// 相邻 ⇒ 两句之间无任何求值 ⇒ 删除恒安全(x 终值不变;x 的旧值
1879// 不被其他求值观察到)。
1880// 守卫:x 为局部变量(is_local_var——字段写有可见副作用);K 为
1881// 无副作用字面量;x 精确整型(浮点 (x+K)-K 在溢出/Inf 上 ≠ x)。
1882// 真实样本:PMD 语料 PMDTaskTestExample.java(a+=1; a-=1 交替 50 次
1883// 完全不动——寄存器噪声经典形态,109 行全为噪声)。
1884// ---------------------------------------------------------------------------
1885
1886pub struct InverseAssignPair;
1887
1888impl<L: Lang> Rule<L> for InverseAssignPair {
1889    fn name(&self) -> &'static str {
1890        "inverse_assign_pair"
1891    }
1892    fn kinds(&self) -> &'static [NodeKind] {
1893        &[NodeKind::Block]
1894    }
1895    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1896        let RewriteCtx { lang, .. } = ctx;
1897        let ch = lang.children(id).to_vec();
1898        if ch.len() < 2 {
1899            return None;
1900        }
1901        for w in 0..ch.len() - 1 {
1902            let (s1, s2) = (ch[w], ch[w + 1]);
1903            // 窗口左端不是赋值语句 → 滑到下一窗口(不能整check放弃)
1904            let Some(a1) = norm_assign(lang, s1) else { continue };
1905            let Some(a2) = norm_assign(lang, s2) else { continue };
1906            // 同一局部变量(同一绑定;名字键仅作快速预筛)
1907            if a1.key != a2.key
1908                || !lang.same_binding(a1.target, a2.target)
1909                || !a1.local
1910                || !a2.local
1911            {
1912                continue;
1913            }
1914            // 精确整型(模 2^w 群恒等性成立;浮点舍入破坏交换律)
1915            if !a1.exact_int || !a2.exact_int {
1916                continue;
1917            }
1918            // 同一常量 + 互逆算符
1919            if a1.k != a2.k {
1920                continue;
1921            }
1922            let inverse = match (a1.op, a2.op) {
1923                (BinOp::Add, BinOp::Sub) | (BinOp::Sub, BinOp::Add) => true,
1924                (BinOp::BitXor, BinOp::BitXor) => true,
1925                _ => false,
1926            };
1927            if !inverse {
1928                continue;
1929            }
1930            // 删除两条语句:先删靠后的(索引不失效),再删靠前的
1931            return Some(Edit::Multi(vec![
1932                Edit::Delete { node: s2 },
1933                Edit::Delete { node: s1 },
1934            ]));
1935        }
1936        None
1937    }
1938}
1939
1940/// 归一化一条赋值语句 → `(变量键, 算符, 常量)`。
1941/// 接受复合形态(`x op= K`,RHS 即字面量)与展开形态(`x = x op K`,
1942/// RHS = Binary{op, VarRef(x), K})。
1943struct NormAssign<L: Lang> {
1944    key: L::NameKey,
1945    target: L::Id,
1946    op: BinOp,
1947    k: i64,
1948    local: bool,
1949    /// 精确整型(浮点 (x+K)-K 在舍入/Inf 上 ≠ x,必须拒绝)
1950    exact_int: bool,
1951}
1952
1953fn norm_assign<L: Lang>(lang: &L, stmt: L::Id) -> Option<NormAssign<L>> {
1954    // 语句形态:ExprStmt{Assign} 或裸 Assign
1955    let a = match lang.kind(stmt) {
1956        NodeKind::ExprStmt => {
1957            let c = lang.children(stmt);
1958            if c.len() == 1 && lang.kind(c[0]) == NodeKind::Assign {
1959                c[0]
1960            } else {
1961                return None;
1962            }
1963        }
1964        NodeKind::Assign => stmt,
1965        _ => return None,
1966    };
1967    let ch = lang.children(a).to_vec();
1968    if ch.len() != 2 {
1969        return None;
1970    }
1971    let (target, rhs) = (ch[0], ch[1]);
1972    if lang.kind(target) != NodeKind::VarRef {
1973        return None;
1974    }
1975    let key = lang.var_key(target)?;
1976    let local = lang.is_local_var(target);
1977    let exact_int = lang.is_exact_int(target);
1978    // RHS 的求值效果:字面量 Pure;Binary(VarRef, lit) 是 Pure/MayRead——
1979    // 展开形态对 x 旧值的读正是我们要抵消的一部分(两句间的观察点
1980    // 为空),但 Binary 内若非纯形态拒绝
1981    match lang.assign_op(a) {
1982        // 复合形态:`x op= K`——RHS 就是操作数
1983        Some(op) => {
1984            let k = lang.literal(rhs).and_then(|x| x.as_int())?;
1985            Some(NormAssign { key, target, op, k, local, exact_int })
1986        }
1987        // 展开形态:`x = x op K`——RHS = Binary{op, VarRef(x), K}
1988        None => {
1989            if lang.kind(rhs) != NodeKind::Binary {
1990                return None;
1991            }
1992            let op = lang.bin_op(rhs)?;
1993            let rc = lang.children(rhs).to_vec();
1994            if rc.len() != 2 {
1995                return None;
1996            }
1997            // 左操作数必须是 x 自身(同一绑定)
1998            if lang.kind(rc[0]) != NodeKind::VarRef || !lang.same_binding(target, rc[0]) {
1999                return None;
2000            }
2001            let k = lang.literal(rc[1]).and_then(|x| x.as_int())?;
2002            Some(NormAssign { key, target, op, k, local, exact_int })
2003        }
2004    }
2005}
2006
2007pub struct DeadStore;
2008
2009impl<L: Lang> Rule<L> for DeadStore {
2010    fn name(&self) -> &'static str {
2011        "dead_store"
2012    }
2013    fn kinds(&self) -> &'static [NodeKind] {
2014        &[NodeKind::VarDecl, NodeKind::Assign, NodeKind::ExprStmt]
2015    }
2016    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2017        let RewriteCtx { lang, walk } = ctx;
2018        // 归一化第一条语句:VarDecl / Assign / ExprStmt{Assign}
2019        let (decl_form, first_assign) = match lang.kind(id) {
2020            NodeKind::VarDecl => {
2021                if lang.children(id).is_empty() {
2022                    return None; // 无 init 的声明不适用
2023                }
2024                (true, None)
2025            }
2026            NodeKind::Assign => (false, Some(id)),
2027            NodeKind::ExprStmt => {
2028                let ch = lang.children(id);
2029                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2030                    (false, Some(ch[0]))
2031                } else {
2032                    return None;
2033                }
2034            }
2035            _ => return None,
2036        };
2037        if decl_form && !lang.dead_decl_allowed(id) {
2038            // 声明形态的死存储删除同受语言侧约束(TS 的 let/const 删除
2039            // 改变 TDZ/词法遮蔽——annexB block-decl 家族抓获)
2040            return None;
2041        }
2042        let first_value = if decl_form {
2043            *lang.children(id).first()?
2044        } else {
2045            let a = first_assign?;
2046            if lang.assign_op(a).is_some() {
2047                return None; // 复合赋值有读语义
2048            }
2049            *lang.children(a).get(1)?
2050        };
2051        if lang.value_may_throw_unresolved(first_value) {
2052            return None;
2053        }
2054
2055        // 父 Block + 相邻下一条语句
2056        let parent = walk.parent(id)?;
2057        if lang.kind(parent) != NodeKind::Block {
2058            return None;
2059        }
2060        let idx = walk.index(id)?;
2061        let stmts = lang.children(parent).to_vec();
2062        let next_stmt = *stmts.get(idx + 1)?;
2063
2064        // 归一化第二条语句:Assign / ExprStmt{Assign}(简单赋值);
2065        // 不是赋值 → 落入「零用途死存储」形态(见下)
2066        let second = match lang.kind(next_stmt) {
2067            NodeKind::Assign => Some(next_stmt),
2068            NodeKind::ExprStmt => {
2069                let ch = lang.children(next_stmt);
2070                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2071                    Some(ch[0])
2072                } else {
2073                    None
2074                }
2075            }
2076            _ => None,
2077        };
2078        let Some(second) = second else {
2079            // 零用途死存储:后继对该变量**无读且无写**(含遮蔽重声明),
2080            // 存入的值永不流出 → 整条删除。值效果 ≤ MayRead 才可丢弃。
2081            // (批量化后可达的形态:传播先消费了击杀写,init 残留为死值;
2082            //   旧逐编辑时序下由相邻对形态逐步吸收,未暴露此缺口。)
2083            // **作用域前提**:名字必须声明于本块(块级作用域,出块不可见)。
2084            // 外层声明的名字可能在本块之后被外层代码读取(if/while 分支里
2085            // 对外层变量的赋值就是典型)→ 扫描只覆盖本块尾部,必须拒绝。
2086            // (property.rs seed=29 的随机程序抓获此漏洞。)
2087            let (name_node, target_local) = if decl_form {
2088                (id, true)
2089            } else {
2090                let t = *lang.children(first_assign?).first()?;
2091                if lang.kind(t) != NodeKind::VarRef {
2092                    return None;
2093                }
2094                (t, lang.is_local_var(t))
2095            };
2096            // 赋值形态:目标必须是本局部(声明形态天然是局部)
2097            if !target_local {
2098                return None;
2099            }
2100            let name = lang.var_name(name_node)?.to_string();
2101            let declared_in_this_block = if decl_form {
2102                true // 检查对象就是本块的声明
2103            } else if parent == walk.root {
2104                // 方法体根块:尾部扫描覆盖到方法末尾——参数/局部变量的
2105                // 零后续事件即可判定为死(方法外不可见)
2106                true
2107            } else {
2108                // 嵌套块:名字必须声明于本块(外层声明的名字可能被
2109                // 本块之后的外层代码读取)
2110                stmts[..idx].iter().any(|&s| {
2111                    lang.kind(s) == NodeKind::VarDecl && lang.var_name(s) == Some(name.as_str())
2112                })
2113            };
2114            if !declared_in_this_block {
2115                return None;
2116            }
2117            // 块内**之前**的原文/类体可能捕获该名字并在之后调用时读
2118            //(`class A{constructor(){flag…}}; flag=true; new A()`——
2119            // 之后语句看不到类体里的读;Test262 TypedArray 家族抓获)
2120            for &s in &stmts[..idx] {
2121                if lang.stmt_has_opaque_region(s) {
2122                    return None;
2123                }
2124            }
2125            let name_key0 = lang.var_key(name_node)?;
2126            // Indexed: a statement that neither mentions the name (any
2127            // VarRef, including event-blind assignment targets, or a
2128            // shadowing declaration) nor contains opaque text cannot read,
2129            // write or shadow it. The recursive event scan remains the
2130            // fallback for unindexed nodes.
2131            for &s in &stmts[idx + 1..] {
2132                if lang.stmt_has_opaque_region(s) {
2133                    return None;
2134                }
2135                match (lang.stmt_mentions(s, name_key0), lang.stmt_opaque(s)) {
2136                    (Some(mentions), Some(opaque)) => {
2137                        if mentions || opaque {
2138                            return None;
2139                        }
2140                    }
2141                    _ => {
2142                        let mut w0 = Watch::<L>::new(name_key0, &[name_key0]);
2143                        scan_region(&*lang, s, &mut w0);
2144                        if w0.opaque
2145                            || !w0.uses.is_empty()
2146                            || w0.wrote(name_key0)
2147                            || w0.shadowed
2148                        {
2149                            return None;
2150                        }
2151                        if subtree_contains(&*lang, s, |n| {
2152                            lang.kind(n) == NodeKind::VarRef
2153                                && lang.var_name(n) == Some(name.as_str())
2154                        }) {
2155                            return None;
2156                        }
2157                    }
2158                }
2159            }
2160            if lang.effect(first_value) > Effect::MayRead {
2161                return None;
2162            }
2163            return Some(Edit::Delete { node: id });
2164        };
2165        if lang.assign_op(second).is_some() {
2166            return None;
2167        }
2168        let nch = lang.children(second);
2169        let (target, value) = (*nch.first()?, *nch.get(1)?);
2170        if lang.kind(target) != NodeKind::VarRef {
2171            return None;
2172        }
2173
2174        // 变量一致性 + 局部性
2175        if decl_form {
2176            if !lang.same_binding(id, target) {
2177                return None;
2178            }
2179        } else {
2180            let a = first_assign?;
2181            let t = *lang.children(a).first()?;
2182            if lang.kind(t) != NodeKind::VarRef
2183                || !lang.same_binding(t, target)
2184                || !lang.is_local_var(t)
2185            {
2186                return None;
2187            }
2188        }
2189        // b 中不得引用 x(x = b 依赖旧值时不可删第一条)
2190        if subtree_contains(&*lang, value, |n| {
2191            lang.kind(n) == NodeKind::VarRef && lang.same_binding(n, target)
2192        }) {
2193            return None;
2194        }
2195        // a 的求值被丢弃:effect ≤ MayRead
2196        if lang.effect(first_value) > Effect::MayRead {
2197            return None;
2198        }
2199
2200        if decl_form {
2201            // int x = a; x = b; → init 换成 b,删除第二条语句
2202            Some(Edit::Multi(vec![
2203                Edit::Replace {
2204                    target: first_value,
2205                    with: value,
2206                },
2207                Edit::Delete {
2208                    node: next_stmt,
2209                },
2210            ]))
2211        } else {
2212            // x = a; x = b; → 删除第一条语句(id 本身:Assign 或 ExprStmt)
2213            Some(Edit::Delete { node: id })
2214        }
2215    }
2216}
2217
2218// ---------------------------------------------------------------------------
2219// 不可达语句删除(块内 return/throw/break/continue 之后的语句)。
2220// 语义安全(永不执行),但属于 DCE 范畴——**默认不启用**,经 all_rules() 选配。
2221// ---------------------------------------------------------------------------
2222
2223/// 块展平:Block 的直接孩子是 Block 且**内层无任何声明**(VarDecl/
2224/// ForEach/Catch 绑定引入作用域)→ 内层语句上提。典型形态:try 剥壳、
2225/// CFF 还原后的裸嵌套块。无声明 ⇒ 无遮蔽 ⇒ 语义等价。
2226pub struct BlockFlatten;
2227
2228impl<L: Lang> Rule<L> for BlockFlatten {
2229    fn name(&self) -> &'static str {
2230        "block_flatten"
2231    }
2232    fn kinds(&self) -> &'static [NodeKind] {
2233        &[NodeKind::Block]
2234    }
2235    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2236        let RewriteCtx { lang, walk } = ctx;
2237        let parent = walk.parent(id)?;
2238        if lang.kind(parent) != NodeKind::Block {
2239            return None;
2240        }
2241        // 内层不得有任何声明(作用域引入)
2242        let inner = lang.children(id).to_vec();
2243        let declares = |n: L::Id| -> bool {
2244            matches!(
2245                lang.kind(n),
2246                NodeKind::VarDecl | NodeKind::ForEach | NodeKind::Catch
2247            )
2248        };
2249        if inner.iter().any(|&s| declares(s)) {
2250            return None;
2251        }
2252        // JS:直接孩子的函数声明同理(Annex B 提升/初始化时机改变)
2253        if lang.block_hoists_decl(id) {
2254            return None;
2255        }
2256        // 模式绑定流作用域(R15 三角化根因 1——BindingsTest1 抓获):
2257        // instanceof 绑定(藏在 while/for/if 条件子树里,不在直接孩子的
2258        // declares 三类中)的作用域按 JLS 6.3 可越出语句到 enclosing
2259        // block——上提曾使两个匿名块内的 String s 绑定落进同一方法域
2260        //(javac「变量 s 重复定义」)
2261        if lang.has_pattern_bindings(id) {
2262            return None;
2263        }
2264        let idx = walk.index(id)?;
2265        Some(Edit::Splice {
2266            node: parent,
2267            index: idx,
2268            remove: 1,
2269            insert: inner,
2270        })
2271    }
2272}
2273
2274/// 数值双重取负:-(-x) → x(补码回绕下恒等:-(-MIN) ≡ MIN ≡ x);
2275/// -(-lit) → |lit|(解析器不折负字面量的外层负)。
2276pub struct DoubleNegFold;
2277
2278impl<L: Lang> Rule<L> for DoubleNegFold {
2279    fn name(&self) -> &'static str {
2280        "double_neg_fold"
2281    }
2282    fn kinds(&self) -> &'static [NodeKind] {
2283        &[NodeKind::Unary]
2284    }
2285    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2286        let RewriteCtx { lang, .. } = ctx;
2287        if lang.un_op(id) != Some(UnOp::Neg) {
2288            return None;
2289        }
2290        let inner = *lang.children(id).first()?;
2291        if lang.un_op(inner) == Some(UnOp::Neg) {
2292            let x = *lang.children(inner).first()?;
2293            // `-(-x)` 仅对精确整数恒等:布尔/字符串/null 会被 ToNumber
2294            //(`-(-true)` = 1 而不是 true);浮点 -0 也会改变符号。
2295            if !lang.is_exact_int(x) {
2296                return None;
2297            }
2298            return Some(Edit::Replace {
2299                target: id,
2300                with: x,
2301            });
2302        }
2303        // -(-lit):负数值字面量取外层负 → 正字面量(宽度溢出时语言侧
2304        // 拒绝——Java i32 的 `-(-2147483648)` 无法表示)
2305        if let Some((v, wide)) = lang.literal(inner).and_then(|l| match l {
2306            LitRef::Int(v) => Some((v, false)),
2307            LitRef::Long(v) => Some((v, true)),
2308            _ => None,
2309        }) {
2310            if v < 0 {
2311                let n = lang.fold_lit_neg(v, wide)?;
2312                let with = lang.build_int(n, wide);
2313                return Some(Edit::Replace {
2314                    target: id,
2315                    with,
2316                });
2317            }
2318        }
2319        None
2320    }
2321}
2322
2323pub struct UnreachableAfterTerminal;
2324
2325impl<L: Lang> Rule<L> for UnreachableAfterTerminal {
2326    fn name(&self) -> &'static str {
2327        "unreachable_after_terminal"
2328    }
2329    fn kinds(&self) -> &'static [NodeKind] {
2330        &[NodeKind::Block]
2331    }
2332    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2333        let RewriteCtx { lang, walk: _ } = ctx;
2334        if lang.kind(id) != NodeKind::Block {
2335            return None;
2336        }
2337        let children = lang.children(id).to_vec();
2338        let terminal = |n: L::Id| -> bool {
2339            matches!(
2340                lang.kind(n),
2341                NodeKind::Return | NodeKind::Throw | NodeKind::Break | NodeKind::Continue
2342            )
2343        };
2344        let mut cut: Option<(usize, usize)> = None;
2345        for (i, &s) in children.iter().enumerate() {
2346            if terminal(s) && i + 1 < children.len() {
2347                cut = Some((i + 1, children.len() - i - 1));
2348                break;
2349            }
2350        }
2351        let (index, remove) = cut?;
2352        Some(Edit::Splice {
2353            node: id,
2354            index,
2355            remove,
2356            insert: Vec::new(),
2357        })
2358    }
2359}
2360
2361
2362// ---------------------------------------------------------------------------
2363// 比较常量折叠(反混淆核心):1 < 2 → true、2 == 3 → false。
2364// 仅整数(含 long)与布尔字面量;浮点(NaN 语义)与字符串(引用比较)不折。
2365// 连锁:boolean b = 2 > 1; if (b) {...} → 不透明谓词被完全击穿。
2366// ---------------------------------------------------------------------------
2367
2368pub struct CmpConstFold;
2369
2370impl<L: Lang> Rule<L> for CmpConstFold {
2371    fn name(&self) -> &'static str {
2372        "cmp_const_fold"
2373    }
2374    fn kinds(&self) -> &'static [NodeKind] {
2375        &[NodeKind::Binary]
2376    }
2377    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2378        let RewriteCtx { lang, walk: _ } = ctx;
2379        if lang.kind(id) != NodeKind::Binary {
2380            return None;
2381        }
2382        let op = lang.bin_op(id)?;
2383        if !op.is_comparison() {
2384            return None;
2385        }
2386        let ch = lang.children(id);
2387        let (l, r) = (*ch.first()?, *ch.get(1)?);
2388        // 双字面量才考虑折叠;可比较种类与比较语义由语言决定
2389        // (Java:整数/字符/布尔 Eq/Ne;字符串是引用比较 → 不折)
2390        lang.literal(l)?;
2391        lang.literal(r)?;
2392        let v = lang.fold_lit_cmp(op, l, r)?;
2393        let with = lang.build_bool(v);
2394        Some(Edit::Replace {
2395            target: id,
2396            with,
2397        })
2398    }
2399}
2400
2401// ---------------------------------------------------------------------------
2402// 位运算恒等式(混淆器高频产物):
2403//   x ^ 0 → x、0 ^ x → x、x | 0 → x、0 | x → x、x & -1 → x、-1 & x → x(x 保留,恒安全)
2404//   x | -1 → -1、x & 0 → 0、0 & x → 0(x 求值被丢弃 → effect ≤ MayRead)
2405//   x ^ x → 0(同名局部整型变量)
2406// ---------------------------------------------------------------------------
2407
2408pub struct BitIdentity;
2409
2410impl<L: Lang> Rule<L> for BitIdentity {
2411    fn name(&self) -> &'static str {
2412        "bit_identity"
2413    }
2414    fn kinds(&self) -> &'static [NodeKind] {
2415        &[NodeKind::Binary]
2416    }
2417    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2418        let RewriteCtx { lang, walk: _ } = ctx;
2419        if lang.kind(id) != NodeKind::Binary {
2420            return None;
2421        }
2422        let op = lang.bin_op(id)?;
2423        if !matches!(op, BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor) {
2424            return None;
2425        }
2426        let ch = lang.children(id);
2427        let (l, r) = (*ch.first()?, *ch.get(1)?);
2428        let int_lit = |n: L::Id| lang.literal(n).and_then(|x| x.as_int());
2429
2430        // x ^ x → 0(同名局部变量)
2431        if op == BinOp::BitXor
2432            && lang.kind(l) == NodeKind::VarRef
2433            && lang.kind(r) == NodeKind::VarRef
2434            && lang.same_binding(l, r)
2435            && lang.is_local_var(l)
2436            && lang.is_exact_int(l)
2437        {
2438            let with = lang.build_int(0, false);
2439            return Some(Edit::Replace {
2440                target: id,
2441                with,
2442            });
2443        }
2444
2445        // 字面量在任一侧(互换尝试)
2446        for (x, k) in [(l, r), (r, l)] {
2447            let Some(kv) = int_lit(k) else {
2448                continue;
2449            };
2450            if !lang.is_exact_int(x) {
2451                continue;
2452            }
2453            let identity = match (op, kv) {
2454                (BinOp::BitXor, 0) => Some(x),          // x ^ 0 → x
2455                (BinOp::BitOr, 0) => Some(x),           // x | 0 → x
2456                (BinOp::BitAnd, -1) => Some(x),         // x & -1 → x
2457                (BinOp::BitAnd, 0) | (BinOp::BitOr, -1) => {
2458                    // x & 0 → 0 / x | -1 → -1:丢弃 x 求值,需可丢弃
2459                    if lang.effect(x) > Effect::MayRead {
2460                        None
2461                    } else {
2462                        Some(k)
2463                    }
2464                }
2465                _ => None,
2466            };
2467            if let Some(with) = identity {
2468                return Some(Edit::Replace {
2469                    target: id,
2470                    with,
2471                });
2472            }
2473        }
2474        None
2475    }
2476}
2477
2478// ---------------------------------------------------------------------------
2479// 算术/异或重结合(混淆器经典):(x + K1) - K2 → x ± K、(x ^ K1) ^ K2 → x ^ K。
2480// 整数回绕算术下恒安全;K == 0 时直接得 x((x + 5) - 5 → x、(x ^ 84) ^ 84 → x)。
2481// ---------------------------------------------------------------------------
2482
2483pub struct ArithReassoc;
2484
2485impl<L: Lang> Rule<L> for ArithReassoc {
2486    fn name(&self) -> &'static str {
2487        "arith_reassoc"
2488    }
2489    fn kinds(&self) -> &'static [NodeKind] {
2490        &[NodeKind::Binary]
2491    }
2492    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2493        let RewriteCtx { lang, walk: _ } = ctx;
2494        if lang.kind(id) != NodeKind::Binary {
2495            return None;
2496        }
2497        let op2 = lang.bin_op(id)?;
2498        if !matches!(op2, BinOp::Add | BinOp::Sub | BinOp::BitXor) {
2499            return None;
2500        }
2501        let och = lang.children(id).to_vec();
2502        let inner = och[0];
2503        if lang.kind(inner) != NodeKind::Binary {
2504            return None;
2505        }
2506        let op1 = lang.bin_op(inner)?;
2507        let ich = lang.children(inner).to_vec();
2508        let x = ich[0];
2509
2510        // 字符串拼接重结合:(x + "K1") + "K2" → x + "K1K2"
2511        // (拼接满足结合律且各操作数恰按序求值一次;x 任意类型)。
2512        // **整链折叠**:左倾链 (((x+"a")+"a")+"a")… 一刀折成 x+"aaa…"——
2513        // 逐层折每 pass 一层是 O(n²)(openjdk DeepStringConcat 32001 项
2514        // 78s/32k passes 抓获;整链后 1 编辑收敛)
2515        if op1 == BinOp::Add && op2 == BinOp::Add {
2516            if let (Some(LitRef::Str(k1)), Some(LitRef::Str(k2))) =
2517                (lang.literal(ich[1]), lang.literal(och[1]))
2518            {
2519                // 收集左倾链(外→内),要求每层 (child + "str") 同构
2520                let mut chain: Vec<L::Id> = vec![id];
2521                let mut cur = id;
2522                loop {
2523                    let cch = lang.children(cur).to_vec();
2524                    let next = cch[0];
2525                    let deeper = lang.kind(next) == NodeKind::Binary
2526                        && lang.bin_op(next) == Some(BinOp::Add)
2527                        && matches!(lang.literal(lang.children(next).to_vec()[1]),
2528                            Some(LitRef::Str(_)));
2529                    if deeper {
2530                        chain.push(next);
2531                        cur = next;
2532                    } else {
2533                        break;
2534                    }
2535                }
2536                // base = 最内层的左操作数;parts 内→外 = 源顺序
2537                let innermost = *chain.last().unwrap();
2538                let ich2 = lang.children(innermost).to_vec();
2539                let base = ich2[0];
2540                let mut joined = String::new();
2541                for &n in chain.iter().rev() {
2542                    if let Some(LitRef::Str(k)) =
2543                        lang.literal(lang.children(n).to_vec()[1])
2544                    {
2545                        joined.push_str(k);
2546                    }
2547                }
2548                let _ = (k1, k2); // 已并入 joined
2549                let lit = lang.build_str(&joined);
2550                let with = lang.build_bin(BinOp::Add, base, lit);
2551                return Some(Edit::Replace {
2552                    target: id,
2553                    with,
2554                });
2555            }
2556        }
2557
2558        // 整数重结合:(x ± K1) ± K2 → x ± K、(x ^ K1) ^ K2 → x ^ K
2559        // 常量合并与宽度回绕语义在 Lang::reassoc_delta(Java:i32/i64
2560        // JLS 环绕——i32 合并溢出不回绕会产出非法字面量)
2561        let k1 = lang.literal(ich[1]).and_then(|x| x.as_int())?;
2562        let k2 = lang.literal(och[1]).and_then(|x| x.as_int())?;
2563        if !lang.is_exact_int(x) {
2564            return None;
2565        }
2566        let is_long = matches!(lang.literal(ich[1]), Some(LitRef::Long(_)))
2567            || matches!(lang.literal(och[1]), Some(LitRef::Long(_)));
2568        let with = match lang.reassoc_delta(op1, k1, op2, k2, is_long)? {
2569            ReassocOutcome::Neutral => x,
2570            ReassocOutcome::Lit(lit, op) => lang.build_bin(op, x, lit),
2571        };
2572        Some(Edit::Replace {
2573            target: id,
2574            with,
2575        })
2576    }
2577}
2578
2579// ---------------------------------------------------------------------------
2580// if→三元归并(反编译器/混淆器经典形态):
2581//   if (c) return a; else return b;    →  return c ? a : b;
2582//   if (c) return a; return b;(收尾) →  return c ? a : b;
2583// 恒安全:c 单次求值、a/b 条件求值在两种形态下一致(三元是惰性的)。
2584// 布尔特例由 ternary_bool 继续收敛成 return c / return !c。
2585// ---------------------------------------------------------------------------
2586
2587pub struct IfToTernary;
2588
2589impl<L: Lang> Rule<L> for IfToTernary {
2590    fn name(&self) -> &'static str {
2591        "if_to_ternary"
2592    }
2593    fn kinds(&self) -> &'static [NodeKind] {
2594        &[NodeKind::If]
2595    }
2596    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2597        let RewriteCtx { lang, walk } = ctx;
2598        if lang.kind(id) != NodeKind::If {
2599            return None;
2600        }
2601        let ch = lang.children(id).to_vec();
2602        let c = ch[0];
2603
2604        // 提取"单 return 语句"分支:Block{Return} 或裸 Return
2605        let single_ret = |n: L::Id| -> Option<Option<L::Id>> {
2606            match lang.kind(n) {
2607                NodeKind::Return => Some(lang.children(n).first().copied()),
2608                NodeKind::Block if lang.children(n).len() == 1 => {
2609                    let inner = lang.children(n)[0];
2610                    if lang.kind(inner) == NodeKind::Return {
2611                        Some(lang.children(inner).first().copied())
2612                    } else {
2613                        None
2614                    }
2615                }
2616                _ => None,
2617            }
2618        };
2619
2620        if ch.len() == 3 {
2621            // 完整 if-else:两支都是单 return
2622            let a = single_ret(ch[1])?;
2623            let b = single_ret(ch[2])?;
2624            let with = match (a, b) {
2625                (Some(va), Some(vb)) => {
2626                    let t = lang.build_ternary(c, va, vb);
2627                    lang.build_return(Some(t))
2628                }
2629                (None, None) => lang.build_return(None),
2630                _ => return None,
2631            };
2632            return Some(Edit::Replace {
2633                target: id,
2634                with,
2635            });
2636        }
2637
2638        // if-then + 紧随的收尾 return(必须是父块最后一条语句)
2639        if ch.len() != 2 {
2640            return None;
2641        }
2642        let a = single_ret(ch[1])?;
2643        let parent = walk.parent(id)?;
2644        if lang.kind(parent) != NodeKind::Block {
2645            return None;
2646        }
2647        let idx = walk.index(id)?;
2648        let stmts = lang.children(parent).to_vec();
2649        if idx + 2 != stmts.len() {
2650            return None; // return 必须是最后一条
2651        }
2652        let tail = stmts[idx + 1];
2653        if lang.kind(tail) != NodeKind::Return {
2654            return None;
2655        }
2656        let b = lang.children(tail).first().copied();
2657        let with = match (a, b) {
2658            (Some(va), Some(vb)) => {
2659                let t = lang.build_ternary(c, va, vb);
2660                lang.build_return(Some(t))
2661            }
2662            (None, None) => lang.build_return(None),
2663            _ => return None,
2664        };
2665        Some(Edit::Splice {
2666            node: parent,
2667            index: idx,
2668            remove: 2,
2669            insert: vec![with],
2670        })
2671    }
2672}
2673
2674// ---------------------------------------------------------------------------
2675// if→三元赋值归并:
2676//   if (c) { x = a; } else { x = b; }  →  x = c ? a : b;
2677// 恒安全(x 为变量名,两种形态各求值一次;a/b 条件求值一致)。
2678// ---------------------------------------------------------------------------
2679
2680pub struct IfAssignTernary;
2681
2682impl<L: Lang> Rule<L> for IfAssignTernary {
2683    fn name(&self) -> &'static str {
2684        "if_assign_ternary"
2685    }
2686    fn kinds(&self) -> &'static [NodeKind] {
2687        &[NodeKind::If]
2688    }
2689    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2690        let RewriteCtx { lang, walk: _ } = ctx;
2691        if lang.kind(id) != NodeKind::If {
2692            return None;
2693        }
2694        let ch = lang.children(id).to_vec();
2695        if ch.len() != 3 {
2696            return None;
2697        }
2698        let c = ch[0];
2699        // 单赋值分支:Block{Assign} / Block{ExprStmt{Assign}} / 裸(ExprStmt 包裹的)Assign
2700        let single_assign = |n: L::Id| -> Option<(L::Id, L::Id)> {
2701            let unwrap = |i: L::Id| -> L::Id {
2702                if lang.kind(i) == NodeKind::ExprStmt {
2703                    let ch = lang.children(i);
2704                    if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2705                        return ch[0];
2706                    }
2707                }
2708                i
2709            };
2710            let inner = match lang.kind(n) {
2711                NodeKind::Assign => n,
2712                NodeKind::ExprStmt => unwrap(n),
2713                NodeKind::Block if lang.children(n).len() == 1 => unwrap(lang.children(n)[0]),
2714                _ => return None,
2715            };
2716            if lang.kind(inner) != NodeKind::Assign {
2717                return None;
2718            }
2719            if lang.assign_op(inner).is_some() {
2720                return None;
2721            }
2722            let a = lang.children(inner).to_vec();
2723            if lang.kind(a[0]) != NodeKind::VarRef {
2724                return None;
2725            }
2726            Some((a[0], a[1]))
2727        };
2728        let (t1, va) = single_assign(ch[1])?;
2729        let (t2, vb) = single_assign(ch[2])?;
2730        if !lang.same_binding(t1, t2) || lang.var_name(t1).is_none() {
2731            return None;
2732        }
2733        let t = lang.build_ternary(c, va, vb);
2734        let with = lang.build_assign(t1, t);
2735        Some(Edit::Replace {
2736            target: id,
2737            with,
2738        })
2739    }
2740}
2741
2742
2743// ---------------------------------------------------------------------------
2744// 声明-赋值合并(反编译器把声明与赋值拆开的形态):
2745//   int x; x = 5;  →  int x = 5;
2746// 守卫:相邻同块、简单赋值、value 不引用 x(依赖旧值则非法)。
2747// 之后 local_propagation 可继续把 init 内联到唯一使用处。
2748// ---------------------------------------------------------------------------
2749
2750pub struct DeclAssignMerge;
2751
2752impl<L: Lang> Rule<L> for DeclAssignMerge {
2753    fn name(&self) -> &'static str {
2754        "decl_assign_merge"
2755    }
2756    fn kinds(&self) -> &'static [NodeKind] {
2757        &[NodeKind::VarDecl]
2758    }
2759    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2760        let RewriteCtx { lang, walk } = ctx;
2761        if lang.kind(id) != NodeKind::VarDecl || !lang.children(id).is_empty() {
2762            return None; // 必须是无 init 声明
2763        }
2764        let name = lang.var_name(id)?.to_string();
2765        let parent = walk.parent(id)?;
2766        if lang.kind(parent) != NodeKind::Block {
2767            return None;
2768        }
2769        let idx = walk.index(id)?;
2770        let stmts = lang.children(parent).to_vec();
2771        let next = *stmts.get(idx + 1)?;
2772        // 下一条:Assign(x, v) / ExprStmt{Assign(x, v)}(简单赋值)
2773        let assign = match lang.kind(next) {
2774            NodeKind::Assign => next,
2775            NodeKind::ExprStmt => {
2776                let ch = lang.children(next);
2777                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2778                    ch[0]
2779                } else {
2780                    return None;
2781                }
2782            }
2783            _ => return None,
2784        };
2785        if lang.assign_op(assign).is_some() {
2786            return None;
2787        }
2788        let ach = lang.children(assign).to_vec();
2789        let (target, value) = (ach[0], ach[1]);
2790        if lang.kind(target) != NodeKind::VarRef || lang.var_name(target) != Some(name.as_str()) {
2791            return None;
2792        }
2793        // value 不得引用 x(读旧值)
2794        if subtree_contains(&*lang, value, |n| {
2795            lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name.as_str())
2796        }) {
2797            return None;
2798        }
2799        Some(Edit::Multi(vec![
2800            Edit::Splice {
2801                node: id,
2802                index: 0,
2803                remove: 0,
2804                insert: vec![value],
2805            },
2806            Edit::Delete { node: next },
2807        ]))
2808    }
2809}
2810
2811// ---------------------------------------------------------------------------
2812// 赋值传播(寄存器拷贝消除,jadx/jcdc 产物):
2813//   x = v; …唯一一次读 x(区间内无对 x / v 读集的写、无遮蔽)…
2814//   → 用 v 替换该次读,删除赋值。
2815// 安全锚点:x 必须在本块内有前置声明(赋值无作用域,块外读取会破坏语义)。
2816// v 纯/只读 → 自由移动(写冲突检查);v 有副作用 → 相邻 + 直线 + 可读前缀。
2817// ---------------------------------------------------------------------------
2818
2819pub struct AssignPropagation;
2820
2821impl<L: Lang> Rule<L> for AssignPropagation {
2822    fn name(&self) -> &'static str {
2823        "assign_propagation"
2824    }
2825    fn kinds(&self) -> &'static [NodeKind] {
2826        &[NodeKind::Assign, NodeKind::ExprStmt]
2827    }
2828    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2829        let RewriteCtx { lang, walk } = ctx;
2830        // 语句形态:Assign / ExprStmt{Assign}
2831        let (assign, stmt_node) = match lang.kind(id) {
2832            NodeKind::Assign => (id, id),
2833            NodeKind::ExprStmt => {
2834                let ch = lang.children(id);
2835                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2836                    (ch[0], id)
2837                } else {
2838                    return None;
2839                }
2840            }
2841            _ => return None,
2842        };
2843        if lang.assign_op(assign).is_some() {
2844            return None;
2845        }
2846        let ach = lang.children(assign).to_vec();
2847        let (target, value) = (ach[0], ach[1]);
2848        if lang.kind(target) != NodeKind::VarRef || !lang.is_local_var(target) {
2849            return None;
2850        }
2851        // 裸数组字面量只在声明/赋值 RHS 位置合法,传播会产出非法源码 → 拒绝
2852        if lang.kind(value) == NodeKind::ArrayLit {
2853            return None;
2854        }
2855        // lambda / 方法引用值同 ArrayLit:receiver 位非法 → 保守拒绝
2856        if matches!(lang.kind(value), NodeKind::Lambda | NodeKind::MethodRef) {
2857            return None;
2858        }
2859        let name = lang.var_name(target)?.to_string();
2860
2861        let parent = walk.parent(stmt_node)?;
2862        if lang.kind(parent) != NodeKind::Block {
2863            return None;
2864        }
2865        let idx = walk.index(stmt_node)?;
2866        let stmts = lang.children(parent).to_vec();
2867
2868        // 锚点:x 在本块 idx 之前有 VarDecl(保证 x 不会逃逸到块外)
2869        let anchored = stmts[..idx].iter().any(|&s| {
2870            lang.kind(s) == NodeKind::VarDecl && lang.var_name(s) == Some(name.as_str())
2871        });
2872        if !anchored {
2873            return None;
2874        }
2875
2876        // value 不引用 x(x = x + 1 之类)
2877        if subtree_contains(&*lang, value, |n| {
2878            lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name.as_str())
2879        }) {
2880            return None;
2881        }
2882
2883        // 扫描赋值之后的区域(用途/遮蔽全区间;写冲突窗口见下)。
2884        // 句法引用计数兜底(同 local_propagation——bd.java 教训)
2885        let name_key = lang.var_key(target)?;
2886        if count_name_refs(&*lang, &stmts[idx + 1..], name_key, &name) != 1 {
2887            return None;
2888        }
2889        let mut wa = Watch::<L>::new(name_key, &[]);
2890        for &s in &stmts[idx + 1..] {
2891            if lang.stmt_has_opaque_region(s) {
2892                return None;
2893            }
2894            scan_region(&*lang, s, &mut wa);
2895        }
2896        if wa.opaque || wa.shadowed || wa.uses.len() != 1 {
2897            return None;
2898        }
2899        let use_id = wa.uses[0];
2900        // null 字面量:接收者位(AST 形态 Call→Member→[recv],VarRef 的
2901        // 父是 Member)或静态调用 callee(防御)之外,**实参位**同样拒绝
2902        // ——`str2 = null; append(str2)` 合法(str2 声明类型钉住重载),
2903        // 内联成 `append(null)` 后 append(CharSequence)/append(char[])
2904        // 二义(ddc 输出 PurchasesOrchestrator 复现)。null_unsafe_position
2905        // 沿 Paren 链判位;Cast 包裹((String) s)重建类型 → 放行
2906        if matches!(lang.literal(value), Some(LitRef::Null))
2907            && null_unsafe_position(&*lang, walk, use_id)
2908        {
2909            return None;
2910        }
2911        // VarRef 值不得内联进 cast/instanceof 类型判定位(声明类型即合法
2912        // 性依据——同 null 实参位的族);声明类型未解析或与目标类型
2913        // 同名时恒可编译(var_cast_position_ok 钩子裁决)
2914        if lang.kind(value) == NodeKind::VarRef
2915            && typed_position(&*lang, walk, use_id)
2916                .is_some_and(|t| !lang.var_cast_position_ok(value, t))
2917        {
2918            return None;
2919        }
2920        // 写冲突兴趣集:value 读到的名字键 + x 自身(自身被写 → 覆盖,拒绝)
2921        let mut watch_keys: Vec<L::NameKey> = Vec::new();
2922        collect_read_keys(&*lang, value, &mut watch_keys);
2923        watch_keys.push(name_key);
2924        let mut wb = Watch::<L>::new(name_key, &watch_keys);
2925        for &s in &stmts[idx + 1..] {
2926            let uses_before = wb.uses.len();
2927            scan_region(&*lang, s, &mut wb);
2928            if uses_before < wb.uses.len() {
2929                break; // 使用语句之后的写不参与冲突判定
2930            }
2931        }
2932        // x 自身在窗口内被写 → 赋值会被覆盖,拒绝(先于目标写排除)
2933        if wb.wrote(name_key) {
2934            return None;
2935        }
2936        let ve = lang.effect(value);
2937
2938        if ve <= Effect::MayRead {
2939            if let Some(target_key) = use_stmt_target_write_key(&*lang, &stmts[idx + 1..], use_id) {
2940                wb.clear_wrote(target_key);
2941            }
2942            if wb.any_write() {
2943                return None;
2944            }
2945            return Some(Edit::Multi(vec![
2946                Edit::Replace {
2947                    target: use_id,
2948                    with: value,
2949                },
2950                Edit::Delete { node: stmt_node },
2951            ]));
2952        }
2953
2954        // 有副作用:相邻 + 直线 + 可读前缀(与 local_propagation 同判据)
2955        if idx + 1 >= stmts.len() {
2956            return None;
2957        }
2958        let stmt = stmts[idx + 1];
2959        if !subtree_contains(&*lang, stmt, |n| n == use_id) {
2960            return None;
2961        }
2962        match lang.kind(stmt) {
2963            NodeKind::Return
2964            | NodeKind::ExprStmt
2965            | NodeKind::Assign
2966            | NodeKind::Throw
2967            | NodeKind::VarDecl => {}
2968            _ => return None,
2969        }
2970        if !straight_path(&*lang, stmt, use_id) {
2971            return None;
2972        }
2973        if !prefix_effects_readable(&*lang, stmt, use_id) {
2974            return None;
2975        }
2976        Some(Edit::Multi(vec![
2977            Edit::Replace {
2978                target: use_id,
2979                with: value,
2980            },
2981            Edit::Delete { node: stmt_node },
2982        ]))
2983    }
2984}
2985
2986
2987/// 单一使用点若恰为 `target = use` 的 RHS,则该语句对 target 的写发生在
2988/// use 求值**之后**——从写冲突集中排除(否则 `int v = x + s; x = v;` 这类
2989/// 循环尾寄存器回拷永远无法内联)。
2990/// 排除目标必须≠被传播变量自身:`int i = 1; i = i;` 里排除 `i` 会删掉声明
2991/// 留下无绑定的赋值(该场景由 self_assign 处理)。
2992fn use_stmt_target_write_key<L: Lang>(
2993    lang: &L,
2994    stmts_after: &[L::Id],
2995    use_id: L::Id,
2996) -> Option<L::NameKey> {
2997    for &s in stmts_after {
2998        if subtree_contains(lang, s, |n| n == use_id) {
2999            let assign = match lang.kind(s) {
3000                NodeKind::Assign => s,
3001                NodeKind::ExprStmt => {
3002                    let ch = lang.children(s);
3003                    if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
3004                        ch[0]
3005                    } else {
3006                        return None;
3007                    }
3008                }
3009                _ => return None,
3010            };
3011            if lang.assign_op(assign).is_some() {
3012                return None;
3013            }
3014            let ch = lang.children(assign);
3015            if ch.len() == 2 && ch[1] == use_id && lang.kind(ch[0]) == NodeKind::VarRef {
3016                return lang.var_key(ch[0]);
3017            }
3018            return None;
3019        }
3020    }
3021    None
3022}
3023
3024/// 收集 `id` 子树中 VarRef 读到的全部名字键(去重,线性)。
3025/// 替代旧 reads_vars 的 HashSet 版:value 的读名字典型 ≤10,线性查重更快。
3026fn collect_read_keys<L: Lang>(lang: &L, id: L::Id, out: &mut Vec<L::NameKey>) {
3027    let mut stack = vec![id];
3028    while let Some(n) = stack.pop() {
3029        if lang.kind(n) == NodeKind::VarRef {
3030            if let Some(k) = lang.var_key(n) {
3031                if !out.contains(&k) {
3032                    out.push(k);
3033                }
3034            }
3035        }
3036        for &c in lang.children(n) {
3037            stack.push(c);
3038        }
3039    }
3040}
3041
3042
3043// ---------------------------------------------------------------------------
3044// 尾部 continue 删除(反编译器产物):
3045//   while (c) { …; continue; }  →  while (c) { … }
3046// 循环体最后一条无标签 continue 与顺序落入下一轮等价。恒安全。
3047// ---------------------------------------------------------------------------
3048
3049pub struct TrailingContinue;
3050
3051impl<L: Lang> Rule<L> for TrailingContinue {
3052    fn name(&self) -> &'static str {
3053        "trailing_continue"
3054    }
3055    fn kinds(&self) -> &'static [NodeKind] {
3056        &[NodeKind::While, NodeKind::For, NodeKind::DoWhile, NodeKind::ForEach]
3057    }
3058    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3059        let RewriteCtx { lang, walk: _ } = ctx;
3060        // While/For/DoWhile/ForEach 的 body 为 Block 且末语句为无标签 continue
3061        let body = match lang.kind(id) {
3062            NodeKind::While | NodeKind::For | NodeKind::DoWhile | NodeKind::ForEach => {
3063                *lang.children(id).last()?
3064            }
3065            _ => return None,
3066        };
3067        if lang.kind(body) != NodeKind::Block {
3068            return None;
3069        }
3070        let ch = lang.children(body).to_vec();
3071        let last = *ch.last()?;
3072        if lang.kind(last) != NodeKind::Continue || !lang.children(last).is_empty() {
3073            // JavaAST 的 Continue 标签在负载里,children 为空即无标签(引擎层约定)
3074            // 对带标签的语言实现:children[last] 非空即有标签 → 不动
3075            if lang.kind(last) != NodeKind::Continue {
3076                return None;
3077            }
3078        }
3079        Some(Edit::Delete { node: last })
3080    }
3081}
3082
3083// ---------------------------------------------------------------------------
3084// 多用途拷贝传播(寄存器副本消除):
3085//   x = y;(y 为简单 VarRef,x 有本块声明锚点,之后无对 x/y 的写、无遮蔽)
3086//   → 把 x 的**所有**后续读替换为 y,删除赋值。
3087// 守卫 y 不被写:替换后各使用点读的是 y 的当前值,须与原 x 的值一致。
3088// ---------------------------------------------------------------------------
3089
3090pub struct MultiUseCopyPropagation;
3091
3092impl<L: Lang> Rule<L> for MultiUseCopyPropagation {
3093    fn name(&self) -> &'static str {
3094        "multi_use_copy"
3095    }
3096    fn kinds(&self) -> &'static [NodeKind] {
3097        &[NodeKind::Assign, NodeKind::ExprStmt]
3098    }
3099    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3100        let RewriteCtx { lang, walk } = ctx;
3101        // 语句形态:Assign(x, VarRef y) / ExprStmt{Assign(...)}
3102        let (assign, stmt_node) = match lang.kind(id) {
3103            NodeKind::Assign => (id, id),
3104            NodeKind::ExprStmt => {
3105                let ch = lang.children(id);
3106                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
3107                    (ch[0], id)
3108                } else {
3109                    return None;
3110                }
3111            }
3112            _ => return None,
3113        };
3114        if lang.assign_op(assign).is_some() {
3115            return None;
3116        }
3117        let ach = lang.children(assign).to_vec();
3118        let (target, value) = (ach[0], ach[1]);
3119        if lang.kind(target) != NodeKind::VarRef || lang.kind(value) != NodeKind::VarRef {
3120            return None;
3121        }
3122        // lambda / 方法引用值:内联进 receiver 位非法(对抗波 2)→ 保守拒绝
3123        if matches!(lang.kind(value), NodeKind::Lambda | NodeKind::MethodRef) {
3124            return None;
3125        }
3126        if !lang.is_local_var(target) {
3127            return None;
3128        }
3129        let name = lang.var_name(target)?.to_string();
3130        let src = lang.var_name(value)?.to_string();
3131        if name == src {
3132            return None;
3133        }
3134
3135        let parent = walk.parent(stmt_node)?;
3136        if lang.kind(parent) != NodeKind::Block {
3137            return None;
3138        }
3139        let idx = walk.index(stmt_node)?;
3140        let stmts = lang.children(parent).to_vec();
3141
3142        // 锚点:x 在本块 idx 之前有 VarDecl
3143        let anchored = stmts[..idx].iter().any(|&s| {
3144            lang.kind(s) == NodeKind::VarDecl && lang.var_name(s) == Some(name.as_str())
3145        });
3146        if !anchored {
3147            return None;
3148        }
3149
3150        // 扫描后续:收集 x 的全部读;对 x 的任何写 / 对 y 的任何写 / 遮蔽 → 拒绝
3151        let name_key = lang.var_key(target)?;
3152        let src_key = lang.var_key(value)?;
3153        let mut w1 = Watch::<L>::new(name_key, &[name_key, src_key]);
3154        for &s in &stmts[idx + 1..] {
3155            scan_region(&*lang, s, &mut w1);
3156        }
3157        if w1.opaque || w1.shadowed || w1.uses.is_empty() {
3158            return None;
3159        }
3160        if w1.wrote(name_key) || w1.wrote(src_key) {
3161            return None;
3162        }
3163        // 句法守卫:x 在赋值后的句法出现总数(含数组写 base——x[i]=v
3164        // 的 x 不产生事件,事件模型盲区)必须等于事件 uses 数。
3165        // 出现 > uses ⇒ 存在事件看不见的引用(bd.java 差分二轮抓获:
3166        // var10004 = var154 的读被替换、var10004[i]= 写目标残留 →
3167        // 读写裂脑 → 类初始化 AIOOBE)
3168        let synth = count_name_refs(&*lang, &stmts[idx + 1..], name_key, &name);
3169        if synth != w1.uses.len() {
3170            return None;
3171        }
3172        // y 也不得在【赋值前】与 x 指向不同值(x=y 之前 y 已是其所值,无需检查);
3173        // 但 x=y 之间不能有对 y 的写(相邻语句,天然无中间)——赋值本身就是当前值 ✓
3174
3175        // VarRef 值(y)不得内联进 cast/instanceof 类型判定位:被替换
3176        // 变量(x)的声明类型是这些位置可转换性的依据——`Object x =
3177        // k3Var; s = (String) x;` 合法,替换成 `(String) k3Var` 引用类型
3178        // 不相关即不可编译(ddc weixin dt/k 复现)。任一使用点命中 →
3179        // 整体拒绝(多用途替换不可拆单点)。
3180        if w1.uses.iter().any(|&u| {
3181            typed_position(&*lang, walk, u).is_some_and(|t| !lang.var_cast_position_ok(value, t))
3182        }) {
3183            return None;
3184        }
3185
3186        let mut edits: Vec<Edit<L>> = w1.uses
3187            .iter()
3188            .map(|&u| Edit::Replace {
3189                target: u,
3190                with: value,
3191            })
3192            .collect();
3193        edits.push(Edit::Delete { node: stmt_node });
3194        Some(Edit::Multi(edits))
3195    }
3196}
3197
3198
3199// ---------------------------------------------------------------------------
3200// 尾部裸 return 删除(DAD/androguard 伪影):
3201//   void 方法/构造器体最后一条无值 return 与自然结束等价,删除。
3202// 仅匹配根块(方法体)末语句,避免动到块中间的提前 return。
3203// ---------------------------------------------------------------------------
3204
3205pub struct TrailingReturn;
3206
3207impl<L: Lang> Rule<L> for TrailingReturn {
3208    fn name(&self) -> &'static str {
3209        "trailing_return"
3210    }
3211    fn kinds(&self) -> &'static [NodeKind] {
3212        &[NodeKind::Return]
3213    }
3214    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3215        let root = ctx.root();
3216        let parent = ctx.parent(id)?;
3217        let lang = ctx.lang;
3218        if lang.kind(id) != NodeKind::Return || !lang.children(id).is_empty() {
3219            return None;
3220        }
3221        if parent != root || lang.kind(parent) != NodeKind::Block {
3222            return None;
3223        }
3224        if lang.children(parent).last() != Some(&id) {
3225            return None;
3226        }
3227        Some(Edit::Delete { node: id })
3228    }
3229}
3230
3231
3232// ---------------------------------------------------------------------------
3233// 三元-布尔运算归并(反混淆产物):
3234//   c ? a : false → c && a、c ? true : a → c || a(a 为布尔)
3235//   c 单次求值、a 条件求值,两种形态完全一致。
3236// ---------------------------------------------------------------------------
3237
3238pub struct TernaryBoolOp;
3239
3240impl<L: Lang> Rule<L> for TernaryBoolOp {
3241    fn name(&self) -> &'static str {
3242        "ternary_bool_op"
3243    }
3244    fn kinds(&self) -> &'static [NodeKind] {
3245        &[NodeKind::Ternary]
3246    }
3247    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3248        let RewriteCtx { lang, walk: _ } = ctx;
3249        if lang.kind(id) != NodeKind::Ternary {
3250            return None;
3251        }
3252        let ch = lang.children(id);
3253        let (c, a, b) = (*ch.first()?, *ch.get(1)?, *ch.get(2)?);
3254        // c 必须是语法级布尔:`1 ? true : false` 得 true,而 `1 || false`
3255        // 得 1(truthy 非布尔条件改变结果类型)。随机差分 case 2074 抓获。
3256        if !lang.is_bool(c) {
3257            return None;
3258        }
3259        let lb = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if v);
3260        let is_false = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if !v);
3261        let with = if is_false(b) && lang.is_bool(a) {
3262            lang.build_bin(BinOp::And, c, a)
3263        } else if lb(b) && lang.is_bool(a) {
3264            // c ? a : true ≡ !c || a —— 少见,跳过保持保守
3265            return None;
3266        } else if lb(a) && lang.is_bool(b) {
3267            // c ? true : b ≡ c || b
3268            lang.build_bin(BinOp::Or, c, b)
3269        } else if is_false(a) && lang.is_bool(b) {
3270            // c ? false : b ≡ !c && b —— 少见,跳过保持保守
3271            return None;
3272        } else {
3273            return None;
3274        };
3275        Some(Edit::Replace {
3276            target: id,
3277            with,
3278        })
3279    }
3280}
3281
3282
3283// ---------------------------------------------------------------------------
3284// 远距存储消除(寄存器预声明清理,ddc/jcdc 产物):
3285//   int x = 0; …(无 x 事件)…; x = 1;   →  int x = 1;(字面量提升)
3286//   int x = 0; …(无 x 事件)…; x = v;   →  int x;(init 死亡,剥除)
3287//   x = 0; …(无 x 事件)…; x = v;       →  删除第一条(值 ≤ MayRead)
3288// 守卫:首个 x 事件必须是写;语句内 x 出现若非简单赋值目标即视为读(阻断)。
3289// ---------------------------------------------------------------------------
3290
3291pub struct StoreKill;
3292
3293/// 扫描一个节点在 `name` 上的事件。
3294/// `dominating`:当前子树是否在必经路径上(If/While 的 cond、Ternary 的
3295/// cond、For 的 init/cond、非短路二元两侧 = true;分支体/循环体 = false)。
3296///
3297/// 语言侧若提供语句级预计算索引([`Lang::stmt_kill_event`]),调用方
3298/// 应优先查询;本函数是未索引节点的回退路径。
3299pub fn scan_kill_event<L: Lang>(
3300    lang: &L,
3301    node: L::Id,
3302    name: &str,
3303    dominating: bool,
3304) -> StmtKill<L::Id> {
3305    match lang.kind(node) {
3306        NodeKind::VarRef => {
3307            if lang.var_name(node) == Some(name) {
3308                StmtKill::Read
3309            } else {
3310                StmtKill::None
3311            }
3312        }
3313        NodeKind::VarDecl => {
3314            if lang.var_name(node) == Some(name) {
3315                StmtKill::Shadow
3316            } else {
3317                // 其他变量声明:init 必经求值
3318                for &c in lang.children(node) {
3319                    let e = scan_kill_event(lang, c, name, dominating);
3320                    if !matches!(e, StmtKill::None) {
3321                        return e;
3322                    }
3323                }
3324                StmtKill::None
3325            }
3326        }
3327        NodeKind::Assign => {
3328            let ch = lang.children(node);
3329            // RHS 先求值
3330            if let Some(&val) = ch.get(1) {
3331                let e = scan_kill_event(lang, val, name, dominating);
3332                if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3333                    return e;
3334                }
3335            }
3336            if lang.assign_op(node).is_none() {
3337                if let Some(&t) = ch.first() {
3338                    if lang.kind(t) == NodeKind::VarRef && lang.var_name(t) == Some(name) {
3339                        if dominating {
3340                            return StmtKill::DomWrite(node);
3341                        }
3342                        return StmtKill::None; // 条件写:不击杀,也不算读
3343                    }
3344                }
3345            }
3346            StmtKill::None
3347        }
3348        NodeKind::If | NodeKind::While | NodeKind::Ternary => {
3349            // cond 必经;分支体/循环体不必然——但其中的【读】仍保活 init,
3350            // 必须扫描(以非支配标记:读传播,写不击杀)
3351            let ch = lang.children(node);
3352            if let Some(&c) = ch.first() {
3353                let e = scan_kill_event(lang, c, name, true);
3354                if !matches!(e, StmtKill::None) {
3355                    return e;
3356                }
3357            }
3358            for &c in &ch[1..] {
3359                let e = scan_kill_event(lang, c, name, false);
3360                if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3361                    return e;
3362                }
3363            }
3364            StmtKill::None
3365        }
3366        NodeKind::Binary => {
3367            let Some(op) = lang.bin_op(node) else {
3368                return StmtKill::None;
3369            };
3370            let ch = lang.children(node);
3371            if op.is_short_circuit() {
3372                // 左支必经;右支条件求值(读→保活;写→不击杀)
3373                if let Some(&l) = ch.first() {
3374                    let e = scan_kill_event(lang, l, name, true);
3375                    if !matches!(e, StmtKill::None) {
3376                        return e;
3377                    }
3378                }
3379                if let Some(&r) = ch.get(1) {
3380                    let e = scan_kill_event(lang, r, name, false);
3381                    if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3382                        return e;
3383                    }
3384                }
3385                StmtKill::None
3386            } else {
3387                for &c in ch {
3388                    let e = scan_kill_event(lang, c, name, dominating);
3389                    if !matches!(e, StmtKill::None) {
3390                        return e;
3391                    }
3392                }
3393                StmtKill::None
3394            }
3395        }
3396        NodeKind::For | NodeKind::ForEach | NodeKind::Switch | NodeKind::Try => {
3397            // 必经部分(For 的 inits+cond / ForEach 的 iterable / Switch 的
3398            // subject)以支配标记扫;其余(循环体/case 体/try 块)以非支配
3399            // 标记扫——其中的读仍保活
3400            let ch = lang.children(node);
3401            let dom_len = match lang.kind(node) {
3402                NodeKind::For => ch.len().saturating_sub(2),
3403                NodeKind::ForEach | NodeKind::Switch => 1,
3404                _ => 0,
3405            };
3406            for &c in &ch[..dom_len] {
3407                let e = scan_kill_event(lang, c, name, true);
3408                if !matches!(e, StmtKill::None) {
3409                    return e;
3410                }
3411            }
3412            for &c in &ch[dom_len..] {
3413                let e = scan_kill_event(lang, c, name, false);
3414                if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3415                    return e;
3416                }
3417            }
3418            StmtKill::None
3419        }
3420        _ => {
3421            for &c in lang.children(node) {
3422                let e = scan_kill_event(lang, c, name, dominating);
3423                if !matches!(e, StmtKill::None) {
3424                    return e;
3425                }
3426            }
3427            StmtKill::None
3428        }
3429    }
3430}
3431
3432/// One DFS of a statement computing the dominating=true kill event for
3433/// every name that has one. Recursion order matches [`scan_kill_event`];
3434/// the first event per name wins. Used by language crates to build a
3435/// per-statement index so StoreKill does not rescan subtrees per candidate.
3436pub fn collect_stmt_kill_events<L: Lang>(lang: &L, root: L::Id) -> Vec<(L::NameKey, StmtKill<L::Id>)> {
3437    use std::collections::BTreeMap;
3438    enum Act<Id> {
3439        Enter(Id, bool),
3440        AfterAssign(Id, bool),
3441    }
3442    let mut events: BTreeMap<L::NameKey, StmtKill<L::Id>> = BTreeMap::new();
3443    let mut stack = vec![Act::Enter(root, true)];
3444    while let Some(act) = stack.pop() {
3445        match act {
3446            Act::AfterAssign(id, dominating) => {
3447                if lang.assign_op(id).is_some() {
3448                    continue;
3449                }
3450                let Some(&target) = lang.children(id).first() else {
3451                    continue;
3452                };
3453                if lang.kind(target) != NodeKind::VarRef {
3454                    continue;
3455                }
3456                let Some(key) = lang.var_key(target) else {
3457                    continue;
3458                };
3459                if dominating {
3460                    events.entry(key).or_insert(StmtKill::DomWrite(id));
3461                }
3462            }
3463            Act::Enter(node, dominating) => match lang.kind(node) {
3464                NodeKind::VarRef => {
3465                    if let Some(key) = lang.var_key(node) {
3466                        events.entry(key).or_insert(StmtKill::Read);
3467                    }
3468                }
3469                NodeKind::VarDecl => {
3470                    if let Some(key) = lang.var_key(node) {
3471                        events.entry(key).or_insert(StmtKill::Shadow);
3472                    }
3473                    for &c in lang.children(node).iter().rev() {
3474                        stack.push(Act::Enter(c, dominating));
3475                    }
3476                }
3477                NodeKind::Assign => {
3478                    let ch = lang.children(node);
3479                    stack.push(Act::AfterAssign(node, dominating));
3480                    if let Some(&val) = ch.get(1) {
3481                        stack.push(Act::Enter(val, dominating));
3482                    }
3483                }
3484                NodeKind::If | NodeKind::While | NodeKind::Ternary => {
3485                    let ch = lang.children(node);
3486                    for (i, &c) in ch.iter().enumerate().rev() {
3487                        stack.push(Act::Enter(c, i == 0));
3488                    }
3489                }
3490                NodeKind::Binary => {
3491                    let ch = lang.children(node);
3492                    let short = lang.bin_op(node).is_some_and(|op| op.is_short_circuit());
3493                    for (i, &c) in ch.iter().enumerate().rev() {
3494                        let child_dom = if short { i == 0 } else { dominating };
3495                        stack.push(Act::Enter(c, child_dom));
3496                    }
3497                }
3498                NodeKind::For | NodeKind::ForEach | NodeKind::Switch | NodeKind::Try => {
3499                    let ch = lang.children(node);
3500                    let dom_len = match lang.kind(node) {
3501                        NodeKind::For => ch.len().saturating_sub(2),
3502                        NodeKind::ForEach | NodeKind::Switch => 1,
3503                        _ => 0,
3504                    };
3505                    for (i, &c) in ch.iter().enumerate().rev() {
3506                        stack.push(Act::Enter(c, i < dom_len));
3507                    }
3508                }
3509                _ => {
3510                    for &c in lang.children(node).iter().rev() {
3511                        stack.push(Act::Enter(c, dominating));
3512                    }
3513                }
3514            },
3515        }
3516    }
3517    events.into_iter().collect()
3518}
3519
3520impl<L: Lang> Rule<L> for StoreKill {
3521    fn name(&self) -> &'static str {
3522        "store_kill"
3523    }
3524    fn kinds(&self) -> &'static [NodeKind] {
3525        &[NodeKind::VarDecl, NodeKind::Assign, NodeKind::ExprStmt]
3526    }
3527    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3528        let RewriteCtx { lang, walk } = ctx;
3529        // 形态:VarDecl(带 init) 或 Assign(裸/ExprStmt 包裹)
3530        let (is_decl, stmt_node, first_value) = match lang.kind(id) {
3531            NodeKind::VarDecl => {
3532                if lang.children(id).is_empty() {
3533                    return None;
3534                }
3535                (true, id, *lang.children(id).first()?)
3536            }
3537            NodeKind::Assign => {
3538                if lang.assign_op(id).is_some() {
3539                    return None;
3540                }
3541                (false, id, *lang.children(id).get(1)?)
3542            }
3543            NodeKind::ExprStmt => {
3544                let ch = lang.children(id);
3545                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
3546                    let a = ch[0];
3547                    if lang.assign_op(a).is_some() {
3548                        return None;
3549                    }
3550                    (false, id, *lang.children(a).get(1)?)
3551                } else {
3552                    return None;
3553                }
3554            }
3555            _ => return None,
3556        };
3557        let (name, name_key) = if is_decl {
3558            (lang.var_name(id)?.to_string(), lang.var_key(id)?)
3559        } else {
3560            let a = match lang.kind(id) {
3561                NodeKind::Assign => id,
3562                _ => lang.children(id)[0],
3563            };
3564            let t = *lang.children(a).first()?;
3565            if lang.kind(t) != NodeKind::VarRef || !lang.is_local_var(t) {
3566                return None;
3567            }
3568            (lang.var_name(t)?.to_string(), lang.var_key(t)?)
3569        };
3570
3571        let parent = walk.parent(stmt_node)?;
3572        if lang.kind(parent) != NodeKind::Block {
3573            return None;
3574        }
3575        let idx = walk.index(stmt_node)?;
3576        let stmts = lang.children(parent).to_vec();
3577
3578        // 扫描:读(任意路径)→ 保活;支配写 → 击杀;条件写 → 继续
3579        let mut killed_by: Option<(usize, L::Id)> = None;
3580        for (si, &s) in stmts.iter().enumerate().skip(idx + 1) {
3581            // 原文/类体等不可见区域可能读本变量——击杀不得跨越
3582            //(Test262 TypedArray:class 构造器读 flag,首个 store 被杀,
3583            // flag 变成声明即 true)
3584            if lang.stmt_has_opaque_region(s) {
3585                return None;
3586            }
3587            let event = lang
3588                .stmt_kill_event(s, name_key)
3589                .unwrap_or_else(|| scan_kill_event(&*lang, s, &name, true));
3590            match event {
3591                StmtKill::None => continue,
3592                StmtKill::Read | StmtKill::Shadow => return None,
3593                StmtKill::DomWrite(assign) => {
3594                    killed_by = Some((si, assign));
3595                    break;
3596                }
3597            }
3598        }
3599        let (si, kill_assign) = killed_by?;
3600        // 击杀语句级守卫:击杀赋值必须就是**顶层语句本身**(或其
3601        // ExprStmt 包裹)。嵌套击杀(do-while 体/裸块/for-init 里的写)
3602        // 曾按 stmts[si] 删整条外层语句——连兄弟副作用一起删
3603        //(RealTimeSequencer.pump() 的 133 行 do-while 曾被整删成
3604        // 3 行;ConstantPool.writeTags 双 for+switch 全删——JDK 语料
3605        // ≥9 文件)。同语句内击杀点之后的读也无法用语句级窗口证明
3606        // 不可达 → 保守拒绝嵌套击杀(顶层击杀不受影响)
3607        {
3608            let kill_container = match walk.parents.get(&kill_assign) {
3609                Some(&(p, _)) if lang.kind(p) == NodeKind::ExprStmt => p,
3610                _ => kill_assign,
3611            };
3612            if kill_container != stmts[si] {
3613                return None;
3614            }
3615        }
3616        // 异常窗口守卫:被击杀点到击杀点之间的语句若可能抛(效果 >
3617        // MayRead),异常路径会绕过击杀点直达 finally/catch——后者可能
3618        // 在「中间值」状态读到本变量(finally-flag 恢复模式:
3619        // v=true; setSoTimeout(1); v=false; finally{if(v)…}——差分
3620        // 抓获:v=true 曾被杀,异常路径的恢复逻辑变死代码)。
3621        //
3622        // 精化:异常只可能被**包围本块的 Try** 的 handler 截获;无包围
3623        // Try 时异常直接出方法,局部变量对外不可见——中间值无人可观
3624        // 察,窗口内的不纯语句不构成障碍(ddc 0.1.33 形态:声明序列 →
3625        // new 对象 → 击杀赋值,构造调用恰在窗口内)。窗口内嵌套 try
3626        // 的 handler 读已由 per-statement 事件扫描以 Read 击退,不在
3627        // 此列。
3628        let mut has_try_ancestor = false;
3629        {
3630            let mut cur = stmt_node;
3631            while let Some(&(p, _)) = walk.parents.get(&cur) {
3632                if lang.kind(p) == NodeKind::Try {
3633                    has_try_ancestor = true;
3634                    break;
3635                }
3636                cur = p;
3637            }
3638        }
3639        if has_try_ancestor {
3640            for &s in &stmts[idx + 1..si] {
3641                if lang.effect(s) > Effect::MayRead {
3642                    return None;
3643                }
3644            }
3645        }
3646
3647        if is_decl {
3648            // 击杀赋值的值是字面量 → 提升进声明 init
3649            // (原 init 被替换丢弃——同样必须可丢弃,副作用调用不得删!)
3650            if lang.effect(first_value) > Effect::MayRead {
3651                return None;
3652            }
3653            let value = *lang.children(kill_assign).get(1)?;
3654            if is_literal(lang, value) {
3655                let assign_stmt = stmts[si];
3656                return Some(Edit::Multi(vec![
3657                    Edit::Splice {
3658                        node: id,
3659                        index: 0,
3660                        remove: 1,
3661                        insert: vec![value],
3662                    },
3663                    Edit::Delete { node: assign_stmt },
3664                ]));
3665            }
3666            // 否则:剥除 init(裸声明)——init 必须可丢弃(副作用调用不得删!)。
3667            // 推断类型声明(Java `var`)剥 init 后非法——拒绝
3668            //(obf_fuzz 差分 seed 4 抓获:`var flag;` 编译失败)
3669            if lang.is_inferred_decl(id) {
3670                return None;
3671            }
3672            if lang.effect(first_value) > Effect::MayRead {
3673                return None;
3674            }
3675            return Some(Edit::Splice {
3676                node: id,
3677                index: 0,
3678                remove: 1,
3679                insert: Vec::new(),
3680            });
3681        }
3682        // 赋值形态:首值必须可丢弃
3683        if lang.effect(first_value) > Effect::MayRead {
3684            return None;
3685        }
3686        Some(Edit::Delete { node: stmt_node })
3687    }
3688}
3689
3690/// value 是否为字面量(提升安全:无求值位置问题)。
3691fn is_literal<L: Lang>(lang: &L, n: L::Id) -> bool {
3692    matches!(lang.kind(n), NodeKind::Literal)
3693}
3694
3695// ---------------------------------------------------------------------------
3696// 注册表
3697// ---------------------------------------------------------------------------
3698
3699/// 默认规则(保守集:不做 DCE 类清理)。
3700pub fn default_rules<L: Lang>() -> Vec<Box<dyn Rule<L>>> {
3701    vec![
3702        Box::new(DoubleNegFold),
3703        Box::new(BlockFlatten),
3704        Box::new(ParenRemoval),
3705        Box::new(ConstCondition),
3706        Box::new(BooleanReturn),
3707        Box::new(IfToTernary),
3708        Box::new(IfAssignTernary),
3709        Box::new(IfElseEmpty),
3710        Box::new(BoolCompare),
3711        Box::new(DoubleNot),
3712        Box::new(BoolNotFold),
3713        Box::new(BoolShortCircuit),
3714        Box::new(NotCompare),
3715        Box::new(TernaryFold),
3716        Box::new(TernaryBool),
3717        Box::new(TernaryBoolOp),
3718        Box::new(ConstFoldBin),
3719        Box::new(CmpConstFold),
3720        Box::new(BitIdentity),
3721        Box::new(ArithReassoc),
3722        Box::new(SelfAssign),
3723        Box::new(ArithIdentity),
3724        Box::new(ArithZero),
3725        Box::new(DeclAssignMerge),
3726        Box::new(LocalPropagation),
3727        Box::new(AssignPropagation),
3728        Box::new(MultiUseCopyPropagation),
3729        Box::new(TrailingReturn),
3730        Box::new(InverseAssignPair),
3731        Box::new(DeadDecl),
3732        Box::new(AssignBackFold),
3733        Box::new(DeadStore),
3734        Box::new(StoreKill),
3735    ]
3736}
3737
3738/// 全量规则(含 DCE 类选配项;按设计文档"可选配,默认不启用")。
3739pub fn all_rules<L: Lang>() -> Vec<Box<dyn Rule<L>>> {
3740    let mut rules = default_rules();
3741    rules.push(Box::new(UnreachableAfterTerminal));
3742    rules
3743}