1use crate::ir::expr::{ConstVal, Expr};
9use crate::ir::stmt::Stmt;
10
11pub fn dead_end_infinite_while(s: &Stmt) -> bool {
20 let (label, body, cond) = match s {
21 Stmt::While { cond, body } => (None, body.as_ref(), cond),
22 Stmt::Labeled { label, body } => match &**body {
23 Stmt::While { cond, body } => (Some(label.as_str()), body.as_ref(), cond),
24 _ => return false,
25 },
26 _ => return false,
27 };
28 if !matches!(cond, Expr::Const(ConstVal::Int(1))) {
29 return false;
30 }
31 !has_break_exiting(body, label, 0)
32}
33
34fn has_break_exiting(s: &Stmt, lbl: Option<&str>, depth: usize) -> bool {
37 match s {
38 Stmt::Break(None) => depth == 0,
39 Stmt::Break(Some(l)) => Some(l.as_str()) == lbl,
42 Stmt::Continue(_) | Stmt::Return(_) | Stmt::Throw(_) => false,
43 Stmt::Goto(_) => true,
44 Stmt::Block(v) => v.iter().any(|x| has_break_exiting(x, lbl, depth)),
45 Stmt::If { then_stmt, else_stmt, .. } => {
46 has_break_exiting(then_stmt, lbl, depth)
47 || else_stmt
48 .as_deref()
49 .map(|e| has_break_exiting(e, lbl, depth))
50 .unwrap_or(false)
51 }
52 Stmt::While { body, .. }
53 | Stmt::DoWhile { body, .. }
54 | Stmt::For { body, .. }
55 | Stmt::ForEach { body, .. } => has_break_exiting(body, lbl, depth + 1),
56 Stmt::Switch { cases, default, .. } => {
57 cases
58 .iter()
59 .any(|c| c.body.iter().any(|x| has_break_exiting(x, lbl, depth + 1)))
60 || default
61 .as_deref()
62 .map(|d| has_break_exiting(d, lbl, depth + 1))
63 .unwrap_or(false)
64 }
65 Stmt::Try { body, catches, finally }
66 | Stmt::TryWithResources { body, catches, finally, .. } => {
67 has_break_exiting(body, lbl, depth)
68 || catches.iter().any(|c| has_break_exiting(&c.body, lbl, depth))
69 || finally
70 .as_deref()
71 .map(|f| has_break_exiting(f, lbl, depth))
72 .unwrap_or(false)
73 }
74 Stmt::Synchronized { body, .. } => has_break_exiting(body, lbl, depth),
75 Stmt::Labeled { body, .. } => has_break_exiting(body, lbl, depth),
76 Stmt::ExprStmt(_)
78 | Stmt::LocalDef { .. }
79 | Stmt::Assert { .. }
80 | Stmt::TernaryValue { .. }
81 | Stmt::MonitorEnter(_)
82 | Stmt::MonitorExit(_)
83 | Stmt::Comment(_) => false,
84 _ => true,
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::ir::build::int_const;
94
95 fn while_true(body: Stmt) -> Stmt {
96 Stmt::While { cond: int_const(1), body: Box::new(body) }
97 }
98
99 #[test]
100 fn infinite_while_without_break_is_a_dead_end() {
101 assert!(dead_end_infinite_while(&while_true(Stmt::Block(vec![]))));
102 let nested = Stmt::Block(vec![while_true(Stmt::Block(vec![Stmt::Break(None)]))]);
104 assert!(dead_end_infinite_while(&while_true(nested)));
105 assert!(!dead_end_infinite_while(&while_true(Stmt::Block(vec![Stmt::Break(None)]))));
107 assert!(dead_end_infinite_while(&Stmt::Labeled {
109 label: "L".into(),
110 body: Box::new(while_true(Stmt::Block(vec![Stmt::Break(Some("outer".into()))]))),
111 }));
112 }
113}
114pub const ASSERT_FIELD: &str = "$jcdcAssertionsDisabled";