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