1use crate::ast::{Expr, Prop, Stmt, StmtKind, SwitchCase};
25
26pub fn stmt_captures(s: &Stmt) -> bool {
29 match &s.kind {
30 StmtKind::FuncDecl { .. } | StmtKind::ClassDecl(_) => true,
32
33 StmtKind::Expr(e) | StmtKind::Throw(e) => expr_captures(e),
34 StmtKind::Return(e) => e.as_ref().is_some_and(expr_captures),
35 StmtKind::Decl { decls, .. } => decls
36 .iter()
37 .any(|d| expr_captures(&d.target) || d.init.as_ref().is_some_and(expr_captures)),
38 StmtKind::Block(body) => body.iter().any(stmt_captures),
39 StmtKind::If { test, cons, alt } => {
40 expr_captures(test) || stmt_captures(cons) || alt.as_deref().is_some_and(stmt_captures)
41 }
42 StmtKind::While { test, body } | StmtKind::DoWhile { body, test } => {
43 expr_captures(test) || stmt_captures(body)
44 }
45 StmtKind::For {
46 init,
47 test,
48 update,
49 body,
50 } => {
51 init.as_deref().is_some_and(stmt_captures)
52 || test.as_ref().is_some_and(expr_captures)
53 || update.as_ref().is_some_and(expr_captures)
54 || stmt_captures(body)
55 }
56 StmtKind::ForOf {
57 target, iter, body, ..
58 } => expr_captures(target) || expr_captures(iter) || stmt_captures(body),
59 StmtKind::ForIn {
60 target,
61 object,
62 body,
63 ..
64 } => expr_captures(target) || expr_captures(object) || stmt_captures(body),
65 StmtKind::Switch { disc, cases } => expr_captures(disc) || cases.iter().any(case_captures),
66 StmtKind::Labeled { body, .. } => stmt_captures(body),
67 StmtKind::Try {
68 block,
69 handler,
70 finalizer,
71 } => {
72 block.iter().any(stmt_captures)
73 || handler.as_ref().is_some_and(|(param, body)| {
74 param.as_ref().is_some_and(expr_captures) || body.iter().any(stmt_captures)
75 })
76 || finalizer
77 .as_ref()
78 .is_some_and(|body| body.iter().any(stmt_captures))
79 }
80 StmtKind::Break(_) | StmtKind::Continue(_) | StmtKind::Empty => false,
81 }
82}
83
84fn case_captures(c: &SwitchCase) -> bool {
85 c.test.as_ref().is_some_and(expr_captures) || c.body.iter().any(stmt_captures)
86}
87
88pub fn expr_captures(e: &Expr) -> bool {
91 match e {
92 Expr::Function { .. } | Expr::Class(_) => true,
94 Expr::Ident(n) => n == "eval",
99
100 Expr::Null
101 | Expr::Undefined
102 | Expr::Hole
103 | Expr::True
104 | Expr::False
105 | Expr::Number(_)
106 | Expr::BigInt(_)
107 | Expr::Regex(_, _)
108 | Expr::Str(_)
109 | Expr::This
110 | Expr::Super
111 | Expr::NewTarget => false,
112
113 Expr::Template { exprs, .. } => exprs.iter().any(expr_captures),
114 Expr::TaggedTemplate { tag, exprs, .. } => {
115 expr_captures(tag) || exprs.iter().any(expr_captures)
116 }
117 Expr::Yield { arg, .. } => arg.as_deref().is_some_and(expr_captures),
118 Expr::Await(inner) | Expr::Spread(inner) | Expr::Unary(_, inner) => expr_captures(inner),
119 Expr::Array(items) | Expr::Sequence(items) => items.iter().any(expr_captures),
120 Expr::Object(props) => props.iter().any(prop_captures),
121 Expr::Logical(_, l, r) | Expr::Binary(_, l, r) => expr_captures(l) || expr_captures(r),
122 Expr::Conditional { test, cons, alt } => {
123 expr_captures(test) || expr_captures(cons) || expr_captures(alt)
124 }
125 Expr::Assign { target, value } => expr_captures(target) || expr_captures(value),
126 Expr::Update { target, .. } => expr_captures(target),
127 Expr::Call { func, args, .. } | Expr::New { callee: func, args } => {
128 expr_captures(func) || args.iter().any(expr_captures)
129 }
130 Expr::Member { object, .. } => expr_captures(object),
131 Expr::Index { object, index, .. } => expr_captures(object) || expr_captures(index),
132 }
133}
134
135fn prop_captures(p: &Prop) -> bool {
136 match p {
137 Prop::KeyValue { key, value, .. } => expr_captures(key) || expr_captures(value),
138 Prop::Spread(e) => expr_captures(e),
139 Prop::Accessor { key, func, .. } => expr_captures(key) || expr_captures(func),
142 }
143}
144
145pub fn block_needs_scope(body: &[Stmt]) -> bool {
154 body.iter().any(|s| match &s.kind {
155 StmtKind::Decl { kind, .. } => !matches!(kind, crate::ast::DeclKind::Var),
156 StmtKind::FuncDecl { .. } | StmtKind::ClassDecl(_) => true,
157 StmtKind::Expr(e) => mentions_eval(e),
160 _ => false,
161 })
162}
163
164fn mentions_eval(e: &Expr) -> bool {
166 match e {
167 Expr::Ident(n) => n == "eval",
168 Expr::Call { func, args, .. } | Expr::New { callee: func, args } => {
169 mentions_eval(func) || args.iter().any(mentions_eval)
170 }
171 Expr::Assign { target, value } => mentions_eval(target) || mentions_eval(value),
172 Expr::Sequence(items) => items.iter().any(mentions_eval),
173 Expr::Logical(_, l, r) | Expr::Binary(_, l, r) => mentions_eval(l) || mentions_eval(r),
174 Expr::Conditional { test, cons, alt } => {
175 mentions_eval(test) || mentions_eval(cons) || mentions_eval(alt)
176 }
177 Expr::Unary(_, inner) | Expr::Await(inner) | Expr::Spread(inner) => mentions_eval(inner),
178 _ => false,
179 }
180}