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                    if lang.assign_op(node).is_some() {
1423                        scan_region(lang, t, w);
1424                    }
1425                } else {
1426                    // 成员/索引目标:基座/键是读
1427                    scan_region(lang, t, w);
1428                }
1429            }
1430            if let Some(&v) = ch.get(1) {
1431                scan_region(lang, v, w);
1432            }
1433        }
1434        NodeKind::Unary if lang.un_op(node).is_some_and(|o| o.is_incdec()) => {
1435            let ch = lang.children(node);
1436            if let Some(&t) = ch.first() {
1437                if lang.kind(t) == NodeKind::VarRef {
1438                    if let Some(k) = lang.var_key(t) {
1439                        for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1440                            if *wn == k {
1441                                *hit = true;
1442                            }
1443                        }
1444                    }
1445                    // ++/-- 也读旧值
1446                    scan_region(lang, t, w);
1447                } else {
1448                    scan_region(lang, t, w);
1449                }
1450            }
1451        }
1452        NodeKind::VarDecl => {
1453            if let Some(k) = lang.var_key(node) {
1454                if k == w.name {
1455                    w.shadowed = true;
1456                }
1457                for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1458                    if *wn == k {
1459                        *hit = true;
1460                    }
1461                }
1462            }
1463            for &c in lang.children(node) {
1464                scan_region(lang, c, w);
1465            }
1466        }
1467        // ForEach 循环变量 / Catch 绑定:在子作用域声明了同名变量 → 视为遮蔽;
1468        // ForEach 每轮给循环变量赋值 → 也算显式写。
1469        NodeKind::ForEach | NodeKind::Catch => {
1470            if let Some(k) = lang.var_key(node) {
1471                if k == w.name {
1472                    w.shadowed = true;
1473                }
1474                if lang.kind(node) == NodeKind::ForEach {
1475                    for (wn, hit) in w.watch.iter().zip(w.writes_hit.iter_mut()) {
1476                        if *wn == k {
1477                            *hit = true;
1478                        }
1479                    }
1480                }
1481            }
1482            for &c in lang.children(node) {
1483                scan_region(lang, c, w);
1484            }
1485        }
1486        NodeKind::VarRef => {
1487            if lang.var_key(node) == Some(w.name) {
1488                w.uses.push(node);
1489            }
1490        }
1491        NodeKind::Raw => {
1492            // 不可解析原文:读/写集未知
1493            w.opaque = true;
1494        }
1495        _ => {
1496            for &c in lang.children(node) {
1497                scan_region(lang, c, w);
1498            }
1499        }
1500    }
1501}
1502
1503/// root → target 的路径上不得有条件求值结构(Ternary / If / 短路右支)。
1504fn straight_path<L: Lang>(lang: &L, root: L::Id, target: L::Id) -> bool {
1505    fn go<L: Lang>(lang: &L, node: L::Id, target: L::Id) -> bool {
1506        if node == target {
1507            return true;
1508        }
1509        match lang.kind(node) {
1510            NodeKind::Ternary | NodeKind::If => return false,
1511            NodeKind::Binary if lang.bin_op(node).is_some_and(|o| o.is_short_circuit()) => {
1512                return false
1513            }
1514            _ => {}
1515        }
1516        for &c in lang.children(node) {
1517            if subtree_contains(lang, c, |n| n == target) {
1518                return go(lang, c, target);
1519            }
1520        }
1521        false
1522    }
1523    go(lang, root, target)
1524}
1525
1526
1527// ---------------------------------------------------------------------------
1528// 短路折叠:
1529//   false && x → false / true || x → true(x 原本就不求值,恒安全)
1530//   true && x → x / false || x → x / x && true → x / x || false → x(x 保留,恒安全)
1531//   x && false → false / x || true → true(x 的求值被丢弃,需 effect ≤ MayRead)
1532// ---------------------------------------------------------------------------
1533
1534/// id 是否位于标签模板 tag 位(Custom(Template) 的 child 0)。
1535fn in_tag_position<L: Lang>(lang: &L, walk: &crate::walk::Walk<L>, id: L::Id) -> bool {
1536    let Some((p, i)) = walk.parents.get(&id) else { return false };
1537    *i == 0
1538        && lang.kind(*p) == NodeKind::Custom(1) // custom::TEMPLATE
1539        && lang.is_tagged_template(*p)
1540}
1541
1542pub struct BoolShortCircuit;
1543
1544impl<L: Lang> Rule<L> for BoolShortCircuit {
1545    fn name(&self) -> &'static str {
1546        "bool_short_circuit"
1547    }
1548    fn kinds(&self) -> &'static [NodeKind] {
1549        &[NodeKind::Binary]
1550    }
1551    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1552        let RewriteCtx { lang, walk } = ctx;
1553        if lang.kind(id) != NodeKind::Binary {
1554            return None;
1555        }
1556        let op = lang.bin_op(id)?;
1557        if !op.is_short_circuit() {
1558            return None;
1559        }
1560        // `delete (a && b)` 的 delete 语义是「非引用 → true」,折叠成
1561        // `delete b` 会真的尝试删除(返回值改变);
1562        // 标签位折臂会改变 tag 的 Reference/this 绑定。
1563        if in_tag_position(&*lang, &walk, id) {
1564            return None;
1565        }
1566        if let Some((p, i)) = walk.parents.get(&id) {
1567            if *i == 0 && lang.kind(*p) == NodeKind::Unary && lang.un_op(*p) == Some(UnOp::Delete) {
1568                return None;
1569            }
1570        }
1571        let ch = lang.children(id);
1572        let (l, r) = (*ch.first()?, *ch.get(1)?);
1573        let is_lit = |n: L::Id, v: bool| matches!(lang.literal(n), Some(LitRef::Bool(b)) if b == v);
1574        // 左侧字面量:短路后 x 可能根本不求值 → 直接取字面量结果
1575        let with = match (op, is_lit(l, true), is_lit(l, false)) {
1576            (BinOp::And, _, true) => l,  // false && x → false(x 不求值)
1577            (BinOp::Or, true, _) => l,   // true || x → true(x 不求值)
1578            (BinOp::And, true, _) => r,  // true && x → x
1579            (BinOp::Or, _, true) => r,   // false || x → x
1580            _ => {
1581                // 右侧字面量:x && true → x / x || false → x(x 保留求值,恒安全);
1582                // x && false → false / x || true → true(x 的求值被丢弃,需可丢弃)
1583                // x && true → x / x || false → x **仅当 x 是布尔值**:
1584                // JS 里 `sym && true` 对 truthy 的非布尔 x 返回 true 而不是
1585                // x(Test262 logical-and/symbol 抓获;Java 类型系统恒真)。
1586                match (op, is_lit(r, true), is_lit(r, false)) {
1587                    (BinOp::And, true, _) if lang.is_bool(l) => l,
1588                    (BinOp::Or, _, true) if lang.is_bool(l) => l,
1589                    // x && false → false / x || true → true **仅当 x 布尔**:
1590                    // JS 里 truthy/falsy 非布尔 x 返回 x(`null && false`
1591                    // === null),且未解析引用求值会抛 ReferenceError
1592                    //(Test262 logical 家族抓获)
1593                    (BinOp::And, _, true) | (BinOp::Or, true, _)
1594                        if lang.is_bool(l) && lang.effect(l) <= Effect::MayRead => {
1595                            r
1596                        }
1597                    _ => return None,
1598                }
1599            }
1600        };
1601        Some(Edit::Replace {
1602            target: id,
1603            with,
1604        })
1605    }
1606}
1607
1608// ---------------------------------------------------------------------------
1609// 比较取反:!(a == b) → a != b(恒安全,含 NaN/引用);
1610// !(a < b) → a >= b 等次序比较仅精确整数(浮点 NaN 下不成立)。
1611// ---------------------------------------------------------------------------
1612
1613pub struct NotCompare;
1614
1615impl<L: Lang> Rule<L> for NotCompare {
1616    fn name(&self) -> &'static str {
1617        "not_compare"
1618    }
1619    fn kinds(&self) -> &'static [NodeKind] {
1620        &[NodeKind::Unary]
1621    }
1622    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1623        let RewriteCtx { lang, walk: _ } = ctx;
1624        if lang.kind(id) != NodeKind::Unary || lang.un_op(id) != Some(UnOp::Not) {
1625            return None;
1626        }
1627        let inner = *lang.children(id).first()?;
1628        if lang.kind(inner) != NodeKind::Binary {
1629            return None;
1630        }
1631        let op = lang.bin_op(inner)?;
1632        let neg = match op {
1633            BinOp::Eq => BinOp::Ne,
1634            BinOp::Ne => BinOp::Eq,
1635            BinOp::Lt => BinOp::Ge,
1636            BinOp::Le => BinOp::Gt,
1637            BinOp::Gt => BinOp::Le,
1638            BinOp::Ge => BinOp::Lt,
1639            _ => return None,
1640        };
1641        if op.is_short_circuit() {
1642            return None;
1643        }
1644        // 次序比较的取反仅对精确整数成立(NaN: !(a<b)=true 但 a>=b=false)
1645        if matches!(op, BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
1646            let ch = lang.children(inner);
1647            if !lang.is_exact_int(*ch.first()?) || !lang.is_exact_int(*ch.get(1)?) {
1648                return None;
1649            }
1650        }
1651        let ch = lang.children(inner).to_vec();
1652        let rebuilt = lang.build_bin(neg, ch[0], ch[1]);
1653        Some(Edit::Replace {
1654            target: id,
1655            with: rebuilt,
1656        })
1657    }
1658}
1659
1660// ---------------------------------------------------------------------------
1661// 三元折叠:true ? a : b → a;false ? a : b → b(另一支原本不求值,恒安全);
1662//           c ? a : a → a(c 的求值被丢弃,需 effect ≤ MayRead)
1663// ---------------------------------------------------------------------------
1664
1665/// 折臂敏感位:从 `id` 向上穿过 Paren/Ternary(类型透明链),最终落在
1666/// **推断声明**(Java 10 `var`)的 init 上 → true。var 的静态类型由整个
1667/// init 表达式推断(三元 = lub(两臂))——折臂改类型。其余容器(实参/
1668/// 二元操作数/Cast/λ 体)的静态类型由外层结构决定,内层折臂不影响推断。
1669fn in_inferred_var_init<L: Lang>(lang: &L, walk: &Walk<L>, mut id: L::Id) -> bool {
1670    while let Some(&(p, _)) = walk.parents.get(&id) {
1671        match lang.kind(p) {
1672            NodeKind::Paren | NodeKind::Ternary => id = p,
1673            NodeKind::VarDecl => return lang.is_inferred_decl(p),
1674            _ => return false,
1675        }
1676    }
1677    false
1678}
1679
1680pub struct TernaryFold;
1681
1682impl<L: Lang> Rule<L> for TernaryFold {
1683    fn name(&self) -> &'static str {
1684        "ternary_fold"
1685    }
1686    fn kinds(&self) -> &'static [NodeKind] {
1687        &[NodeKind::Ternary]
1688    }
1689    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1690        let RewriteCtx { lang, walk } = ctx;
1691        if lang.kind(id) != NodeKind::Ternary {
1692            return None;
1693        }
1694        if in_tag_position(&*lang, &walk, id) {
1695            return None;
1696        }
1697        let ch = lang.children(id);
1698        let (c, a, b) = (*ch.first()?, *ch.get(1)?, *ch.get(2)?);
1699        match lang.literal(c) {
1700            // R15 P0-1(ClassNotFoundExceptionDueToPrunedCodeTest o1/o2)
1701            // 抓获:`var o1 = false ? new Object(){} : null` 曾折成
1702            // `var o1 = null`——var 推断从匿名类类型变 null 类型,
1703            // `o1 == o1` 不可编译。推断声明 init 位(类型透明链上)
1704            // 拒绝折臂。
1705            Some(LitRef::Bool(true)) => {
1706                if !in_inferred_var_init(&*lang, walk, id) {
1707                    return Some(Edit::Replace { target: id, with: a });
1708                }
1709            }
1710            Some(LitRef::Bool(false))
1711                if !in_inferred_var_init(&*lang, walk, id) => {
1712                    return Some(Edit::Replace { target: id, with: b });
1713                }
1714            _ => {}
1715        }
1716        // c ? a : a:两臂结构全同 → 折叠不改变静态类型(lub(a,a)=a),
1717        // var init 位同样安全
1718        if crate::analysis::structurally_equal(&*lang, a, b) && lang.effect(c) <= Effect::MayRead {
1719            return Some(Edit::Replace { target: id, with: a });
1720        }
1721        None
1722    }
1723}
1724
1725// ---------------------------------------------------------------------------
1726// 三元布尔:c ? true : false → c;c ? false : true → !c(c 为原始 boolean)
1727// ---------------------------------------------------------------------------
1728
1729pub struct TernaryBool;
1730
1731impl<L: Lang> Rule<L> for TernaryBool {
1732    fn name(&self) -> &'static str {
1733        "ternary_bool"
1734    }
1735    fn kinds(&self) -> &'static [NodeKind] {
1736        &[NodeKind::Ternary]
1737    }
1738    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1739        let RewriteCtx { lang, walk: _ } = ctx;
1740        if lang.kind(id) != NodeKind::Ternary {
1741            return None;
1742        }
1743        let ch = lang.children(id);
1744        let (c, a, b) = (*ch.first()?, *ch.get(1)?, *ch.get(2)?);
1745        let lb = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if v);
1746        let is_false = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if !v);
1747        if !lang.is_bool(c) {
1748            return None;
1749        }
1750        let with = if lb(a) && is_false(b) {
1751            c
1752        } else if is_false(a) && lb(b) {
1753            lang.build_unary(UnOp::Not, c)
1754        } else {
1755            return None;
1756        };
1757        Some(Edit::Replace {
1758            target: id,
1759            with,
1760        })
1761    }
1762}
1763
1764// ---------------------------------------------------------------------------
1765// 整数常量折叠 + 字符串拼接:形态匹配在引擎(双字面量 + 非短路/比较
1766// 算符),数值/拼接**语义**全权在 Lang::fold_lit_bin(Java:JLS i32/i64
1767// 环绕 + 移位掩码 + 除零不折 + "a"+1 拼接;其他语言各自定义)。
1768// ---------------------------------------------------------------------------
1769
1770pub struct ConstFoldBin;
1771
1772impl<L: Lang> Rule<L> for ConstFoldBin {
1773    fn name(&self) -> &'static str {
1774        "const_fold_bin"
1775    }
1776    fn kinds(&self) -> &'static [NodeKind] {
1777        &[NodeKind::Binary]
1778    }
1779    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1780        let RewriteCtx { lang, walk: _ } = ctx;
1781        if lang.kind(id) != NodeKind::Binary {
1782            return None;
1783        }
1784        let op = lang.bin_op(id)?;
1785        if op.is_short_circuit() || op.is_comparison() {
1786            return None;
1787        }
1788        let ch = lang.children(id);
1789        let (l, r) = (*ch.first()?, *ch.get(1)?);
1790        // 双字面量才考虑折叠(具体哪些组合可折由语言语义决定)
1791        lang.literal(l)?;
1792        lang.literal(r)?;
1793        let with = lang.fold_lit_bin(op, l, r)?;
1794        Some(Edit::Replace {
1795            target: id,
1796            with,
1797        })
1798    }
1799}
1800
1801// ---------------------------------------------------------------------------
1802// 零元素 / 平凡算术:
1803//   0 * x → 0、x * 0 → 0(x 求值被丢弃:需 effect ≤ MayRead 且精确整数——
1804//     浮点 0.0*NaN=NaN)
1805//   x - x → 0(同名局部整型变量)
1806//   x << 0 / x >> 0 / x >>> 0 → x(位移 0 恒等,x 保留,恒安全)
1807// ---------------------------------------------------------------------------
1808
1809pub struct ArithZero;
1810
1811impl<L: Lang> Rule<L> for ArithZero {
1812    fn name(&self) -> &'static str {
1813        "arith_zero"
1814    }
1815    fn kinds(&self) -> &'static [NodeKind] {
1816        &[NodeKind::Binary]
1817    }
1818    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1819        let RewriteCtx { lang, walk: _ } = ctx;
1820        if lang.kind(id) != NodeKind::Binary {
1821            return None;
1822        }
1823        let op = lang.bin_op(id)?;
1824        let ch = lang.children(id);
1825        let (l, r) = (*ch.first()?, *ch.get(1)?);
1826        let int_lit = |n: L::Id, v: i64| lang.literal(n).and_then(|x| x.as_int()) == Some(v);
1827
1828        // 移位 0:x << 0 → x(恒等)。**需精确整数域**:JS 的移位是
1829        // ToInt32 胁迫——`-4294967295 << 0 === 1` 而非原值(Test262
1830        // left-shift 族抓获)。Java 的 int/long 解析为真 → 中性。
1831        if matches!(op, BinOp::Shl | BinOp::Shr | BinOp::UShr) && int_lit(r, 0) {
1832            if !lang.is_exact_int(l) {
1833                return None;
1834            }
1835            return Some(Edit::Replace {
1836                target: id,
1837                with: l,
1838            });
1839        }
1840        // 0 * x → 0 / x * 0 → 0
1841        if op == BinOp::Mul {
1842            let (zero, other) = if int_lit(l, 0) {
1843                (l, r)
1844            } else if int_lit(r, 0) {
1845                (r, l)
1846            } else {
1847                return None;
1848            };
1849            if !lang.is_exact_int(other) || lang.effect(other) > Effect::MayRead {
1850                return None;
1851            }
1852            return Some(Edit::Replace {
1853                target: id,
1854                with: zero,
1855            });
1856        }
1857        // x - x → 0(同名局部变量,整型)
1858        if op == BinOp::Sub
1859            && lang.kind(l) == NodeKind::VarRef
1860            && lang.kind(r) == NodeKind::VarRef
1861            && lang.same_binding(l, r)
1862            && lang.is_local_var(l)
1863            && lang.is_exact_int(l)
1864        {
1865            let with = lang.build_int(0, false);
1866            return Some(Edit::Replace {
1867                target: id,
1868                with,
1869            });
1870        }
1871        None
1872    }
1873}
1874
1875// ---------------------------------------------------------------------------
1876// 死赋值:x = a; x = b; → x = b;(a 的求值被丢弃:局部变量 x + effect(a) ≤ MayRead)
1877// 含声明形:int x = a; x = b; → int x = b;
1878// ---------------------------------------------------------------------------
1879
1880
1881// ---------------------------------------------------------------------------
1882// 逆运算对抵消(寄存器噪声):`x op= K;` 紧跟 `x op⁻¹= K;`(同局部变量、
1883// 同字面量常量、相邻语句、精确整型)→ 两条全删。
1884//   `x += K; x -= K` → 删(模 2^w 加法群恒等)
1885//   `x ^= K; x ^= K` → 删(XOR 自逆)
1886//   `x -= K; x += K` → 删(对称)
1887// 展开形态 `x = x + K; x = x - K`(RHS = Binary(x op K))与混合形态
1888// (`x += K; x = x - K`)同样处理——反编译器常输出展开形态。
1889// 相邻 ⇒ 两句之间无任何求值 ⇒ 删除恒安全(x 终值不变;x 的旧值
1890// 不被其他求值观察到)。
1891// 守卫:x 为局部变量(is_local_var——字段写有可见副作用);K 为
1892// 无副作用字面量;x 精确整型(浮点 (x+K)-K 在溢出/Inf 上 ≠ x)。
1893// 真实样本:PMD 语料 PMDTaskTestExample.java(a+=1; a-=1 交替 50 次
1894// 完全不动——寄存器噪声经典形态,109 行全为噪声)。
1895// ---------------------------------------------------------------------------
1896
1897pub struct InverseAssignPair;
1898
1899impl<L: Lang> Rule<L> for InverseAssignPair {
1900    fn name(&self) -> &'static str {
1901        "inverse_assign_pair"
1902    }
1903    fn kinds(&self) -> &'static [NodeKind] {
1904        &[NodeKind::Block]
1905    }
1906    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
1907        let RewriteCtx { lang, .. } = ctx;
1908        let ch = lang.children(id).to_vec();
1909        if ch.len() < 2 {
1910            return None;
1911        }
1912        for w in 0..ch.len() - 1 {
1913            let (s1, s2) = (ch[w], ch[w + 1]);
1914            // 窗口左端不是赋值语句 → 滑到下一窗口(不能整check放弃)
1915            let Some(a1) = norm_assign(lang, s1) else { continue };
1916            let Some(a2) = norm_assign(lang, s2) else { continue };
1917            // 同一局部变量(同一绑定;名字键仅作快速预筛)
1918            if a1.key != a2.key
1919                || !lang.same_binding(a1.target, a2.target)
1920                || !a1.local
1921                || !a2.local
1922            {
1923                continue;
1924            }
1925            // 精确整型(模 2^w 群恒等性成立;浮点舍入破坏交换律)
1926            if !a1.exact_int || !a2.exact_int {
1927                continue;
1928            }
1929            // 同一常量 + 互逆算符
1930            if a1.k != a2.k {
1931                continue;
1932            }
1933            let inverse = match (a1.op, a2.op) {
1934                (BinOp::Add, BinOp::Sub) | (BinOp::Sub, BinOp::Add) => true,
1935                (BinOp::BitXor, BinOp::BitXor) => true,
1936                _ => false,
1937            };
1938            if !inverse {
1939                continue;
1940            }
1941            // 删除两条语句:先删靠后的(索引不失效),再删靠前的
1942            return Some(Edit::Multi(vec![
1943                Edit::Delete { node: s2 },
1944                Edit::Delete { node: s1 },
1945            ]));
1946        }
1947        None
1948    }
1949}
1950
1951/// 归一化一条赋值语句 → `(变量键, 算符, 常量)`。
1952/// 接受复合形态(`x op= K`,RHS 即字面量)与展开形态(`x = x op K`,
1953/// RHS = Binary{op, VarRef(x), K})。
1954struct NormAssign<L: Lang> {
1955    key: L::NameKey,
1956    target: L::Id,
1957    op: BinOp,
1958    k: i64,
1959    local: bool,
1960    /// 精确整型(浮点 (x+K)-K 在舍入/Inf 上 ≠ x,必须拒绝)
1961    exact_int: bool,
1962}
1963
1964fn norm_assign<L: Lang>(lang: &L, stmt: L::Id) -> Option<NormAssign<L>> {
1965    // 语句形态:ExprStmt{Assign} 或裸 Assign
1966    let a = match lang.kind(stmt) {
1967        NodeKind::ExprStmt => {
1968            let c = lang.children(stmt);
1969            if c.len() == 1 && lang.kind(c[0]) == NodeKind::Assign {
1970                c[0]
1971            } else {
1972                return None;
1973            }
1974        }
1975        NodeKind::Assign => stmt,
1976        _ => return None,
1977    };
1978    let ch = lang.children(a).to_vec();
1979    if ch.len() != 2 {
1980        return None;
1981    }
1982    let (target, rhs) = (ch[0], ch[1]);
1983    if lang.kind(target) != NodeKind::VarRef {
1984        return None;
1985    }
1986    let key = lang.var_key(target)?;
1987    let local = lang.is_local_var(target);
1988    let exact_int = lang.is_exact_int(target);
1989    // RHS 的求值效果:字面量 Pure;Binary(VarRef, lit) 是 Pure/MayRead——
1990    // 展开形态对 x 旧值的读正是我们要抵消的一部分(两句间的观察点
1991    // 为空),但 Binary 内若非纯形态拒绝
1992    match lang.assign_op(a) {
1993        // 复合形态:`x op= K`——RHS 就是操作数
1994        Some(op) => {
1995            let k = lang.literal(rhs).and_then(|x| x.as_int())?;
1996            Some(NormAssign { key, target, op, k, local, exact_int })
1997        }
1998        // 展开形态:`x = x op K`——RHS = Binary{op, VarRef(x), K}
1999        None => {
2000            if lang.kind(rhs) != NodeKind::Binary {
2001                return None;
2002            }
2003            let op = lang.bin_op(rhs)?;
2004            let rc = lang.children(rhs).to_vec();
2005            if rc.len() != 2 {
2006                return None;
2007            }
2008            // 左操作数必须是 x 自身(同一绑定)
2009            if lang.kind(rc[0]) != NodeKind::VarRef || !lang.same_binding(target, rc[0]) {
2010                return None;
2011            }
2012            let k = lang.literal(rc[1]).and_then(|x| x.as_int())?;
2013            Some(NormAssign { key, target, op, k, local, exact_int })
2014        }
2015    }
2016}
2017
2018pub struct DeadStore;
2019
2020impl<L: Lang> Rule<L> for DeadStore {
2021    fn name(&self) -> &'static str {
2022        "dead_store"
2023    }
2024    fn kinds(&self) -> &'static [NodeKind] {
2025        &[NodeKind::VarDecl, NodeKind::Assign, NodeKind::ExprStmt]
2026    }
2027    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2028        let RewriteCtx { lang, walk } = ctx;
2029        // 归一化第一条语句:VarDecl / Assign / ExprStmt{Assign}
2030        let (decl_form, first_assign) = match lang.kind(id) {
2031            NodeKind::VarDecl => {
2032                if lang.children(id).is_empty() {
2033                    return None; // 无 init 的声明不适用
2034                }
2035                (true, None)
2036            }
2037            NodeKind::Assign => (false, Some(id)),
2038            NodeKind::ExprStmt => {
2039                let ch = lang.children(id);
2040                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2041                    (false, Some(ch[0]))
2042                } else {
2043                    return None;
2044                }
2045            }
2046            _ => return None,
2047        };
2048        if decl_form && !lang.dead_decl_allowed(id) {
2049            // 声明形态的死存储删除同受语言侧约束(TS 的 let/const 删除
2050            // 改变 TDZ/词法遮蔽——annexB block-decl 家族抓获)
2051            return None;
2052        }
2053        let first_value = if decl_form {
2054            *lang.children(id).first()?
2055        } else {
2056            let a = first_assign?;
2057            if lang.assign_op(a).is_some() {
2058                return None; // 复合赋值有读语义
2059            }
2060            *lang.children(a).get(1)?
2061        };
2062        if lang.value_may_throw_unresolved(first_value) {
2063            return None;
2064        }
2065
2066        // 父 Block + 相邻下一条语句
2067        let parent = walk.parent(id)?;
2068        if lang.kind(parent) != NodeKind::Block {
2069            return None;
2070        }
2071        let idx = walk.index(id)?;
2072        let stmts = lang.children(parent).to_vec();
2073        let next_stmt = *stmts.get(idx + 1)?;
2074
2075        // 归一化第二条语句:Assign / ExprStmt{Assign}(简单赋值);
2076        // 不是赋值 → 落入「零用途死存储」形态(见下)
2077        let second = match lang.kind(next_stmt) {
2078            NodeKind::Assign => Some(next_stmt),
2079            NodeKind::ExprStmt => {
2080                let ch = lang.children(next_stmt);
2081                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2082                    Some(ch[0])
2083                } else {
2084                    None
2085                }
2086            }
2087            _ => None,
2088        };
2089        let Some(second) = second else {
2090            // 零用途死存储:后继对该变量**无读且无写**(含遮蔽重声明),
2091            // 存入的值永不流出 → 整条删除。值效果 ≤ MayRead 才可丢弃。
2092            // (批量化后可达的形态:传播先消费了击杀写,init 残留为死值;
2093            //   旧逐编辑时序下由相邻对形态逐步吸收,未暴露此缺口。)
2094            // **作用域前提**:名字必须声明于本块(块级作用域,出块不可见)。
2095            // 外层声明的名字可能在本块之后被外层代码读取(if/while 分支里
2096            // 对外层变量的赋值就是典型)→ 扫描只覆盖本块尾部,必须拒绝。
2097            // (property.rs seed=29 的随机程序抓获此漏洞。)
2098            let (name_node, target_local) = if decl_form {
2099                (id, true)
2100            } else {
2101                let t = *lang.children(first_assign?).first()?;
2102                if lang.kind(t) != NodeKind::VarRef {
2103                    return None;
2104                }
2105                (t, lang.is_local_var(t))
2106            };
2107            // 赋值形态:目标必须是本局部(声明形态天然是局部)
2108            if !target_local {
2109                return None;
2110            }
2111            let name = lang.var_name(name_node)?.to_string();
2112            let declared_in_this_block = if decl_form {
2113                true // 检查对象就是本块的声明
2114            } else if parent == walk.root {
2115                // 方法体根块:尾部扫描覆盖到方法末尾——参数/局部变量的
2116                // 零后续事件即可判定为死(方法外不可见)
2117                true
2118            } else {
2119                // 嵌套块:名字必须声明于本块(外层声明的名字可能被
2120                // 本块之后的外层代码读取)
2121                stmts[..idx].iter().any(|&s| {
2122                    lang.kind(s) == NodeKind::VarDecl && lang.var_name(s) == Some(name.as_str())
2123                })
2124            };
2125            if !declared_in_this_block {
2126                return None;
2127            }
2128            // 块内**之前**的原文/类体可能捕获该名字并在之后调用时读
2129            //(`class A{constructor(){flag…}}; flag=true; new A()`——
2130            // 之后语句看不到类体里的读;Test262 TypedArray 家族抓获)
2131            for &s in &stmts[..idx] {
2132                if lang.stmt_has_opaque_region(s) {
2133                    return None;
2134                }
2135            }
2136            let name_key0 = lang.var_key(name_node)?;
2137            // Indexed: a statement that neither mentions the name (any
2138            // VarRef, including event-blind assignment targets, or a
2139            // shadowing declaration) nor contains opaque text cannot read,
2140            // write or shadow it. The recursive event scan remains the
2141            // fallback for unindexed nodes.
2142            for &s in &stmts[idx + 1..] {
2143                if lang.stmt_has_opaque_region(s) {
2144                    return None;
2145                }
2146                match (lang.stmt_mentions(s, name_key0), lang.stmt_opaque(s)) {
2147                    (Some(mentions), Some(opaque)) => {
2148                        if mentions || opaque {
2149                            return None;
2150                        }
2151                    }
2152                    _ => {
2153                        let mut w0 = Watch::<L>::new(name_key0, &[name_key0]);
2154                        scan_region(&*lang, s, &mut w0);
2155                        if w0.opaque
2156                            || !w0.uses.is_empty()
2157                            || w0.wrote(name_key0)
2158                            || w0.shadowed
2159                        {
2160                            return None;
2161                        }
2162                        if subtree_contains(&*lang, s, |n| {
2163                            lang.kind(n) == NodeKind::VarRef
2164                                && lang.var_name(n) == Some(name.as_str())
2165                        }) {
2166                            return None;
2167                        }
2168                    }
2169                }
2170            }
2171            if lang.effect(first_value) > Effect::MayRead {
2172                return None;
2173            }
2174            return Some(Edit::Delete { node: id });
2175        };
2176        if lang.assign_op(second).is_some() {
2177            return None;
2178        }
2179        let nch = lang.children(second);
2180        let (target, value) = (*nch.first()?, *nch.get(1)?);
2181        if lang.kind(target) != NodeKind::VarRef {
2182            return None;
2183        }
2184
2185        // 变量一致性 + 局部性
2186        if decl_form {
2187            if !lang.same_binding(id, target) {
2188                return None;
2189            }
2190        } else {
2191            let a = first_assign?;
2192            let t = *lang.children(a).first()?;
2193            if lang.kind(t) != NodeKind::VarRef
2194                || !lang.same_binding(t, target)
2195                || !lang.is_local_var(t)
2196            {
2197                return None;
2198            }
2199        }
2200        // b 中不得引用 x(x = b 依赖旧值时不可删第一条)。
2201        // 含 Raw/原文区域时 AST 看不见其中的读(`r2 = yield r2`)——
2202        // 保守拒绝,否则会删掉前一条仍被读取的存储。
2203        if lang.stmt_has_opaque_region(value)
2204            || subtree_contains(&*lang, value, |n| {
2205                lang.kind(n) == NodeKind::VarRef && lang.same_binding(n, target)
2206            })
2207        {
2208            return None;
2209        }
2210        // a 的求值被丢弃:effect ≤ MayRead
2211        if lang.effect(first_value) > Effect::MayRead {
2212            return None;
2213        }
2214
2215        if decl_form {
2216            // int x = a; x = b; → init 换成 b,删除第二条语句
2217            Some(Edit::Multi(vec![
2218                Edit::Replace {
2219                    target: first_value,
2220                    with: value,
2221                },
2222                Edit::Delete {
2223                    node: next_stmt,
2224                },
2225            ]))
2226        } else {
2227            // x = a; x = b; → 删除第一条语句(id 本身:Assign 或 ExprStmt)
2228            Some(Edit::Delete { node: id })
2229        }
2230    }
2231}
2232
2233// ---------------------------------------------------------------------------
2234// 不可达语句删除(块内 return/throw/break/continue 之后的语句)。
2235// 语义安全(永不执行),但属于 DCE 范畴——**默认不启用**,经 all_rules() 选配。
2236// ---------------------------------------------------------------------------
2237
2238/// 块展平:Block 的直接孩子是 Block 且**内层无任何声明**(VarDecl/
2239/// ForEach/Catch 绑定引入作用域)→ 内层语句上提。典型形态:try 剥壳、
2240/// CFF 还原后的裸嵌套块。无声明 ⇒ 无遮蔽 ⇒ 语义等价。
2241pub struct BlockFlatten;
2242
2243impl<L: Lang> Rule<L> for BlockFlatten {
2244    fn name(&self) -> &'static str {
2245        "block_flatten"
2246    }
2247    fn kinds(&self) -> &'static [NodeKind] {
2248        &[NodeKind::Block]
2249    }
2250    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2251        let RewriteCtx { lang, walk } = ctx;
2252        let parent = walk.parent(id)?;
2253        if lang.kind(parent) != NodeKind::Block {
2254            return None;
2255        }
2256        // 内层不得有任何声明(作用域引入)
2257        let inner = lang.children(id).to_vec();
2258        let declares = |n: L::Id| -> bool {
2259            matches!(
2260                lang.kind(n),
2261                NodeKind::VarDecl | NodeKind::ForEach | NodeKind::Catch
2262            )
2263        };
2264        if inner.iter().any(|&s| declares(s)) {
2265            return None;
2266        }
2267        // JS:直接孩子的函数声明同理(Annex B 提升/初始化时机改变)
2268        if lang.block_hoists_decl(id) {
2269            return None;
2270        }
2271        // 模式绑定流作用域(R15 三角化根因 1——BindingsTest1 抓获):
2272        // instanceof 绑定(藏在 while/for/if 条件子树里,不在直接孩子的
2273        // declares 三类中)的作用域按 JLS 6.3 可越出语句到 enclosing
2274        // block——上提曾使两个匿名块内的 String s 绑定落进同一方法域
2275        //(javac「变量 s 重复定义」)
2276        if lang.has_pattern_bindings(id) {
2277            return None;
2278        }
2279        let idx = walk.index(id)?;
2280        Some(Edit::Splice {
2281            node: parent,
2282            index: idx,
2283            remove: 1,
2284            insert: inner,
2285        })
2286    }
2287}
2288
2289/// 数值双重取负:-(-x) → x(补码回绕下恒等:-(-MIN) ≡ MIN ≡ x);
2290/// -(-lit) → |lit|(解析器不折负字面量的外层负)。
2291pub struct DoubleNegFold;
2292
2293impl<L: Lang> Rule<L> for DoubleNegFold {
2294    fn name(&self) -> &'static str {
2295        "double_neg_fold"
2296    }
2297    fn kinds(&self) -> &'static [NodeKind] {
2298        &[NodeKind::Unary]
2299    }
2300    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2301        let RewriteCtx { lang, .. } = ctx;
2302        if lang.un_op(id) != Some(UnOp::Neg) {
2303            return None;
2304        }
2305        let inner = *lang.children(id).first()?;
2306        if lang.un_op(inner) == Some(UnOp::Neg) {
2307            let x = *lang.children(inner).first()?;
2308            // `-(-x)` 仅对精确整数恒等:布尔/字符串/null 会被 ToNumber
2309            //(`-(-true)` = 1 而不是 true);浮点 -0 也会改变符号。
2310            if !lang.is_exact_int(x) {
2311                return None;
2312            }
2313            return Some(Edit::Replace {
2314                target: id,
2315                with: x,
2316            });
2317        }
2318        // -(-lit):负数值字面量取外层负 → 正字面量(宽度溢出时语言侧
2319        // 拒绝——Java i32 的 `-(-2147483648)` 无法表示)
2320        if let Some((v, wide)) = lang.literal(inner).and_then(|l| match l {
2321            LitRef::Int(v) => Some((v, false)),
2322            LitRef::Long(v) => Some((v, true)),
2323            _ => None,
2324        }) {
2325            if v < 0 {
2326                let n = lang.fold_lit_neg(v, wide)?;
2327                let with = lang.build_int(n, wide);
2328                return Some(Edit::Replace {
2329                    target: id,
2330                    with,
2331                });
2332            }
2333        }
2334        None
2335    }
2336}
2337
2338pub struct UnreachableAfterTerminal;
2339
2340impl<L: Lang> Rule<L> for UnreachableAfterTerminal {
2341    fn name(&self) -> &'static str {
2342        "unreachable_after_terminal"
2343    }
2344    fn kinds(&self) -> &'static [NodeKind] {
2345        &[NodeKind::Block]
2346    }
2347    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2348        let RewriteCtx { lang, walk: _ } = ctx;
2349        if lang.kind(id) != NodeKind::Block {
2350            return None;
2351        }
2352        let children = lang.children(id).to_vec();
2353        let terminal = |n: L::Id| -> bool {
2354            matches!(
2355                lang.kind(n),
2356                NodeKind::Return | NodeKind::Throw | NodeKind::Break | NodeKind::Continue
2357            )
2358        };
2359        let mut cut: Option<(usize, usize)> = None;
2360        for (i, &s) in children.iter().enumerate() {
2361            if terminal(s) && i + 1 < children.len() {
2362                cut = Some((i + 1, children.len() - i - 1));
2363                break;
2364            }
2365        }
2366        let (index, remove) = cut?;
2367        Some(Edit::Splice {
2368            node: id,
2369            index,
2370            remove,
2371            insert: Vec::new(),
2372        })
2373    }
2374}
2375
2376
2377// ---------------------------------------------------------------------------
2378// 比较常量折叠(反混淆核心):1 < 2 → true、2 == 3 → false。
2379// 仅整数(含 long)与布尔字面量;浮点(NaN 语义)与字符串(引用比较)不折。
2380// 连锁:boolean b = 2 > 1; if (b) {...} → 不透明谓词被完全击穿。
2381// ---------------------------------------------------------------------------
2382
2383pub struct CmpConstFold;
2384
2385impl<L: Lang> Rule<L> for CmpConstFold {
2386    fn name(&self) -> &'static str {
2387        "cmp_const_fold"
2388    }
2389    fn kinds(&self) -> &'static [NodeKind] {
2390        &[NodeKind::Binary]
2391    }
2392    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2393        let RewriteCtx { lang, walk: _ } = ctx;
2394        if lang.kind(id) != NodeKind::Binary {
2395            return None;
2396        }
2397        let op = lang.bin_op(id)?;
2398        if !op.is_comparison() {
2399            return None;
2400        }
2401        let ch = lang.children(id);
2402        let (l, r) = (*ch.first()?, *ch.get(1)?);
2403        // 双字面量才考虑折叠;可比较种类与比较语义由语言决定
2404        // (Java:整数/字符/布尔 Eq/Ne;字符串是引用比较 → 不折)
2405        lang.literal(l)?;
2406        lang.literal(r)?;
2407        let v = lang.fold_lit_cmp(op, l, r)?;
2408        let with = lang.build_bool(v);
2409        Some(Edit::Replace {
2410            target: id,
2411            with,
2412        })
2413    }
2414}
2415
2416// ---------------------------------------------------------------------------
2417// 位运算恒等式(混淆器高频产物):
2418//   x ^ 0 → x、0 ^ x → x、x | 0 → x、0 | x → x、x & -1 → x、-1 & x → x(x 保留,恒安全)
2419//   x | -1 → -1、x & 0 → 0、0 & x → 0(x 求值被丢弃 → effect ≤ MayRead)
2420//   x ^ x → 0(同名局部整型变量)
2421// ---------------------------------------------------------------------------
2422
2423pub struct BitIdentity;
2424
2425impl<L: Lang> Rule<L> for BitIdentity {
2426    fn name(&self) -> &'static str {
2427        "bit_identity"
2428    }
2429    fn kinds(&self) -> &'static [NodeKind] {
2430        &[NodeKind::Binary]
2431    }
2432    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2433        let RewriteCtx { lang, walk: _ } = ctx;
2434        if lang.kind(id) != NodeKind::Binary {
2435            return None;
2436        }
2437        let op = lang.bin_op(id)?;
2438        if !matches!(op, BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor) {
2439            return None;
2440        }
2441        let ch = lang.children(id);
2442        let (l, r) = (*ch.first()?, *ch.get(1)?);
2443        let int_lit = |n: L::Id| lang.literal(n).and_then(|x| x.as_int());
2444
2445        // x ^ x → 0(同名局部变量)
2446        if op == BinOp::BitXor
2447            && lang.kind(l) == NodeKind::VarRef
2448            && lang.kind(r) == NodeKind::VarRef
2449            && lang.same_binding(l, r)
2450            && lang.is_local_var(l)
2451            && lang.is_exact_int(l)
2452        {
2453            let with = lang.build_int(0, false);
2454            return Some(Edit::Replace {
2455                target: id,
2456                with,
2457            });
2458        }
2459
2460        // 字面量在任一侧(互换尝试)
2461        for (x, k) in [(l, r), (r, l)] {
2462            let Some(kv) = int_lit(k) else {
2463                continue;
2464            };
2465            if !lang.is_exact_int(x) {
2466                continue;
2467            }
2468            let identity = match (op, kv) {
2469                (BinOp::BitXor, 0) => Some(x),          // x ^ 0 → x
2470                (BinOp::BitOr, 0) => Some(x),           // x | 0 → x
2471                (BinOp::BitAnd, -1) => Some(x),         // x & -1 → x
2472                (BinOp::BitAnd, 0) | (BinOp::BitOr, -1) => {
2473                    // x & 0 → 0 / x | -1 → -1:丢弃 x 求值,需可丢弃
2474                    if lang.effect(x) > Effect::MayRead {
2475                        None
2476                    } else {
2477                        Some(k)
2478                    }
2479                }
2480                _ => None,
2481            };
2482            if let Some(with) = identity {
2483                return Some(Edit::Replace {
2484                    target: id,
2485                    with,
2486                });
2487            }
2488        }
2489        None
2490    }
2491}
2492
2493// ---------------------------------------------------------------------------
2494// 算术/异或重结合(混淆器经典):(x + K1) - K2 → x ± K、(x ^ K1) ^ K2 → x ^ K。
2495// 整数回绕算术下恒安全;K == 0 时直接得 x((x + 5) - 5 → x、(x ^ 84) ^ 84 → x)。
2496// ---------------------------------------------------------------------------
2497
2498pub struct ArithReassoc;
2499
2500impl<L: Lang> Rule<L> for ArithReassoc {
2501    fn name(&self) -> &'static str {
2502        "arith_reassoc"
2503    }
2504    fn kinds(&self) -> &'static [NodeKind] {
2505        &[NodeKind::Binary]
2506    }
2507    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2508        let RewriteCtx { lang, walk: _ } = ctx;
2509        if lang.kind(id) != NodeKind::Binary {
2510            return None;
2511        }
2512        let op2 = lang.bin_op(id)?;
2513        if !matches!(op2, BinOp::Add | BinOp::Sub | BinOp::BitXor) {
2514            return None;
2515        }
2516        let och = lang.children(id).to_vec();
2517        let inner = och[0];
2518        if lang.kind(inner) != NodeKind::Binary {
2519            return None;
2520        }
2521        let op1 = lang.bin_op(inner)?;
2522        let ich = lang.children(inner).to_vec();
2523        let x = ich[0];
2524
2525        // 字符串拼接重结合:(x + "K1") + "K2" → x + "K1K2"
2526        // (拼接满足结合律且各操作数恰按序求值一次;x 任意类型)。
2527        // **整链折叠**:左倾链 (((x+"a")+"a")+"a")… 一刀折成 x+"aaa…"——
2528        // 逐层折每 pass 一层是 O(n²)(openjdk DeepStringConcat 32001 项
2529        // 78s/32k passes 抓获;整链后 1 编辑收敛)
2530        if op1 == BinOp::Add && op2 == BinOp::Add {
2531            if let (Some(LitRef::Str(k1)), Some(LitRef::Str(k2))) =
2532                (lang.literal(ich[1]), lang.literal(och[1]))
2533            {
2534                // 收集左倾链(外→内),要求每层 (child + "str") 同构
2535                let mut chain: Vec<L::Id> = vec![id];
2536                let mut cur = id;
2537                loop {
2538                    let cch = lang.children(cur).to_vec();
2539                    let next = cch[0];
2540                    let deeper = lang.kind(next) == NodeKind::Binary
2541                        && lang.bin_op(next) == Some(BinOp::Add)
2542                        && matches!(lang.literal(lang.children(next).to_vec()[1]),
2543                            Some(LitRef::Str(_)));
2544                    if deeper {
2545                        chain.push(next);
2546                        cur = next;
2547                    } else {
2548                        break;
2549                    }
2550                }
2551                // base = 最内层的左操作数;parts 内→外 = 源顺序
2552                let innermost = *chain.last().unwrap();
2553                let ich2 = lang.children(innermost).to_vec();
2554                let base = ich2[0];
2555                let mut joined = String::new();
2556                for &n in chain.iter().rev() {
2557                    if let Some(LitRef::Str(k)) =
2558                        lang.literal(lang.children(n).to_vec()[1])
2559                    {
2560                        joined.push_str(k);
2561                    }
2562                }
2563                let _ = (k1, k2); // 已并入 joined
2564                let lit = lang.build_str(&joined);
2565                let with = lang.build_bin(BinOp::Add, base, lit);
2566                return Some(Edit::Replace {
2567                    target: id,
2568                    with,
2569                });
2570            }
2571        }
2572
2573        // 整数重结合:(x ± K1) ± K2 → x ± K、(x ^ K1) ^ K2 → x ^ K
2574        // 常量合并与宽度回绕语义在 Lang::reassoc_delta(Java:i32/i64
2575        // JLS 环绕——i32 合并溢出不回绕会产出非法字面量)
2576        let k1 = lang.literal(ich[1]).and_then(|x| x.as_int())?;
2577        let k2 = lang.literal(och[1]).and_then(|x| x.as_int())?;
2578        if !lang.is_exact_int(x) {
2579            return None;
2580        }
2581        let is_long = matches!(lang.literal(ich[1]), Some(LitRef::Long(_)))
2582            || matches!(lang.literal(och[1]), Some(LitRef::Long(_)));
2583        let with = match lang.reassoc_delta(op1, k1, op2, k2, is_long)? {
2584            ReassocOutcome::Neutral => x,
2585            ReassocOutcome::Lit(lit, op) => lang.build_bin(op, x, lit),
2586        };
2587        Some(Edit::Replace {
2588            target: id,
2589            with,
2590        })
2591    }
2592}
2593
2594// ---------------------------------------------------------------------------
2595// if→三元归并(反编译器/混淆器经典形态):
2596//   if (c) return a; else return b;    →  return c ? a : b;
2597//   if (c) return a; return b;(收尾) →  return c ? a : b;
2598// 恒安全:c 单次求值、a/b 条件求值在两种形态下一致(三元是惰性的)。
2599// 布尔特例由 ternary_bool 继续收敛成 return c / return !c。
2600// ---------------------------------------------------------------------------
2601
2602pub struct IfToTernary;
2603
2604impl<L: Lang> Rule<L> for IfToTernary {
2605    fn name(&self) -> &'static str {
2606        "if_to_ternary"
2607    }
2608    fn kinds(&self) -> &'static [NodeKind] {
2609        &[NodeKind::If]
2610    }
2611    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2612        let RewriteCtx { lang, walk } = ctx;
2613        if lang.kind(id) != NodeKind::If {
2614            return None;
2615        }
2616        let ch = lang.children(id).to_vec();
2617        let c = ch[0];
2618
2619        // 提取"单 return 语句"分支:Block{Return} 或裸 Return
2620        let single_ret = |n: L::Id| -> Option<Option<L::Id>> {
2621            match lang.kind(n) {
2622                NodeKind::Return => Some(lang.children(n).first().copied()),
2623                NodeKind::Block if lang.children(n).len() == 1 => {
2624                    let inner = lang.children(n)[0];
2625                    if lang.kind(inner) == NodeKind::Return {
2626                        Some(lang.children(inner).first().copied())
2627                    } else {
2628                        None
2629                    }
2630                }
2631                _ => None,
2632            }
2633        };
2634
2635        if ch.len() == 3 {
2636            // 完整 if-else:两支都是单 return
2637            let a = single_ret(ch[1])?;
2638            let b = single_ret(ch[2])?;
2639            let with = match (a, b) {
2640                (Some(va), Some(vb)) => {
2641                    let t = lang.build_ternary(c, va, vb);
2642                    lang.build_return(Some(t))
2643                }
2644                (None, None) => lang.build_return(None),
2645                _ => return None,
2646            };
2647            return Some(Edit::Replace {
2648                target: id,
2649                with,
2650            });
2651        }
2652
2653        // if-then + 紧随的收尾 return(必须是父块最后一条语句)
2654        if ch.len() != 2 {
2655            return None;
2656        }
2657        let a = single_ret(ch[1])?;
2658        let parent = walk.parent(id)?;
2659        if lang.kind(parent) != NodeKind::Block {
2660            return None;
2661        }
2662        let idx = walk.index(id)?;
2663        let stmts = lang.children(parent).to_vec();
2664        if idx + 2 != stmts.len() {
2665            return None; // return 必须是最后一条
2666        }
2667        let tail = stmts[idx + 1];
2668        if lang.kind(tail) != NodeKind::Return {
2669            return None;
2670        }
2671        let b = lang.children(tail).first().copied();
2672        let with = match (a, b) {
2673            (Some(va), Some(vb)) => {
2674                let t = lang.build_ternary(c, va, vb);
2675                lang.build_return(Some(t))
2676            }
2677            (None, None) => lang.build_return(None),
2678            _ => return None,
2679        };
2680        Some(Edit::Splice {
2681            node: parent,
2682            index: idx,
2683            remove: 2,
2684            insert: vec![with],
2685        })
2686    }
2687}
2688
2689// ---------------------------------------------------------------------------
2690// if→三元赋值归并:
2691//   if (c) { x = a; } else { x = b; }  →  x = c ? a : b;
2692// 恒安全(x 为变量名,两种形态各求值一次;a/b 条件求值一致)。
2693// ---------------------------------------------------------------------------
2694
2695pub struct IfAssignTernary;
2696
2697impl<L: Lang> Rule<L> for IfAssignTernary {
2698    fn name(&self) -> &'static str {
2699        "if_assign_ternary"
2700    }
2701    fn kinds(&self) -> &'static [NodeKind] {
2702        &[NodeKind::If]
2703    }
2704    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2705        let RewriteCtx { lang, walk: _ } = ctx;
2706        if lang.kind(id) != NodeKind::If {
2707            return None;
2708        }
2709        let ch = lang.children(id).to_vec();
2710        if ch.len() != 3 {
2711            return None;
2712        }
2713        let c = ch[0];
2714        // 单赋值分支:Block{Assign} / Block{ExprStmt{Assign}} / 裸(ExprStmt 包裹的)Assign
2715        let single_assign = |n: L::Id| -> Option<(L::Id, L::Id)> {
2716            let unwrap = |i: L::Id| -> L::Id {
2717                if lang.kind(i) == NodeKind::ExprStmt {
2718                    let ch = lang.children(i);
2719                    if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2720                        return ch[0];
2721                    }
2722                }
2723                i
2724            };
2725            let inner = match lang.kind(n) {
2726                NodeKind::Assign => n,
2727                NodeKind::ExprStmt => unwrap(n),
2728                NodeKind::Block if lang.children(n).len() == 1 => unwrap(lang.children(n)[0]),
2729                _ => return None,
2730            };
2731            if lang.kind(inner) != NodeKind::Assign {
2732                return None;
2733            }
2734            if lang.assign_op(inner).is_some() {
2735                return None;
2736            }
2737            let a = lang.children(inner).to_vec();
2738            if lang.kind(a[0]) != NodeKind::VarRef {
2739                return None;
2740            }
2741            Some((a[0], a[1]))
2742        };
2743        let (t1, va) = single_assign(ch[1])?;
2744        let (t2, vb) = single_assign(ch[2])?;
2745        if !lang.same_binding(t1, t2) || lang.var_name(t1).is_none() {
2746            return None;
2747        }
2748        let t = lang.build_ternary(c, va, vb);
2749        let with = lang.build_assign(t1, t);
2750        Some(Edit::Replace {
2751            target: id,
2752            with,
2753        })
2754    }
2755}
2756
2757
2758// ---------------------------------------------------------------------------
2759// 声明-赋值合并(反编译器把声明与赋值拆开的形态):
2760//   int x; x = 5;  →  int x = 5;
2761// 守卫:相邻同块、简单赋值、value 不引用 x(依赖旧值则非法)。
2762// 之后 local_propagation 可继续把 init 内联到唯一使用处。
2763// ---------------------------------------------------------------------------
2764
2765pub struct DeclAssignMerge;
2766
2767impl<L: Lang> Rule<L> for DeclAssignMerge {
2768    fn name(&self) -> &'static str {
2769        "decl_assign_merge"
2770    }
2771    fn kinds(&self) -> &'static [NodeKind] {
2772        &[NodeKind::VarDecl]
2773    }
2774    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2775        let RewriteCtx { lang, walk } = ctx;
2776        if lang.kind(id) != NodeKind::VarDecl || !lang.children(id).is_empty() {
2777            return None; // 必须是无 init 声明
2778        }
2779        let name = lang.var_name(id)?.to_string();
2780        let parent = walk.parent(id)?;
2781        if lang.kind(parent) != NodeKind::Block {
2782            return None;
2783        }
2784        let idx = walk.index(id)?;
2785        let stmts = lang.children(parent).to_vec();
2786        let next = *stmts.get(idx + 1)?;
2787        // 下一条:Assign(x, v) / ExprStmt{Assign(x, v)}(简单赋值)
2788        let assign = match lang.kind(next) {
2789            NodeKind::Assign => next,
2790            NodeKind::ExprStmt => {
2791                let ch = lang.children(next);
2792                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2793                    ch[0]
2794                } else {
2795                    return None;
2796                }
2797            }
2798            _ => return None,
2799        };
2800        if lang.assign_op(assign).is_some() {
2801            return None;
2802        }
2803        let ach = lang.children(assign).to_vec();
2804        let (target, value) = (ach[0], ach[1]);
2805        if lang.kind(target) != NodeKind::VarRef || lang.var_name(target) != Some(name.as_str()) {
2806            return None;
2807        }
2808        // value 不得引用 x(读旧值)
2809        if subtree_contains(&*lang, value, |n| {
2810            lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name.as_str())
2811        }) {
2812            return None;
2813        }
2814        Some(Edit::Multi(vec![
2815            Edit::Splice {
2816                node: id,
2817                index: 0,
2818                remove: 0,
2819                insert: vec![value],
2820            },
2821            Edit::Delete { node: next },
2822        ]))
2823    }
2824}
2825
2826// ---------------------------------------------------------------------------
2827// 赋值传播(寄存器拷贝消除,jadx/jcdc 产物):
2828//   x = v; …唯一一次读 x(区间内无对 x / v 读集的写、无遮蔽)…
2829//   → 用 v 替换该次读,删除赋值。
2830// 安全锚点:x 必须在本块内有前置声明(赋值无作用域,块外读取会破坏语义)。
2831// v 纯/只读 → 自由移动(写冲突检查);v 有副作用 → 相邻 + 直线 + 可读前缀。
2832// ---------------------------------------------------------------------------
2833
2834pub struct AssignPropagation;
2835
2836impl<L: Lang> Rule<L> for AssignPropagation {
2837    fn name(&self) -> &'static str {
2838        "assign_propagation"
2839    }
2840    fn kinds(&self) -> &'static [NodeKind] {
2841        &[NodeKind::Assign, NodeKind::ExprStmt]
2842    }
2843    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
2844        let RewriteCtx { lang, walk } = ctx;
2845        // 语句形态:Assign / ExprStmt{Assign}
2846        let (assign, stmt_node) = match lang.kind(id) {
2847            NodeKind::Assign => (id, id),
2848            NodeKind::ExprStmt => {
2849                let ch = lang.children(id);
2850                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
2851                    (ch[0], id)
2852                } else {
2853                    return None;
2854                }
2855            }
2856            _ => return None,
2857        };
2858        if lang.assign_op(assign).is_some() {
2859            return None;
2860        }
2861        let ach = lang.children(assign).to_vec();
2862        let (target, value) = (ach[0], ach[1]);
2863        if lang.kind(target) != NodeKind::VarRef || !lang.is_local_var(target) {
2864            return None;
2865        }
2866        // 裸数组字面量只在声明/赋值 RHS 位置合法,传播会产出非法源码 → 拒绝
2867        if lang.kind(value) == NodeKind::ArrayLit {
2868            return None;
2869        }
2870        // lambda / 方法引用值同 ArrayLit:receiver 位非法 → 保守拒绝
2871        if matches!(lang.kind(value), NodeKind::Lambda | NodeKind::MethodRef) {
2872            return None;
2873        }
2874        let name = lang.var_name(target)?.to_string();
2875
2876        let parent = walk.parent(stmt_node)?;
2877        if lang.kind(parent) != NodeKind::Block {
2878            return None;
2879        }
2880        let idx = walk.index(stmt_node)?;
2881        let stmts = lang.children(parent).to_vec();
2882
2883        // 锚点:x 在本块 idx 之前有 VarDecl(保证 x 不会逃逸到块外)
2884        let anchored = stmts[..idx].iter().any(|&s| {
2885            lang.kind(s) == NodeKind::VarDecl && lang.var_name(s) == Some(name.as_str())
2886        });
2887        if !anchored {
2888            return None;
2889        }
2890
2891        // value 不引用 x(x = x + 1 之类)
2892        if subtree_contains(&*lang, value, |n| {
2893            lang.kind(n) == NodeKind::VarRef && lang.var_name(n) == Some(name.as_str())
2894        }) {
2895            return None;
2896        }
2897
2898        // 扫描赋值之后的区域(用途/遮蔽全区间;写冲突窗口见下)。
2899        // 句法引用计数兜底(同 local_propagation——bd.java 教训)
2900        let name_key = lang.var_key(target)?;
2901        if count_name_refs(&*lang, &stmts[idx + 1..], name_key, &name) != 1 {
2902            return None;
2903        }
2904        let mut wa = Watch::<L>::new(name_key, &[]);
2905        for &s in &stmts[idx + 1..] {
2906            if lang.stmt_has_opaque_region(s) {
2907                return None;
2908            }
2909            scan_region(&*lang, s, &mut wa);
2910        }
2911        if wa.opaque || wa.shadowed || wa.uses.len() != 1 {
2912            return None;
2913        }
2914        let use_id = wa.uses[0];
2915        // null 字面量:接收者位(AST 形态 Call→Member→[recv],VarRef 的
2916        // 父是 Member)或静态调用 callee(防御)之外,**实参位**同样拒绝
2917        // ——`str2 = null; append(str2)` 合法(str2 声明类型钉住重载),
2918        // 内联成 `append(null)` 后 append(CharSequence)/append(char[])
2919        // 二义(ddc 输出 PurchasesOrchestrator 复现)。null_unsafe_position
2920        // 沿 Paren 链判位;Cast 包裹((String) s)重建类型 → 放行
2921        if matches!(lang.literal(value), Some(LitRef::Null))
2922            && null_unsafe_position(&*lang, walk, use_id)
2923        {
2924            return None;
2925        }
2926        // VarRef 值不得内联进 cast/instanceof 类型判定位(声明类型即合法
2927        // 性依据——同 null 实参位的族);声明类型未解析或与目标类型
2928        // 同名时恒可编译(var_cast_position_ok 钩子裁决)
2929        if lang.kind(value) == NodeKind::VarRef
2930            && typed_position(&*lang, walk, use_id)
2931                .is_some_and(|t| !lang.var_cast_position_ok(value, t))
2932        {
2933            return None;
2934        }
2935        // 写冲突兴趣集:value 读到的名字键 + x 自身(自身被写 → 覆盖,拒绝)
2936        let mut watch_keys: Vec<L::NameKey> = Vec::new();
2937        collect_read_keys(&*lang, value, &mut watch_keys);
2938        watch_keys.push(name_key);
2939        let mut wb = Watch::<L>::new(name_key, &watch_keys);
2940        for &s in &stmts[idx + 1..] {
2941            let uses_before = wb.uses.len();
2942            scan_region(&*lang, s, &mut wb);
2943            if uses_before < wb.uses.len() {
2944                break; // 使用语句之后的写不参与冲突判定
2945            }
2946        }
2947        // x 自身在窗口内被写 → 赋值会被覆盖,拒绝(先于目标写排除)
2948        if wb.wrote(name_key) {
2949            return None;
2950        }
2951        let ve = lang.effect(value);
2952
2953        if ve <= Effect::MayRead {
2954            if let Some(target_key) = use_stmt_target_write_key(&*lang, &stmts[idx + 1..], use_id) {
2955                wb.clear_wrote(target_key);
2956            }
2957            if wb.any_write() {
2958                return None;
2959            }
2960            return Some(Edit::Multi(vec![
2961                Edit::Replace {
2962                    target: use_id,
2963                    with: value,
2964                },
2965                Edit::Delete { node: stmt_node },
2966            ]));
2967        }
2968
2969        // 有副作用:相邻 + 直线 + 可读前缀(与 local_propagation 同判据)
2970        if idx + 1 >= stmts.len() {
2971            return None;
2972        }
2973        let stmt = stmts[idx + 1];
2974        if !subtree_contains(&*lang, stmt, |n| n == use_id) {
2975            return None;
2976        }
2977        match lang.kind(stmt) {
2978            NodeKind::Return
2979            | NodeKind::ExprStmt
2980            | NodeKind::Assign
2981            | NodeKind::Throw
2982            | NodeKind::VarDecl => {}
2983            _ => return None,
2984        }
2985        if !straight_path(&*lang, stmt, use_id) {
2986            return None;
2987        }
2988        if !prefix_effects_readable(&*lang, stmt, use_id) {
2989            return None;
2990        }
2991        Some(Edit::Multi(vec![
2992            Edit::Replace {
2993                target: use_id,
2994                with: value,
2995            },
2996            Edit::Delete { node: stmt_node },
2997        ]))
2998    }
2999}
3000
3001
3002/// 单一使用点若恰为 `target = use` 的 RHS,则该语句对 target 的写发生在
3003/// use 求值**之后**——从写冲突集中排除(否则 `int v = x + s; x = v;` 这类
3004/// 循环尾寄存器回拷永远无法内联)。
3005/// 排除目标必须≠被传播变量自身:`int i = 1; i = i;` 里排除 `i` 会删掉声明
3006/// 留下无绑定的赋值(该场景由 self_assign 处理)。
3007fn use_stmt_target_write_key<L: Lang>(
3008    lang: &L,
3009    stmts_after: &[L::Id],
3010    use_id: L::Id,
3011) -> Option<L::NameKey> {
3012    for &s in stmts_after {
3013        if subtree_contains(lang, s, |n| n == use_id) {
3014            let assign = match lang.kind(s) {
3015                NodeKind::Assign => s,
3016                NodeKind::ExprStmt => {
3017                    let ch = lang.children(s);
3018                    if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
3019                        ch[0]
3020                    } else {
3021                        return None;
3022                    }
3023                }
3024                _ => return None,
3025            };
3026            if lang.assign_op(assign).is_some() {
3027                return None;
3028            }
3029            let ch = lang.children(assign);
3030            if ch.len() == 2 && ch[1] == use_id && lang.kind(ch[0]) == NodeKind::VarRef {
3031                return lang.var_key(ch[0]);
3032            }
3033            return None;
3034        }
3035    }
3036    None
3037}
3038
3039/// 收集 `id` 子树中 VarRef 读到的全部名字键(去重,线性)。
3040/// 替代旧 reads_vars 的 HashSet 版:value 的读名字典型 ≤10,线性查重更快。
3041fn collect_read_keys<L: Lang>(lang: &L, id: L::Id, out: &mut Vec<L::NameKey>) {
3042    let mut stack = vec![id];
3043    while let Some(n) = stack.pop() {
3044        if lang.kind(n) == NodeKind::VarRef {
3045            if let Some(k) = lang.var_key(n) {
3046                if !out.contains(&k) {
3047                    out.push(k);
3048                }
3049            }
3050        }
3051        for &c in lang.children(n) {
3052            stack.push(c);
3053        }
3054    }
3055}
3056
3057
3058// ---------------------------------------------------------------------------
3059// 尾部 continue 删除(反编译器产物):
3060//   while (c) { …; continue; }  →  while (c) { … }
3061// 循环体最后一条无标签 continue 与顺序落入下一轮等价。恒安全。
3062// ---------------------------------------------------------------------------
3063
3064pub struct TrailingContinue;
3065
3066impl<L: Lang> Rule<L> for TrailingContinue {
3067    fn name(&self) -> &'static str {
3068        "trailing_continue"
3069    }
3070    fn kinds(&self) -> &'static [NodeKind] {
3071        &[NodeKind::While, NodeKind::For, NodeKind::DoWhile, NodeKind::ForEach]
3072    }
3073    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3074        let RewriteCtx { lang, walk: _ } = ctx;
3075        // While/For/DoWhile/ForEach 的 body 为 Block 且末语句为无标签 continue
3076        let body = match lang.kind(id) {
3077            NodeKind::While | NodeKind::For | NodeKind::DoWhile | NodeKind::ForEach => {
3078                *lang.children(id).last()?
3079            }
3080            _ => return None,
3081        };
3082        if lang.kind(body) != NodeKind::Block {
3083            return None;
3084        }
3085        let ch = lang.children(body).to_vec();
3086        let last = *ch.last()?;
3087        if lang.kind(last) != NodeKind::Continue || !lang.children(last).is_empty() {
3088            // JavaAST 的 Continue 标签在负载里,children 为空即无标签(引擎层约定)
3089            // 对带标签的语言实现:children[last] 非空即有标签 → 不动
3090            if lang.kind(last) != NodeKind::Continue {
3091                return None;
3092            }
3093        }
3094        Some(Edit::Delete { node: last })
3095    }
3096}
3097
3098// ---------------------------------------------------------------------------
3099// 多用途拷贝传播(寄存器副本消除):
3100//   x = y;(y 为简单 VarRef,x 有本块声明锚点,之后无对 x/y 的写、无遮蔽)
3101//   → 把 x 的**所有**后续读替换为 y,删除赋值。
3102// 守卫 y 不被写:替换后各使用点读的是 y 的当前值,须与原 x 的值一致。
3103// ---------------------------------------------------------------------------
3104
3105pub struct MultiUseCopyPropagation;
3106
3107impl<L: Lang> Rule<L> for MultiUseCopyPropagation {
3108    fn name(&self) -> &'static str {
3109        "multi_use_copy"
3110    }
3111    fn kinds(&self) -> &'static [NodeKind] {
3112        &[NodeKind::Assign, NodeKind::ExprStmt]
3113    }
3114    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3115        let RewriteCtx { lang, walk } = ctx;
3116        // 语句形态:Assign(x, VarRef y) / ExprStmt{Assign(...)}
3117        let (assign, stmt_node) = match lang.kind(id) {
3118            NodeKind::Assign => (id, id),
3119            NodeKind::ExprStmt => {
3120                let ch = lang.children(id);
3121                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
3122                    (ch[0], id)
3123                } else {
3124                    return None;
3125                }
3126            }
3127            _ => return None,
3128        };
3129        if lang.assign_op(assign).is_some() {
3130            return None;
3131        }
3132        let ach = lang.children(assign).to_vec();
3133        let (target, value) = (ach[0], ach[1]);
3134        if lang.kind(target) != NodeKind::VarRef || lang.kind(value) != NodeKind::VarRef {
3135            return None;
3136        }
3137        // lambda / 方法引用值:内联进 receiver 位非法(对抗波 2)→ 保守拒绝
3138        if matches!(lang.kind(value), NodeKind::Lambda | NodeKind::MethodRef) {
3139            return None;
3140        }
3141        if !lang.is_local_var(target) {
3142            return None;
3143        }
3144        let name = lang.var_name(target)?.to_string();
3145        let src = lang.var_name(value)?.to_string();
3146        if name == src {
3147            return None;
3148        }
3149
3150        let parent = walk.parent(stmt_node)?;
3151        if lang.kind(parent) != NodeKind::Block {
3152            return None;
3153        }
3154        let idx = walk.index(stmt_node)?;
3155        let stmts = lang.children(parent).to_vec();
3156
3157        // 锚点:x 在本块 idx 之前有 VarDecl
3158        let anchored = stmts[..idx].iter().any(|&s| {
3159            lang.kind(s) == NodeKind::VarDecl && lang.var_name(s) == Some(name.as_str())
3160        });
3161        if !anchored {
3162            return None;
3163        }
3164
3165        // 扫描后续:收集 x 的全部读;对 x 的任何写 / 对 y 的任何写 / 遮蔽 → 拒绝
3166        let name_key = lang.var_key(target)?;
3167        let src_key = lang.var_key(value)?;
3168        let mut w1 = Watch::<L>::new(name_key, &[name_key, src_key]);
3169        for &s in &stmts[idx + 1..] {
3170            scan_region(&*lang, s, &mut w1);
3171        }
3172        if w1.opaque || w1.shadowed || w1.uses.is_empty() {
3173            return None;
3174        }
3175        if w1.wrote(name_key) || w1.wrote(src_key) {
3176            return None;
3177        }
3178        // 句法守卫:x 在赋值后的句法出现总数(含数组写 base——x[i]=v
3179        // 的 x 不产生事件,事件模型盲区)必须等于事件 uses 数。
3180        // 出现 > uses ⇒ 存在事件看不见的引用(bd.java 差分二轮抓获:
3181        // var10004 = var154 的读被替换、var10004[i]= 写目标残留 →
3182        // 读写裂脑 → 类初始化 AIOOBE)
3183        let synth = count_name_refs(&*lang, &stmts[idx + 1..], name_key, &name);
3184        if synth != w1.uses.len() {
3185            return None;
3186        }
3187        // y 也不得在【赋值前】与 x 指向不同值(x=y 之前 y 已是其所值,无需检查);
3188        // 但 x=y 之间不能有对 y 的写(相邻语句,天然无中间)——赋值本身就是当前值 ✓
3189
3190        // VarRef 值(y)不得内联进 cast/instanceof 类型判定位:被替换
3191        // 变量(x)的声明类型是这些位置可转换性的依据——`Object x =
3192        // k3Var; s = (String) x;` 合法,替换成 `(String) k3Var` 引用类型
3193        // 不相关即不可编译(ddc weixin dt/k 复现)。任一使用点命中 →
3194        // 整体拒绝(多用途替换不可拆单点)。
3195        if w1.uses.iter().any(|&u| {
3196            typed_position(&*lang, walk, u).is_some_and(|t| !lang.var_cast_position_ok(value, t))
3197        }) {
3198            return None;
3199        }
3200
3201        let mut edits: Vec<Edit<L>> = w1.uses
3202            .iter()
3203            .map(|&u| Edit::Replace {
3204                target: u,
3205                with: value,
3206            })
3207            .collect();
3208        edits.push(Edit::Delete { node: stmt_node });
3209        Some(Edit::Multi(edits))
3210    }
3211}
3212
3213
3214// ---------------------------------------------------------------------------
3215// 尾部裸 return 删除(DAD/androguard 伪影):
3216//   void 方法/构造器体最后一条无值 return 与自然结束等价,删除。
3217// 仅匹配根块(方法体)末语句,避免动到块中间的提前 return。
3218// ---------------------------------------------------------------------------
3219
3220pub struct TrailingReturn;
3221
3222impl<L: Lang> Rule<L> for TrailingReturn {
3223    fn name(&self) -> &'static str {
3224        "trailing_return"
3225    }
3226    fn kinds(&self) -> &'static [NodeKind] {
3227        &[NodeKind::Return]
3228    }
3229    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3230        let root = ctx.root();
3231        let parent = ctx.parent(id)?;
3232        let lang = ctx.lang;
3233        if lang.kind(id) != NodeKind::Return || !lang.children(id).is_empty() {
3234            return None;
3235        }
3236        if parent != root || lang.kind(parent) != NodeKind::Block {
3237            return None;
3238        }
3239        if lang.children(parent).last() != Some(&id) {
3240            return None;
3241        }
3242        Some(Edit::Delete { node: id })
3243    }
3244}
3245
3246
3247// ---------------------------------------------------------------------------
3248// 三元-布尔运算归并(反混淆产物):
3249//   c ? a : false → c && a、c ? true : a → c || a(a 为布尔)
3250//   c 单次求值、a 条件求值,两种形态完全一致。
3251// ---------------------------------------------------------------------------
3252
3253pub struct TernaryBoolOp;
3254
3255impl<L: Lang> Rule<L> for TernaryBoolOp {
3256    fn name(&self) -> &'static str {
3257        "ternary_bool_op"
3258    }
3259    fn kinds(&self) -> &'static [NodeKind] {
3260        &[NodeKind::Ternary]
3261    }
3262    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3263        let RewriteCtx { lang, walk: _ } = ctx;
3264        if lang.kind(id) != NodeKind::Ternary {
3265            return None;
3266        }
3267        let ch = lang.children(id);
3268        let (c, a, b) = (*ch.first()?, *ch.get(1)?, *ch.get(2)?);
3269        // c 必须是语法级布尔:`1 ? true : false` 得 true,而 `1 || false`
3270        // 得 1(truthy 非布尔条件改变结果类型)。随机差分 case 2074 抓获。
3271        if !lang.is_bool(c) {
3272            return None;
3273        }
3274        let lb = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if v);
3275        let is_false = |n: L::Id| matches!(lang.literal(n), Some(LitRef::Bool(v)) if !v);
3276        let with = if is_false(b) && lang.is_bool(a) {
3277            lang.build_bin(BinOp::And, c, a)
3278        } else if lb(b) && lang.is_bool(a) {
3279            // c ? a : true ≡ !c || a —— 少见,跳过保持保守
3280            return None;
3281        } else if lb(a) && lang.is_bool(b) {
3282            // c ? true : b ≡ c || b
3283            lang.build_bin(BinOp::Or, c, b)
3284        } else if is_false(a) && lang.is_bool(b) {
3285            // c ? false : b ≡ !c && b —— 少见,跳过保持保守
3286            return None;
3287        } else {
3288            return None;
3289        };
3290        Some(Edit::Replace {
3291            target: id,
3292            with,
3293        })
3294    }
3295}
3296
3297
3298// ---------------------------------------------------------------------------
3299// 远距存储消除(寄存器预声明清理,ddc/jcdc 产物):
3300//   int x = 0; …(无 x 事件)…; x = 1;   →  int x = 1;(字面量提升)
3301//   int x = 0; …(无 x 事件)…; x = v;   →  int x;(init 死亡,剥除)
3302//   x = 0; …(无 x 事件)…; x = v;       →  删除第一条(值 ≤ MayRead)
3303// 守卫:首个 x 事件必须是写;语句内 x 出现若非简单赋值目标即视为读(阻断)。
3304// ---------------------------------------------------------------------------
3305
3306pub struct StoreKill;
3307
3308/// 扫描一个节点在 `name` 上的事件。
3309/// `dominating`:当前子树是否在必经路径上(If/While 的 cond、Ternary 的
3310/// cond、For 的 init/cond、非短路二元两侧 = true;分支体/循环体 = false)。
3311///
3312/// 语言侧若提供语句级预计算索引([`Lang::stmt_kill_event`]),调用方
3313/// 应优先查询;本函数是未索引节点的回退路径。
3314pub fn scan_kill_event<L: Lang>(
3315    lang: &L,
3316    node: L::Id,
3317    name: &str,
3318    dominating: bool,
3319) -> StmtKill<L::Id> {
3320    match lang.kind(node) {
3321        NodeKind::VarRef => {
3322            if lang.var_name(node) == Some(name) {
3323                StmtKill::Read
3324            } else {
3325                StmtKill::None
3326            }
3327        }
3328        NodeKind::VarDecl => {
3329            if lang.var_name(node) == Some(name) {
3330                StmtKill::Shadow
3331            } else {
3332                // 其他变量声明:init 必经求值
3333                for &c in lang.children(node) {
3334                    let e = scan_kill_event(lang, c, name, dominating);
3335                    if !matches!(e, StmtKill::None) {
3336                        return e;
3337                    }
3338                }
3339                StmtKill::None
3340            }
3341        }
3342        NodeKind::Assign => {
3343            let ch = lang.children(node);
3344            // RHS 先求值
3345            if let Some(&val) = ch.get(1) {
3346                let e = scan_kill_event(lang, val, name, dominating);
3347                if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3348                    return e;
3349                }
3350            }
3351            let Some(&target) = ch.first() else {
3352                return StmtKill::None;
3353            };
3354            if lang.kind(target) == NodeKind::VarRef {
3355                if lang.assign_op(node).is_some() {
3356                    // 复合赋值 `x += 1` 读旧值
3357                    if lang.var_name(target) == Some(name) {
3358                        return StmtKill::Read;
3359                    }
3360                } else if lang.var_name(target) == Some(name) {
3361                    if dominating {
3362                        return StmtKill::DomWrite(node);
3363                    }
3364                    return StmtKill::None; // 条件写:不击杀,也不算读
3365                }
3366            } else {
3367                // 成员/索引目标:基座与键是读(`a[i] = v` 读 a、i)
3368                let e = scan_kill_event(lang, target, name, false);
3369                if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3370                    return e;
3371                }
3372            }
3373            StmtKill::None
3374        }
3375        NodeKind::If | NodeKind::While | NodeKind::Ternary => {
3376            // cond 必经;分支体/循环体不必然——但其中的【读】仍保活 init,
3377            // 必须扫描(以非支配标记:读传播,写不击杀)
3378            let ch = lang.children(node);
3379            if let Some(&c) = ch.first() {
3380                let e = scan_kill_event(lang, c, name, true);
3381                if !matches!(e, StmtKill::None) {
3382                    return e;
3383                }
3384            }
3385            for &c in &ch[1..] {
3386                let e = scan_kill_event(lang, c, name, false);
3387                if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3388                    return e;
3389                }
3390            }
3391            StmtKill::None
3392        }
3393        NodeKind::Binary => {
3394            let Some(op) = lang.bin_op(node) else {
3395                return StmtKill::None;
3396            };
3397            let ch = lang.children(node);
3398            if op.is_short_circuit() {
3399                // 左支必经;右支条件求值(读→保活;写→不击杀)
3400                if let Some(&l) = ch.first() {
3401                    let e = scan_kill_event(lang, l, name, true);
3402                    if !matches!(e, StmtKill::None) {
3403                        return e;
3404                    }
3405                }
3406                if let Some(&r) = ch.get(1) {
3407                    let e = scan_kill_event(lang, r, name, false);
3408                    if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3409                        return e;
3410                    }
3411                }
3412                StmtKill::None
3413            } else {
3414                for &c in ch {
3415                    let e = scan_kill_event(lang, c, name, dominating);
3416                    if !matches!(e, StmtKill::None) {
3417                        return e;
3418                    }
3419                }
3420                StmtKill::None
3421            }
3422        }
3423        NodeKind::For | NodeKind::ForEach | NodeKind::Switch | NodeKind::Try => {
3424            // 必经部分(For 的 inits+cond / ForEach 的 iterable / Switch 的
3425            // subject)以支配标记扫;其余(循环体/case 体/try 块)以非支配
3426            // 标记扫——其中的读仍保活
3427            let ch = lang.children(node);
3428            let dom_len = match lang.kind(node) {
3429                NodeKind::For => ch.len().saturating_sub(2),
3430                NodeKind::ForEach | NodeKind::Switch => 1,
3431                _ => 0,
3432            };
3433            for &c in &ch[..dom_len] {
3434                let e = scan_kill_event(lang, c, name, true);
3435                if !matches!(e, StmtKill::None) {
3436                    return e;
3437                }
3438            }
3439            for &c in &ch[dom_len..] {
3440                let e = scan_kill_event(lang, c, name, false);
3441                if matches!(e, StmtKill::Read | StmtKill::Shadow) {
3442                    return e;
3443                }
3444            }
3445            StmtKill::None
3446        }
3447        _ => {
3448            for &c in lang.children(node) {
3449                let e = scan_kill_event(lang, c, name, dominating);
3450                if !matches!(e, StmtKill::None) {
3451                    return e;
3452                }
3453            }
3454            StmtKill::None
3455        }
3456    }
3457}
3458
3459/// One DFS of a statement computing the dominating=true kill event for
3460/// every name that has one. Recursion order matches [`scan_kill_event`];
3461/// the first event per name wins. Used by language crates to build a
3462/// per-statement index so StoreKill does not rescan subtrees per candidate.
3463pub fn collect_stmt_kill_events<L: Lang>(lang: &L, root: L::Id) -> Vec<(L::NameKey, StmtKill<L::Id>)> {
3464    use std::collections::BTreeMap;
3465    enum Act<Id> {
3466        Enter(Id, bool),
3467        AfterAssign(Id, bool),
3468    }
3469    let mut events: BTreeMap<L::NameKey, StmtKill<L::Id>> = BTreeMap::new();
3470    let mut stack = vec![Act::Enter(root, true)];
3471    while let Some(act) = stack.pop() {
3472        match act {
3473            Act::AfterAssign(id, dominating) => {
3474                if lang.assign_op(id).is_some() {
3475                    continue;
3476                }
3477                let Some(&target) = lang.children(id).first() else {
3478                    continue;
3479                };
3480                if lang.kind(target) != NodeKind::VarRef {
3481                    continue;
3482                }
3483                let Some(key) = lang.var_key(target) else {
3484                    continue;
3485                };
3486                if dominating {
3487                    events.entry(key).or_insert(StmtKill::DomWrite(id));
3488                }
3489            }
3490            Act::Enter(node, dominating) => match lang.kind(node) {
3491                NodeKind::VarRef => {
3492                    if let Some(key) = lang.var_key(node) {
3493                        events.entry(key).or_insert(StmtKill::Read);
3494                    }
3495                }
3496                NodeKind::VarDecl => {
3497                    if let Some(key) = lang.var_key(node) {
3498                        events.entry(key).or_insert(StmtKill::Shadow);
3499                    }
3500                    for &c in lang.children(node).iter().rev() {
3501                        stack.push(Act::Enter(c, dominating));
3502                    }
3503                }
3504                NodeKind::Assign => {
3505                    let ch = lang.children(node);
3506                    stack.push(Act::AfterAssign(node, dominating));
3507                    if let Some(&val) = ch.get(1) {
3508                        stack.push(Act::Enter(val, dominating));
3509                    }
3510                    // 目标是 VarRef:简单赋值是纯写(不产生 Use),
3511                    // 复合赋值要读旧值;成员/索引目标整体按读扫描。
3512                    if let Some(&target) = ch.first() {
3513                        if lang.kind(target) != NodeKind::VarRef
3514                            || lang.assign_op(node).is_some()
3515                        {
3516                            stack.push(Act::Enter(target, false));
3517                        }
3518                    }
3519                }
3520                NodeKind::If | NodeKind::While | NodeKind::Ternary => {
3521                    let ch = lang.children(node);
3522                    for (i, &c) in ch.iter().enumerate().rev() {
3523                        stack.push(Act::Enter(c, i == 0));
3524                    }
3525                }
3526                NodeKind::Binary => {
3527                    let ch = lang.children(node);
3528                    let short = lang.bin_op(node).is_some_and(|op| op.is_short_circuit());
3529                    for (i, &c) in ch.iter().enumerate().rev() {
3530                        let child_dom = if short { i == 0 } else { dominating };
3531                        stack.push(Act::Enter(c, child_dom));
3532                    }
3533                }
3534                NodeKind::For | NodeKind::ForEach | NodeKind::Switch | NodeKind::Try => {
3535                    let ch = lang.children(node);
3536                    let dom_len = match lang.kind(node) {
3537                        NodeKind::For => ch.len().saturating_sub(2),
3538                        NodeKind::ForEach | NodeKind::Switch => 1,
3539                        _ => 0,
3540                    };
3541                    for (i, &c) in ch.iter().enumerate().rev() {
3542                        stack.push(Act::Enter(c, i < dom_len));
3543                    }
3544                }
3545                _ => {
3546                    for &c in lang.children(node).iter().rev() {
3547                        stack.push(Act::Enter(c, dominating));
3548                    }
3549                }
3550            },
3551        }
3552    }
3553    events.into_iter().collect()
3554}
3555
3556impl<L: Lang> Rule<L> for StoreKill {
3557    fn name(&self) -> &'static str {
3558        "store_kill"
3559    }
3560    fn kinds(&self) -> &'static [NodeKind] {
3561        &[NodeKind::VarDecl, NodeKind::Assign, NodeKind::ExprStmt]
3562    }
3563    fn check(&self, ctx: RewriteCtx<'_, L>, id: L::Id) -> Option<Edit<L>> {
3564        let RewriteCtx { lang, walk } = ctx;
3565        // 形态:VarDecl(带 init) 或 Assign(裸/ExprStmt 包裹)
3566        let (is_decl, stmt_node, first_value) = match lang.kind(id) {
3567            NodeKind::VarDecl => {
3568                if lang.children(id).is_empty() {
3569                    return None;
3570                }
3571                (true, id, *lang.children(id).first()?)
3572            }
3573            NodeKind::Assign => {
3574                if lang.assign_op(id).is_some() {
3575                    return None;
3576                }
3577                (false, id, *lang.children(id).get(1)?)
3578            }
3579            NodeKind::ExprStmt => {
3580                let ch = lang.children(id);
3581                if ch.len() == 1 && lang.kind(ch[0]) == NodeKind::Assign {
3582                    let a = ch[0];
3583                    if lang.assign_op(a).is_some() {
3584                        return None;
3585                    }
3586                    (false, id, *lang.children(a).get(1)?)
3587                } else {
3588                    return None;
3589                }
3590            }
3591            _ => return None,
3592        };
3593        let (name, name_key) = if is_decl {
3594            (lang.var_name(id)?.to_string(), lang.var_key(id)?)
3595        } else {
3596            let a = match lang.kind(id) {
3597                NodeKind::Assign => id,
3598                _ => lang.children(id)[0],
3599            };
3600            let t = *lang.children(a).first()?;
3601            if lang.kind(t) != NodeKind::VarRef || !lang.is_local_var(t) {
3602                return None;
3603            }
3604            (lang.var_name(t)?.to_string(), lang.var_key(t)?)
3605        };
3606
3607        let parent = walk.parent(stmt_node)?;
3608        if lang.kind(parent) != NodeKind::Block {
3609            return None;
3610        }
3611        let idx = walk.index(stmt_node)?;
3612        let stmts = lang.children(parent).to_vec();
3613
3614        // 扫描:读(任意路径)→ 保活;支配写 → 击杀;条件写 → 继续
3615        let mut killed_by: Option<(usize, L::Id)> = None;
3616        for (si, &s) in stmts.iter().enumerate().skip(idx + 1) {
3617            // 原文/类体等不可见区域可能读本变量——击杀不得跨越
3618            //(Test262 TypedArray:class 构造器读 flag,首个 store 被杀,
3619            // flag 变成声明即 true)
3620            if lang.stmt_has_opaque_region(s) {
3621                return None;
3622            }
3623            let event = lang
3624                .stmt_kill_event(s, name_key)
3625                .unwrap_or_else(|| scan_kill_event(&*lang, s, &name, true));
3626            match event {
3627                StmtKill::None => continue,
3628                StmtKill::Read | StmtKill::Shadow => return None,
3629                StmtKill::DomWrite(assign) => {
3630                    killed_by = Some((si, assign));
3631                    break;
3632                }
3633            }
3634        }
3635        let (si, kill_assign) = killed_by?;
3636        // 击杀语句级守卫:击杀赋值必须就是**顶层语句本身**(或其
3637        // ExprStmt 包裹)。嵌套击杀(do-while 体/裸块/for-init 里的写)
3638        // 曾按 stmts[si] 删整条外层语句——连兄弟副作用一起删
3639        //(RealTimeSequencer.pump() 的 133 行 do-while 曾被整删成
3640        // 3 行;ConstantPool.writeTags 双 for+switch 全删——JDK 语料
3641        // ≥9 文件)。同语句内击杀点之后的读也无法用语句级窗口证明
3642        // 不可达 → 保守拒绝嵌套击杀(顶层击杀不受影响)
3643        {
3644            let kill_container = match walk.parents.get(&kill_assign) {
3645                Some(&(p, _)) if lang.kind(p) == NodeKind::ExprStmt => p,
3646                _ => kill_assign,
3647            };
3648            if kill_container != stmts[si] {
3649                return None;
3650            }
3651        }
3652        // 异常窗口守卫:被击杀点到击杀点之间的语句若可能抛(效果 >
3653        // MayRead),异常路径会绕过击杀点直达 finally/catch——后者可能
3654        // 在「中间值」状态读到本变量(finally-flag 恢复模式:
3655        // v=true; setSoTimeout(1); v=false; finally{if(v)…}——差分
3656        // 抓获:v=true 曾被杀,异常路径的恢复逻辑变死代码)。
3657        //
3658        // 精化:异常只可能被**包围本块的 Try** 的 handler 截获;无包围
3659        // Try 时异常直接出方法,局部变量对外不可见——中间值无人可观
3660        // 察,窗口内的不纯语句不构成障碍(ddc 0.1.33 形态:声明序列 →
3661        // new 对象 → 击杀赋值,构造调用恰在窗口内)。窗口内嵌套 try
3662        // 的 handler 读已由 per-statement 事件扫描以 Read 击退,不在
3663        // 此列。
3664        let mut has_try_ancestor = false;
3665        {
3666            let mut cur = stmt_node;
3667            while let Some(&(p, _)) = walk.parents.get(&cur) {
3668                if lang.kind(p) == NodeKind::Try {
3669                    has_try_ancestor = true;
3670                    break;
3671                }
3672                cur = p;
3673            }
3674        }
3675        if has_try_ancestor {
3676            for &s in &stmts[idx + 1..si] {
3677                if lang.effect(s) > Effect::MayRead {
3678                    return None;
3679                }
3680            }
3681        }
3682
3683        if is_decl {
3684            // 击杀赋值的值是字面量 → 提升进声明 init
3685            // (原 init 被替换丢弃——同样必须可丢弃,副作用调用不得删!)
3686            if lang.effect(first_value) > Effect::MayRead {
3687                return None;
3688            }
3689            let value = *lang.children(kill_assign).get(1)?;
3690            if is_literal(lang, value) {
3691                let assign_stmt = stmts[si];
3692                return Some(Edit::Multi(vec![
3693                    Edit::Splice {
3694                        node: id,
3695                        index: 0,
3696                        remove: 1,
3697                        insert: vec![value],
3698                    },
3699                    Edit::Delete { node: assign_stmt },
3700                ]));
3701            }
3702            // 否则:剥除 init(裸声明)——init 必须可丢弃(副作用调用不得删!)。
3703            // 推断类型声明(Java `var`)剥 init 后非法——拒绝
3704            //(obf_fuzz 差分 seed 4 抓获:`var flag;` 编译失败)
3705            if lang.is_inferred_decl(id) {
3706                return None;
3707            }
3708            if lang.effect(first_value) > Effect::MayRead {
3709                return None;
3710            }
3711            return Some(Edit::Splice {
3712                node: id,
3713                index: 0,
3714                remove: 1,
3715                insert: Vec::new(),
3716            });
3717        }
3718        // 赋值形态:首值必须可丢弃
3719        if lang.effect(first_value) > Effect::MayRead {
3720            return None;
3721        }
3722        Some(Edit::Delete { node: stmt_node })
3723    }
3724}
3725
3726/// value 是否为字面量(提升安全:无求值位置问题)。
3727fn is_literal<L: Lang>(lang: &L, n: L::Id) -> bool {
3728    matches!(lang.kind(n), NodeKind::Literal)
3729}
3730
3731// ---------------------------------------------------------------------------
3732// 注册表
3733// ---------------------------------------------------------------------------
3734
3735/// 默认规则(保守集:不做 DCE 类清理)。
3736pub fn default_rules<L: Lang>() -> Vec<Box<dyn Rule<L>>> {
3737    vec![
3738        Box::new(DoubleNegFold),
3739        Box::new(BlockFlatten),
3740        Box::new(ParenRemoval),
3741        Box::new(ConstCondition),
3742        Box::new(BooleanReturn),
3743        Box::new(IfToTernary),
3744        Box::new(IfAssignTernary),
3745        Box::new(IfElseEmpty),
3746        Box::new(BoolCompare),
3747        Box::new(DoubleNot),
3748        Box::new(BoolNotFold),
3749        Box::new(BoolShortCircuit),
3750        Box::new(NotCompare),
3751        Box::new(TernaryFold),
3752        Box::new(TernaryBool),
3753        Box::new(TernaryBoolOp),
3754        Box::new(ConstFoldBin),
3755        Box::new(CmpConstFold),
3756        Box::new(BitIdentity),
3757        Box::new(ArithReassoc),
3758        Box::new(SelfAssign),
3759        Box::new(ArithIdentity),
3760        Box::new(ArithZero),
3761        Box::new(DeclAssignMerge),
3762        Box::new(LocalPropagation),
3763        Box::new(AssignPropagation),
3764        Box::new(MultiUseCopyPropagation),
3765        Box::new(TrailingReturn),
3766        Box::new(InverseAssignPair),
3767        Box::new(DeadDecl),
3768        Box::new(AssignBackFold),
3769        Box::new(DeadStore),
3770        Box::new(StoreKill),
3771    ]
3772}
3773
3774/// 全量规则(含 DCE 类选配项;按设计文档"可选配,默认不启用")。
3775pub fn all_rules<L: Lang>() -> Vec<Box<dyn Rule<L>>> {
3776    let mut rules = default_rules();
3777    rules.push(Box::new(UnreachableAfterTerminal));
3778    rules
3779}