1use ryo_source::pure::{
7 MacroDelimiter, PureBlock, PureClosureParam, PureExpr, PureFn, PurePattern, PureStmt,
8};
9
10use super::marker::DebugMarker;
11use crate::Mutation;
12
13#[derive(Debug, Clone)]
27pub struct InsertInspectMutation {
28 pub after_method: String,
30 pub marker: DebugMarker,
32 pub var_name: String,
34 pub in_function: Option<String>,
36}
37
38impl InsertInspectMutation {
39 pub fn new(after_method: impl Into<String>, marker: DebugMarker) -> Self {
41 Self {
42 after_method: after_method.into(),
43 marker,
44 var_name: "_x".to_string(),
45 in_function: None,
46 }
47 }
48
49 pub fn with_var_name(mut self, name: impl Into<String>) -> Self {
51 self.var_name = name.into();
52 self
53 }
54
55 pub fn in_function(mut self, name: impl Into<String>) -> Self {
57 self.in_function = Some(name.into());
58 self
59 }
60
61 fn transform_expr(&self, expr: &PureExpr) -> (PureExpr, usize) {
63 match expr {
64 PureExpr::MethodCall {
65 receiver,
66 method,
67 args,
68 ..
69 } => {
70 let (new_receiver, mut count) = self.transform_expr(receiver);
72
73 if method == &self.after_method {
75 let method_call = PureExpr::MethodCall {
77 receiver: Box::new(new_receiver),
78 method: method.clone(),
79 turbofish: None,
80 args: args.clone(),
81 };
82
83 let inspect_call = self.build_inspect_call(method_call);
84 count += 1;
85 (inspect_call, count)
86 } else {
87 let new_expr = PureExpr::MethodCall {
89 receiver: Box::new(new_receiver),
90 method: method.clone(),
91 turbofish: None,
92 args: args.clone(),
93 };
94 (new_expr, count)
95 }
96 }
97
98 PureExpr::Call { func, args } => {
100 let (new_func, mut count) = self.transform_expr(func);
101 let mut new_args = Vec::new();
102 for arg in args {
103 let (new_arg, c) = self.transform_expr(arg);
104 new_args.push(new_arg);
105 count += c;
106 }
107 (
108 PureExpr::Call {
109 func: Box::new(new_func),
110 args: new_args,
111 },
112 count,
113 )
114 }
115
116 PureExpr::Binary { op, left, right } => {
117 let (new_left, c1) = self.transform_expr(left);
118 let (new_right, c2) = self.transform_expr(right);
119 (
120 PureExpr::Binary {
121 op: op.clone(),
122 left: Box::new(new_left),
123 right: Box::new(new_right),
124 },
125 c1 + c2,
126 )
127 }
128
129 PureExpr::Unary { op, expr: inner } => {
130 let (new_inner, count) = self.transform_expr(inner);
131 (
132 PureExpr::Unary {
133 op: op.clone(),
134 expr: Box::new(new_inner),
135 },
136 count,
137 )
138 }
139
140 PureExpr::Block { label, block } => {
141 let (new_block, count) = self.transform_block(block);
142 (
143 PureExpr::Block {
144 label: label.clone(),
145 block: new_block,
146 },
147 count,
148 )
149 }
150
151 PureExpr::If {
152 cond,
153 then_branch,
154 else_branch,
155 } => {
156 let (new_cond, c1) = self.transform_expr(cond);
157 let (new_then, c2) = self.transform_block(then_branch);
158 let (new_else, c3) = if let Some(e) = else_branch {
159 let (ne, c) = self.transform_expr(e);
160 (Some(Box::new(ne)), c)
161 } else {
162 (None, 0)
163 };
164 (
165 PureExpr::If {
166 cond: Box::new(new_cond),
167 then_branch: new_then,
168 else_branch: new_else,
169 },
170 c1 + c2 + c3,
171 )
172 }
173
174 PureExpr::Closure {
175 params, ret, body, ..
176 } => {
177 let (new_body, count) = self.transform_expr(body);
178 (
179 PureExpr::Closure {
180 is_async: false,
181 is_move: false,
182 params: params.clone(),
183 ret: ret.clone(),
184 body: Box::new(new_body),
185 },
186 count,
187 )
188 }
189
190 PureExpr::Match { expr: e, arms } => {
191 let (new_expr, mut count) = self.transform_expr(e);
192 let new_arms = arms
193 .iter()
194 .map(|arm| {
195 let (new_body, c) = self.transform_expr(&arm.body);
196 count += c;
197 ryo_source::pure::PureMatchArm {
198 pattern: arm.pattern.clone(),
199 guard: arm.guard.clone(),
200 body: new_body,
201 }
202 })
203 .collect();
204 (
205 PureExpr::Match {
206 expr: Box::new(new_expr),
207 arms: new_arms,
208 },
209 count,
210 )
211 }
212
213 _ => (expr.clone(), 0),
215 }
216 }
217
218 fn build_inspect_call(&self, receiver: PureExpr) -> PureExpr {
220 let marker_str = self.marker.to_marker_string();
223
224 let closure = PureExpr::Closure {
225 is_async: false,
226 is_move: false,
227 params: vec![PureClosureParam::untyped(PurePattern::Ident {
228 name: self.var_name.clone(),
229 is_mut: false,
230 by_ref: false,
231 })],
232 ret: None,
233 body: Box::new(PureExpr::Block {
234 label: None,
235 block: PureBlock {
236 stmts: vec![PureStmt::Semi(PureExpr::Macro {
237 name: "dbg".to_string(),
238 delimiter: MacroDelimiter::Paren,
239 tokens: format!("\"{}\", &{}", marker_str, self.var_name),
240 })],
241 },
242 }),
243 };
244
245 PureExpr::MethodCall {
246 receiver: Box::new(receiver),
247 method: "inspect".to_string(),
248 turbofish: None,
249 args: vec![closure],
250 }
251 }
252
253 fn transform_block(&self, block: &PureBlock) -> (PureBlock, usize) {
255 let mut count = 0;
256 let new_stmts = block
257 .stmts
258 .iter()
259 .map(|stmt| {
260 let (new_stmt, c) = self.transform_stmt(stmt);
261 count += c;
262 new_stmt
263 })
264 .collect();
265 (PureBlock { stmts: new_stmts }, count)
266 }
267
268 fn transform_stmt(&self, stmt: &PureStmt) -> (PureStmt, usize) {
270 match stmt {
271 PureStmt::Local {
272 pattern,
273 ty,
274 init,
275 else_branch,
276 } => {
277 if let Some(init_expr) = init {
278 let (new_init, count) = self.transform_expr(init_expr);
279 (
280 PureStmt::Local {
281 pattern: pattern.clone(),
282 ty: ty.clone(),
283 init: Some(new_init),
284 else_branch: else_branch.clone(),
285 },
286 count,
287 )
288 } else {
289 (stmt.clone(), 0)
290 }
291 }
292 PureStmt::Expr(expr) => {
293 let (new_expr, count) = self.transform_expr(expr);
294 (PureStmt::Expr(new_expr), count)
295 }
296 PureStmt::Semi(expr) => {
297 let (new_expr, count) = self.transform_expr(expr);
298 (PureStmt::Semi(new_expr), count)
299 }
300 _ => (stmt.clone(), 0),
301 }
302 }
303
304 pub fn transform_fn(&self, func: &PureFn) -> (PureFn, usize) {
306 if let Some(ref target_fn) = self.in_function {
308 if &func.name != target_fn {
309 return (func.clone(), 0);
310 }
311 }
312
313 let (new_body, count) = self.transform_block(&func.body);
314 let mut new_func = func.clone();
315 new_func.body = new_body;
316 (new_func, count)
317 }
318}
319
320impl Mutation for InsertInspectMutation {
321 fn describe(&self) -> String {
322 format!(
323 "Insert .inspect() after .{}() [session: {}]",
324 self.after_method, self.marker.session_id
325 )
326 }
327
328 fn mutation_type(&self) -> &'static str {
329 "InsertInspect"
330 }
331
332 fn box_clone(&self) -> Box<dyn Mutation> {
333 Box::new(self.clone())
334 }
335}