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 {
46 then_stmt,
47 else_stmt,
48 ..
49 } => {
50 has_break_exiting(then_stmt, lbl, depth)
51 || else_stmt
52 .as_deref()
53 .map(|e| has_break_exiting(e, lbl, depth))
54 .unwrap_or(false)
55 }
56 Stmt::While { body, .. }
57 | Stmt::DoWhile { body, .. }
58 | Stmt::For { body, .. }
59 | Stmt::ForEach { body, .. } => has_break_exiting(body, lbl, depth + 1),
60 Stmt::Switch { cases, default, .. } => {
61 cases
62 .iter()
63 .any(|c| c.body.iter().any(|x| has_break_exiting(x, lbl, depth + 1)))
64 || default
65 .as_deref()
66 .map(|d| has_break_exiting(d, lbl, depth + 1))
67 .unwrap_or(false)
68 }
69 Stmt::Try {
70 body,
71 catches,
72 finally,
73 }
74 | Stmt::TryWithResources {
75 body,
76 catches,
77 finally,
78 ..
79 } => {
80 has_break_exiting(body, lbl, depth)
81 || catches
82 .iter()
83 .any(|c| has_break_exiting(&c.body, lbl, depth))
84 || finally
85 .as_deref()
86 .map(|f| has_break_exiting(f, lbl, depth))
87 .unwrap_or(false)
88 }
89 Stmt::Synchronized { body, .. } => has_break_exiting(body, lbl, depth),
90 Stmt::Labeled { body, .. } => has_break_exiting(body, lbl, depth),
91 Stmt::ExprStmt(_)
93 | Stmt::LocalDef { .. }
94 | Stmt::Assert { .. }
95 | Stmt::TernaryValue { .. }
96 | Stmt::MonitorEnter(_)
97 | Stmt::MonitorExit(_)
98 | Stmt::Comment(_) => false,
99 _ => true,
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::ir::build::int_const;
109
110 fn while_true(body: Stmt) -> Stmt {
111 Stmt::While {
112 cond: int_const(1),
113 body: Box::new(body),
114 }
115 }
116
117 #[test]
118 fn infinite_while_without_break_is_a_dead_end() {
119 assert!(dead_end_infinite_while(&while_true(Stmt::Block(vec![]))));
120 let nested = Stmt::Block(vec![while_true(Stmt::Block(vec![Stmt::Break(None)]))]);
122 assert!(dead_end_infinite_while(&while_true(nested)));
123 assert!(!dead_end_infinite_while(&while_true(Stmt::Block(vec![
125 Stmt::Break(None)
126 ]))));
127 assert!(dead_end_infinite_while(&Stmt::Labeled {
129 label: "L".into(),
130 body: Box::new(while_true(Stmt::Block(vec![Stmt::Break(Some(
131 "outer".into()
132 ))]))),
133 }));
134 }
135}
136pub const ASSERT_FIELD: &str = "$jcdcAssertionsDisabled";