1use super::*;
28
29pub trait FoldOwned {
39 fn fold_program(&mut self, program: &Program) -> Program {
40 fold_owned_program(self, program)
41 }
42
43 fn fold_stmt(&mut self, stmt: &Stmt) -> Stmt {
44 fold_owned_stmt(self, stmt)
45 }
46
47 fn fold_block(&mut self, block: &Block) -> Block {
48 fold_owned_block(self, block)
49 }
50
51 fn fold_expr(&mut self, expr: &Expr) -> Expr {
52 fold_owned_expr(self, expr)
53 }
54
55 fn fold_param(&mut self, param: &Param) -> Param {
56 fold_owned_param(self, param)
57 }
58
59 fn fold_arg(&mut self, arg: &Arg) -> Arg {
60 fold_owned_arg(self, arg)
61 }
62
63 fn fold_class_member(&mut self, member: &ClassMember) -> ClassMember {
64 fold_owned_class_member(self, member)
65 }
66
67 fn fold_enum_member(&mut self, member: &EnumMember) -> EnumMember {
68 fold_owned_enum_member(self, member)
69 }
70
71 fn fold_property_hook(&mut self, hook: &PropertyHook) -> PropertyHook {
72 fold_owned_property_hook(self, hook)
73 }
74
75 fn fold_type_hint(&mut self, type_hint: &TypeHint) -> TypeHint {
76 fold_owned_type_hint(self, type_hint)
77 }
78
79 fn fold_attribute(&mut self, attribute: &Attribute) -> Attribute {
80 fold_owned_attribute(self, attribute)
81 }
82
83 fn fold_catch_clause(&mut self, catch: &CatchClause) -> CatchClause {
84 fold_owned_catch_clause(self, catch)
85 }
86
87 fn fold_match_arm(&mut self, arm: &MatchArm) -> MatchArm {
88 fold_owned_match_arm(self, arm)
89 }
90
91 fn fold_closure_use_var(&mut self, var: &ClosureUseVar) -> ClosureUseVar {
92 fold_owned_closure_use_var(self, var)
93 }
94
95 fn fold_name(&mut self, name: &Name) -> Name {
96 fold_owned_name(self, name)
97 }
98}
99
100pub fn fold_owned_program<F: FoldOwned + ?Sized>(folder: &mut F, program: &Program) -> Program {
105 Program {
106 stmts: program.stmts.iter().map(|s| folder.fold_stmt(s)).collect(),
107 span: program.span,
108 }
109}
110
111pub fn fold_owned_stmt<F: FoldOwned + ?Sized>(folder: &mut F, stmt: &Stmt) -> Stmt {
112 Stmt {
113 kind: fold_owned_stmt_kind(folder, &stmt.kind),
114 span: stmt.span,
115 doc_comment: stmt.doc_comment.clone(),
116 }
117}
118
119fn fold_owned_stmts<F: FoldOwned + ?Sized>(folder: &mut F, stmts: &[Stmt]) -> Box<[Stmt]> {
120 stmts.iter().map(|s| folder.fold_stmt(s)).collect()
121}
122
123pub fn fold_owned_block<F: FoldOwned + ?Sized>(folder: &mut F, block: &Block) -> Block {
124 Block {
125 stmts: fold_owned_stmts(folder, &block.stmts),
126 span: block.span,
127 }
128}
129
130fn fold_owned_exprs<F: FoldOwned + ?Sized>(folder: &mut F, exprs: &[Expr]) -> Box<[Expr]> {
131 exprs.iter().map(|e| folder.fold_expr(e)).collect()
132}
133
134fn fold_owned_args<F: FoldOwned + ?Sized>(folder: &mut F, args: &[Arg]) -> Box<[Arg]> {
135 args.iter().map(|a| folder.fold_arg(a)).collect()
136}
137
138fn fold_owned_attrs<F: FoldOwned + ?Sized>(
139 folder: &mut F,
140 attrs: &[Attribute],
141) -> Box<[Attribute]> {
142 attrs.iter().map(|a| folder.fold_attribute(a)).collect()
143}
144
145fn fold_owned_params<F: FoldOwned + ?Sized>(folder: &mut F, params: &[Param]) -> Box<[Param]> {
146 params.iter().map(|p| folder.fold_param(p)).collect()
147}
148
149fn fold_owned_hooks<F: FoldOwned + ?Sized>(
150 folder: &mut F,
151 hooks: &[PropertyHook],
152) -> Box<[PropertyHook]> {
153 hooks.iter().map(|h| folder.fold_property_hook(h)).collect()
154}
155
156fn fold_owned_members<F: FoldOwned + ?Sized>(
157 folder: &mut F,
158 members: &[ClassMember],
159) -> Box<[ClassMember]> {
160 members
161 .iter()
162 .map(|m| folder.fold_class_member(m))
163 .collect()
164}
165
166fn fold_owned_string_parts<F: FoldOwned + ?Sized>(
167 folder: &mut F,
168 parts: &[StringPart],
169) -> Box<[StringPart]> {
170 parts
171 .iter()
172 .map(|p| match p {
173 StringPart::Literal(s) => StringPart::Literal(s.clone()),
174 StringPart::Expr(e) => StringPart::Expr(folder.fold_expr(e)),
175 })
176 .collect()
177}
178
179fn fold_owned_stmt_kind<F: FoldOwned + ?Sized>(folder: &mut F, k: &StmtKind) -> StmtKind {
180 match k {
181 StmtKind::Expression(e) => StmtKind::Expression(Box::new(folder.fold_expr(e))),
182 StmtKind::Echo(exprs) => StmtKind::Echo(fold_owned_exprs(folder, exprs)),
183 StmtKind::Return(e) => StmtKind::Return(e.as_ref().map(|e| Box::new(folder.fold_expr(e)))),
184 StmtKind::Block(block) => StmtKind::Block(Box::new(folder.fold_block(block))),
185 StmtKind::If(s) => StmtKind::If(Box::new(IfStmt {
186 condition: folder.fold_expr(&s.condition),
187 then_branch: Box::new(folder.fold_stmt(&s.then_branch)),
188 elseif_branches: s
189 .elseif_branches
190 .iter()
191 .map(|b| ElseIfBranch {
192 condition: folder.fold_expr(&b.condition),
193 body: folder.fold_stmt(&b.body),
194 span: b.span,
195 })
196 .collect(),
197 else_branch: s
198 .else_branch
199 .as_ref()
200 .map(|b| Box::new(folder.fold_stmt(b))),
201 else_kw_start: s.else_kw_start,
202 uses_alternative: s.uses_alternative,
203 })),
204 StmtKind::While(s) => StmtKind::While(Box::new(WhileStmt {
205 condition: folder.fold_expr(&s.condition),
206 body: Box::new(folder.fold_stmt(&s.body)),
207 uses_alternative: s.uses_alternative,
208 })),
209 StmtKind::For(s) => StmtKind::For(Box::new(ForStmt {
210 init: fold_owned_exprs(folder, &s.init),
211 condition: fold_owned_exprs(folder, &s.condition),
212 update: fold_owned_exprs(folder, &s.update),
213 body: Box::new(folder.fold_stmt(&s.body)),
214 uses_alternative: s.uses_alternative,
215 })),
216 StmtKind::Foreach(s) => StmtKind::Foreach(Box::new(ForeachStmt {
217 expr: folder.fold_expr(&s.expr),
218 key: s.key.as_ref().map(|e| folder.fold_expr(e)),
219 value: folder.fold_expr(&s.value),
220 body: Box::new(folder.fold_stmt(&s.body)),
221 uses_alternative: s.uses_alternative,
222 })),
223 StmtKind::DoWhile(s) => StmtKind::DoWhile(Box::new(DoWhileStmt {
224 body: Box::new(folder.fold_stmt(&s.body)),
225 condition: folder.fold_expr(&s.condition),
226 })),
227 StmtKind::Function(f) => StmtKind::Function(Box::new(FunctionDecl {
228 name: f.name.clone(),
229 params: fold_owned_params(folder, &f.params),
230 body: Box::new(folder.fold_block(&f.body)),
231 return_type: f.return_type.as_ref().map(|t| folder.fold_type_hint(t)),
232 by_ref: f.by_ref,
233 attributes: fold_owned_attrs(folder, &f.attributes),
234 doc_comment: f.doc_comment.clone(),
235 })),
236 StmtKind::Break(e) => StmtKind::Break(e.as_ref().map(|e| Box::new(folder.fold_expr(e)))),
237 StmtKind::Continue(e) => {
238 StmtKind::Continue(e.as_ref().map(|e| Box::new(folder.fold_expr(e))))
239 }
240 StmtKind::Switch(s) => StmtKind::Switch(Box::new(SwitchStmt {
241 expr: folder.fold_expr(&s.expr),
242 body: SwitchBody {
243 cases: s
244 .body
245 .cases
246 .iter()
247 .map(|c| SwitchCase {
248 value: c.value.as_ref().map(|v| folder.fold_expr(v)),
249 body: fold_owned_stmts(folder, &c.body),
250 span: c.span,
251 })
252 .collect(),
253 span: s.body.span,
254 },
255 uses_alternative: s.uses_alternative,
256 })),
257 StmtKind::Goto(ident) => StmtKind::Goto(ident.clone()),
258 StmtKind::Label(s) => StmtKind::Label(s.clone()),
259 StmtKind::Declare(d) => StmtKind::Declare(Box::new(DeclareStmt {
260 directives: d
261 .directives
262 .iter()
263 .map(|(k, v)| (k.clone(), folder.fold_expr(v)))
264 .collect(),
265 body: d.body.as_ref().map(|b| Box::new(folder.fold_stmt(b))),
266 uses_alternative: d.uses_alternative,
267 })),
268 StmtKind::Unset(exprs) => StmtKind::Unset(fold_owned_exprs(folder, exprs)),
269 StmtKind::Throw(e) => StmtKind::Throw(Box::new(folder.fold_expr(e))),
270 StmtKind::TryCatch(t) => StmtKind::TryCatch(Box::new(TryCatchStmt {
271 body: Box::new(folder.fold_block(&t.body)),
272 catches: t
273 .catches
274 .iter()
275 .map(|c| folder.fold_catch_clause(c))
276 .collect(),
277 finally: t.finally.as_deref().map(|f| Box::new(folder.fold_block(f))),
278 finally_kw_start: t.finally_kw_start,
279 })),
280 StmtKind::Global(exprs) => StmtKind::Global(fold_owned_exprs(folder, exprs)),
281 StmtKind::Class(cls) => StmtKind::Class(Box::new(fold_owned_class_decl(folder, cls))),
282 StmtKind::Interface(iface) => {
283 StmtKind::Interface(Box::new(fold_owned_interface_decl(folder, iface)))
284 }
285 StmtKind::Trait(tr) => StmtKind::Trait(Box::new(fold_owned_trait_decl(folder, tr))),
286 StmtKind::Enum(en) => StmtKind::Enum(Box::new(fold_owned_enum_decl(folder, en))),
287 StmtKind::Namespace(ns) => StmtKind::Namespace(Box::new(NamespaceDecl {
288 name: ns.name.as_ref().map(|n| folder.fold_name(n)),
289 body: match &ns.body {
290 NamespaceBody::Braced(block) => {
291 NamespaceBody::Braced(Box::new(folder.fold_block(block)))
292 }
293 NamespaceBody::Simple => NamespaceBody::Simple,
294 },
295 })),
296 StmtKind::Use(u) => StmtKind::Use(Box::new(UseDecl {
297 kind: u.kind,
298 uses: u
299 .uses
300 .iter()
301 .map(|item| UseItem {
302 name: folder.fold_name(&item.name),
303 alias: item.alias.clone(),
304 kind: item.kind,
305 span: item.span,
306 })
307 .collect(),
308 })),
309 StmtKind::Const(items) => StmtKind::Const(
310 items
311 .iter()
312 .map(|item| ConstItem {
313 name: item.name.clone(),
314 value: folder.fold_expr(&item.value),
315 attributes: fold_owned_attrs(folder, &item.attributes),
316 span: item.span,
317 doc_comment: item.doc_comment.clone(),
318 })
319 .collect(),
320 ),
321 StmtKind::StaticVar(vars) => StmtKind::StaticVar(
322 vars.iter()
323 .map(|v| StaticVar {
324 name: v.name.clone(),
325 default: v.default.as_ref().map(|e| folder.fold_expr(e)),
326 span: v.span,
327 })
328 .collect(),
329 ),
330 StmtKind::HaltCompiler(s) => StmtKind::HaltCompiler(s.clone()),
331 StmtKind::Nop => StmtKind::Nop,
332 StmtKind::InlineHtml(s) => StmtKind::InlineHtml(s.clone()),
333 StmtKind::Error => StmtKind::Error,
334 }
335}
336
337pub fn fold_owned_expr<F: FoldOwned + ?Sized>(folder: &mut F, expr: &Expr) -> Expr {
338 Expr {
339 kind: fold_owned_expr_kind(folder, &expr.kind),
340 span: expr.span,
341 }
342}
343
344fn fold_owned_expr_kind<F: FoldOwned + ?Sized>(folder: &mut F, k: &ExprKind) -> ExprKind {
345 match k {
346 ExprKind::Int(v) => ExprKind::Int(*v),
347 ExprKind::Float(v) => ExprKind::Float(*v),
348 ExprKind::String(s) => ExprKind::String(s.clone()),
349 ExprKind::InterpolatedString(parts) => {
350 ExprKind::InterpolatedString(fold_owned_string_parts(folder, parts))
351 }
352 ExprKind::Heredoc { label, parts } => ExprKind::Heredoc {
353 label: label.clone(),
354 parts: fold_owned_string_parts(folder, parts),
355 },
356 ExprKind::Nowdoc { label, value } => ExprKind::Nowdoc {
357 label: label.clone(),
358 value: value.clone(),
359 },
360 ExprKind::ShellExec(parts) => ExprKind::ShellExec(fold_owned_string_parts(folder, parts)),
361 ExprKind::Bool(v) => ExprKind::Bool(*v),
362 ExprKind::Null => ExprKind::Null,
363 ExprKind::Variable(s) => ExprKind::Variable(s.clone()),
364 ExprKind::VariableVariable(inner) => {
365 ExprKind::VariableVariable(Box::new(folder.fold_expr(inner)))
366 }
367 ExprKind::Identifier(s) => ExprKind::Identifier(s.clone()),
368 ExprKind::Assign(a) => ExprKind::Assign(AssignExpr {
369 target: Box::new(folder.fold_expr(&a.target)),
370 op: a.op,
371 value: Box::new(folder.fold_expr(&a.value)),
372 by_ref: a.by_ref,
373 }),
374 ExprKind::Binary(b) => ExprKind::Binary(BinaryExpr {
375 left: Box::new(folder.fold_expr(&b.left)),
376 op: b.op,
377 right: Box::new(folder.fold_expr(&b.right)),
378 }),
379 ExprKind::UnaryPrefix(u) => ExprKind::UnaryPrefix(UnaryPrefixExpr {
380 op: u.op,
381 operand: Box::new(folder.fold_expr(&u.operand)),
382 }),
383 ExprKind::UnaryPostfix(u) => ExprKind::UnaryPostfix(UnaryPostfixExpr {
384 operand: Box::new(folder.fold_expr(&u.operand)),
385 op: u.op,
386 }),
387 ExprKind::Ternary(t) => ExprKind::Ternary(TernaryExpr {
388 condition: Box::new(folder.fold_expr(&t.condition)),
389 then_expr: t.then_expr.as_ref().map(|e| Box::new(folder.fold_expr(e))),
390 else_expr: Box::new(folder.fold_expr(&t.else_expr)),
391 }),
392 ExprKind::NullCoalesce(n) => ExprKind::NullCoalesce(NullCoalesceExpr {
393 left: Box::new(folder.fold_expr(&n.left)),
394 right: Box::new(folder.fold_expr(&n.right)),
395 }),
396 ExprKind::FunctionCall(f) => ExprKind::FunctionCall(FunctionCallExpr {
397 name: Box::new(folder.fold_expr(&f.name)),
398 args: fold_owned_args(folder, &f.args),
399 }),
400 ExprKind::Array(elems) => ExprKind::Array(
401 elems
402 .iter()
403 .map(|e| ArrayElement {
404 key: e.key.as_ref().map(|k| folder.fold_expr(k)),
405 value: folder.fold_expr(&e.value),
406 unpack: e.unpack,
407 by_ref: e.by_ref,
408 span: e.span,
409 })
410 .collect(),
411 ),
412 ExprKind::ArrayAccess(a) => ExprKind::ArrayAccess(ArrayAccessExpr {
413 array: Box::new(folder.fold_expr(&a.array)),
414 index: a.index.as_ref().map(|e| Box::new(folder.fold_expr(e))),
415 }),
416 ExprKind::Print(e) => ExprKind::Print(Box::new(folder.fold_expr(e))),
417 ExprKind::Parenthesized(e) => ExprKind::Parenthesized(Box::new(folder.fold_expr(e))),
418 ExprKind::Cast(kind, e) => ExprKind::Cast(*kind, Box::new(folder.fold_expr(e))),
419 ExprKind::ErrorSuppress(e) => ExprKind::ErrorSuppress(Box::new(folder.fold_expr(e))),
420 ExprKind::Isset(exprs) => ExprKind::Isset(fold_owned_exprs(folder, exprs)),
421 ExprKind::Empty(e) => ExprKind::Empty(Box::new(folder.fold_expr(e))),
422 ExprKind::Include(kind, e) => ExprKind::Include(*kind, Box::new(folder.fold_expr(e))),
423 ExprKind::Eval(e) => ExprKind::Eval(Box::new(folder.fold_expr(e))),
424 ExprKind::Exit(e) => ExprKind::Exit(e.as_ref().map(|e| Box::new(folder.fold_expr(e)))),
425 ExprKind::MagicConst(m) => ExprKind::MagicConst(*m),
426 ExprKind::Clone(e) => ExprKind::Clone(Box::new(folder.fold_expr(e))),
427 ExprKind::CloneWith(obj, props) => ExprKind::CloneWith(
428 Box::new(folder.fold_expr(obj)),
429 Box::new(folder.fold_expr(props)),
430 ),
431 ExprKind::New(n) => ExprKind::New(NewExpr {
432 class: Box::new(folder.fold_expr(&n.class)),
433 args: fold_owned_args(folder, &n.args),
434 }),
435 ExprKind::PropertyAccess(p) => ExprKind::PropertyAccess(PropertyAccessExpr {
436 object: Box::new(folder.fold_expr(&p.object)),
437 property: Box::new(folder.fold_expr(&p.property)),
438 }),
439 ExprKind::NullsafePropertyAccess(p) => {
440 ExprKind::NullsafePropertyAccess(PropertyAccessExpr {
441 object: Box::new(folder.fold_expr(&p.object)),
442 property: Box::new(folder.fold_expr(&p.property)),
443 })
444 }
445 ExprKind::MethodCall(m) => ExprKind::MethodCall(Box::new(MethodCallExpr {
446 object: Box::new(folder.fold_expr(&m.object)),
447 method: Box::new(folder.fold_expr(&m.method)),
448 args: fold_owned_args(folder, &m.args),
449 })),
450 ExprKind::NullsafeMethodCall(m) => ExprKind::NullsafeMethodCall(Box::new(MethodCallExpr {
451 object: Box::new(folder.fold_expr(&m.object)),
452 method: Box::new(folder.fold_expr(&m.method)),
453 args: fold_owned_args(folder, &m.args),
454 })),
455 ExprKind::StaticPropertyAccess(s) => ExprKind::StaticPropertyAccess(StaticAccessExpr {
456 class: Box::new(folder.fold_expr(&s.class)),
457 member: Box::new(folder.fold_expr(&s.member)),
458 }),
459 ExprKind::StaticMethodCall(s) => {
460 ExprKind::StaticMethodCall(Box::new(StaticMethodCallExpr {
461 class: Box::new(folder.fold_expr(&s.class)),
462 method: Box::new(folder.fold_expr(&s.method)),
463 args: fold_owned_args(folder, &s.args),
464 }))
465 }
466 ExprKind::StaticDynMethodCall(s) => {
467 ExprKind::StaticDynMethodCall(Box::new(StaticDynMethodCallExpr {
468 class: Box::new(folder.fold_expr(&s.class)),
469 method: Box::new(folder.fold_expr(&s.method)),
470 args: fold_owned_args(folder, &s.args),
471 }))
472 }
473 ExprKind::ClassConstAccess(s) => ExprKind::ClassConstAccess(StaticAccessExpr {
474 class: Box::new(folder.fold_expr(&s.class)),
475 member: Box::new(folder.fold_expr(&s.member)),
476 }),
477 ExprKind::ClassConstAccessDynamic { class, member } => ExprKind::ClassConstAccessDynamic {
478 class: Box::new(folder.fold_expr(class)),
479 member: Box::new(folder.fold_expr(member)),
480 },
481 ExprKind::StaticPropertyAccessDynamic { class, member } => {
482 ExprKind::StaticPropertyAccessDynamic {
483 class: Box::new(folder.fold_expr(class)),
484 member: Box::new(folder.fold_expr(member)),
485 }
486 }
487 ExprKind::Closure(c) => ExprKind::Closure(Box::new(ClosureExpr {
488 is_static: c.is_static,
489 by_ref: c.by_ref,
490 params: fold_owned_params(folder, &c.params),
491 use_vars: c
492 .use_vars
493 .iter()
494 .map(|v| folder.fold_closure_use_var(v))
495 .collect(),
496 return_type: c.return_type.as_ref().map(|t| folder.fold_type_hint(t)),
497 body: Box::new(folder.fold_block(&c.body)),
498 attributes: fold_owned_attrs(folder, &c.attributes),
499 })),
500 ExprKind::ArrowFunction(f) => ExprKind::ArrowFunction(Box::new(ArrowFunctionExpr {
501 is_static: f.is_static,
502 by_ref: f.by_ref,
503 params: fold_owned_params(folder, &f.params),
504 return_type: f.return_type.as_ref().map(|t| folder.fold_type_hint(t)),
505 body: Box::new(folder.fold_expr(&f.body)),
506 attributes: fold_owned_attrs(folder, &f.attributes),
507 })),
508 ExprKind::Match(m) => ExprKind::Match(MatchExpr {
509 subject: Box::new(folder.fold_expr(&m.subject)),
510 arms: m
511 .arms
512 .iter()
513 .map(|arm| folder.fold_match_arm(arm))
514 .collect(),
515 brace_start: m.brace_start,
516 }),
517 ExprKind::ThrowExpr(e) => ExprKind::ThrowExpr(Box::new(folder.fold_expr(e))),
518 ExprKind::Yield(y) => ExprKind::Yield(YieldExpr {
519 key: y.key.as_ref().map(|e| Box::new(folder.fold_expr(e))),
520 value: y.value.as_ref().map(|e| Box::new(folder.fold_expr(e))),
521 is_from: y.is_from,
522 }),
523 ExprKind::AnonymousClass(cls) => {
524 ExprKind::AnonymousClass(Box::new(fold_owned_class_decl(folder, cls)))
525 }
526 ExprKind::CallableCreate(c) => ExprKind::CallableCreate(CallableCreateExpr {
527 kind: match &c.kind {
528 CallableCreateKind::Function(e) => {
529 CallableCreateKind::Function(Box::new(folder.fold_expr(e)))
530 }
531 CallableCreateKind::Method { object, method } => CallableCreateKind::Method {
532 object: Box::new(folder.fold_expr(object)),
533 method: Box::new(folder.fold_expr(method)),
534 },
535 CallableCreateKind::NullsafeMethod { object, method } => {
536 CallableCreateKind::NullsafeMethod {
537 object: Box::new(folder.fold_expr(object)),
538 method: Box::new(folder.fold_expr(method)),
539 }
540 }
541 CallableCreateKind::StaticMethod { class, method } => {
542 CallableCreateKind::StaticMethod {
543 class: Box::new(folder.fold_expr(class)),
544 method: Box::new(folder.fold_expr(method)),
545 }
546 }
547 },
548 }),
549 ExprKind::Omit => ExprKind::Omit,
550 ExprKind::Error => ExprKind::Error,
551 }
552}
553
554pub fn fold_owned_param<F: FoldOwned + ?Sized>(folder: &mut F, p: &Param) -> Param {
555 Param {
556 name: p.name.clone(),
557 type_hint: p.type_hint.as_ref().map(|t| folder.fold_type_hint(t)),
558 default: p.default.as_ref().map(|e| folder.fold_expr(e)),
559 by_ref: p.by_ref,
560 variadic: p.variadic,
561 is_readonly: p.is_readonly,
562 is_final: p.is_final,
563 visibility: p.visibility,
564 set_visibility: p.set_visibility,
565 attributes: fold_owned_attrs(folder, &p.attributes),
566 hooks: fold_owned_hooks(folder, &p.hooks),
567 doc_comment: p.doc_comment.clone(),
568 span: p.span,
569 }
570}
571
572pub fn fold_owned_arg<F: FoldOwned + ?Sized>(folder: &mut F, arg: &Arg) -> Arg {
573 Arg {
574 name: arg.name.as_ref().map(|n| folder.fold_name(n)),
575 value: arg.value.as_ref().map(|v| folder.fold_expr(v)),
576 unpack: arg.unpack,
577 by_ref: arg.by_ref,
578 span: arg.span,
579 }
580}
581
582pub fn fold_owned_closure_use_var<F: FoldOwned + ?Sized>(
583 _folder: &mut F,
584 var: &ClosureUseVar,
585) -> ClosureUseVar {
586 var.clone()
587}
588
589pub fn fold_owned_name<F: FoldOwned + ?Sized>(_folder: &mut F, name: &Name) -> Name {
590 name.clone()
591}
592
593pub fn fold_owned_class_member<F: FoldOwned + ?Sized>(
594 folder: &mut F,
595 member: &ClassMember,
596) -> ClassMember {
597 ClassMember {
598 kind: match &member.kind {
599 ClassMemberKind::Property(p) => ClassMemberKind::Property(PropertyDecl {
600 name: p.name.clone(),
601 visibility: p.visibility,
602 set_visibility: p.set_visibility,
603 is_static: p.is_static,
604 is_readonly: p.is_readonly,
605 type_hint: p.type_hint.as_ref().map(|t| folder.fold_type_hint(t)),
606 default: p.default.as_ref().map(|e| folder.fold_expr(e)),
607 attributes: fold_owned_attrs(folder, &p.attributes),
608 hooks: fold_owned_hooks(folder, &p.hooks),
609 doc_comment: p.doc_comment.clone(),
610 }),
611 ClassMemberKind::Method(m) => ClassMemberKind::Method(MethodDecl {
612 name: m.name.clone(),
613 visibility: m.visibility,
614 is_static: m.is_static,
615 is_abstract: m.is_abstract,
616 is_final: m.is_final,
617 by_ref: m.by_ref,
618 params: fold_owned_params(folder, &m.params),
619 return_type: m.return_type.as_ref().map(|t| folder.fold_type_hint(t)),
620 body: m.body.as_ref().map(|b| Box::new(folder.fold_block(b))),
621 attributes: fold_owned_attrs(folder, &m.attributes),
622 doc_comment: m.doc_comment.clone(),
623 }),
624 ClassMemberKind::ClassConst(c) => {
625 ClassMemberKind::ClassConst(fold_owned_class_const(folder, c))
626 }
627 ClassMemberKind::TraitUse(t) => {
628 ClassMemberKind::TraitUse(fold_owned_trait_use(folder, t))
629 }
630 },
631 span: member.span,
632 }
633}
634
635pub fn fold_owned_enum_member<F: FoldOwned + ?Sized>(
636 folder: &mut F,
637 member: &EnumMember,
638) -> EnumMember {
639 EnumMember {
640 kind: match &member.kind {
641 EnumMemberKind::Case(c) => EnumMemberKind::Case(EnumCase {
642 name: c.name.clone(),
643 value: c.value.as_ref().map(|e| folder.fold_expr(e)),
644 attributes: fold_owned_attrs(folder, &c.attributes),
645 doc_comment: c.doc_comment.clone(),
646 }),
647 EnumMemberKind::Method(m) => EnumMemberKind::Method(MethodDecl {
648 name: m.name.clone(),
649 visibility: m.visibility,
650 is_static: m.is_static,
651 is_abstract: m.is_abstract,
652 is_final: m.is_final,
653 by_ref: m.by_ref,
654 params: fold_owned_params(folder, &m.params),
655 return_type: m.return_type.as_ref().map(|t| folder.fold_type_hint(t)),
656 body: m.body.as_ref().map(|b| Box::new(folder.fold_block(b))),
657 attributes: fold_owned_attrs(folder, &m.attributes),
658 doc_comment: m.doc_comment.clone(),
659 }),
660 EnumMemberKind::ClassConst(c) => {
661 EnumMemberKind::ClassConst(fold_owned_class_const(folder, c))
662 }
663 EnumMemberKind::TraitUse(t) => {
664 EnumMemberKind::TraitUse(fold_owned_trait_use(folder, t))
665 }
666 },
667 span: member.span,
668 }
669}
670
671pub fn fold_owned_property_hook<F: FoldOwned + ?Sized>(
672 folder: &mut F,
673 hook: &PropertyHook,
674) -> PropertyHook {
675 PropertyHook {
676 kind: hook.kind,
677 body: match &hook.body {
678 PropertyHookBody::Block(block) => {
679 PropertyHookBody::Block(Box::new(folder.fold_block(block)))
680 }
681 PropertyHookBody::Expression(e) => PropertyHookBody::Expression(folder.fold_expr(e)),
682 PropertyHookBody::Abstract => PropertyHookBody::Abstract,
683 },
684 is_final: hook.is_final,
685 by_ref: hook.by_ref,
686 params: fold_owned_params(folder, &hook.params),
687 attributes: fold_owned_attrs(folder, &hook.attributes),
688 span: hook.span,
689 }
690}
691
692pub fn fold_owned_type_hint<F: FoldOwned + ?Sized>(
693 folder: &mut F,
694 type_hint: &TypeHint,
695) -> TypeHint {
696 TypeHint {
697 kind: match &type_hint.kind {
698 TypeHintKind::Named(n) => TypeHintKind::Named(folder.fold_name(n)),
699 TypeHintKind::Keyword(b, span) => TypeHintKind::Keyword(*b, *span),
700 TypeHintKind::Nullable(inner) => {
701 TypeHintKind::Nullable(Box::new(folder.fold_type_hint(inner)))
702 }
703 TypeHintKind::Union(types) => {
704 TypeHintKind::Union(types.iter().map(|t| folder.fold_type_hint(t)).collect())
705 }
706 TypeHintKind::Intersection(types) => {
707 TypeHintKind::Intersection(types.iter().map(|t| folder.fold_type_hint(t)).collect())
708 }
709 },
710 span: type_hint.span,
711 }
712}
713
714pub fn fold_owned_attribute<F: FoldOwned + ?Sized>(
715 folder: &mut F,
716 attribute: &Attribute,
717) -> Attribute {
718 Attribute {
719 name: folder.fold_name(&attribute.name),
720 args: fold_owned_args(folder, &attribute.args),
721 span: attribute.span,
722 }
723}
724
725pub fn fold_owned_catch_clause<F: FoldOwned + ?Sized>(
726 folder: &mut F,
727 catch: &CatchClause,
728) -> CatchClause {
729 CatchClause {
730 types: catch.types.iter().map(|n| folder.fold_name(n)).collect(),
731 var: catch.var.clone(),
732 body: Box::new(folder.fold_block(&catch.body)),
733 span: catch.span,
734 }
735}
736
737pub fn fold_owned_match_arm<F: FoldOwned + ?Sized>(folder: &mut F, arm: &MatchArm) -> MatchArm {
738 MatchArm {
739 conditions: arm
740 .conditions
741 .as_ref()
742 .map(|conds| fold_owned_exprs(folder, conds)),
743 body: folder.fold_expr(&arm.body),
744 span: arm.span,
745 }
746}
747
748fn fold_owned_class_const<F: FoldOwned + ?Sized>(
749 folder: &mut F,
750 c: &ClassConstDecl,
751) -> ClassConstDecl {
752 ClassConstDecl {
753 name: c.name.clone(),
754 visibility: c.visibility,
755 is_final: c.is_final,
756 type_hint: c
757 .type_hint
758 .as_ref()
759 .map(|th| Box::new(folder.fold_type_hint(th))),
760 value: folder.fold_expr(&c.value),
761 attributes: fold_owned_attrs(folder, &c.attributes),
762 doc_comment: c.doc_comment.clone(),
763 }
764}
765
766fn fold_owned_trait_use<F: FoldOwned + ?Sized>(folder: &mut F, t: &TraitUseDecl) -> TraitUseDecl {
767 TraitUseDecl {
768 traits: t.traits.iter().map(|n| folder.fold_name(n)).collect(),
769 adaptations: t
770 .adaptations
771 .iter()
772 .map(|a| TraitAdaptation {
773 kind: match &a.kind {
774 TraitAdaptationKind::Precedence {
775 trait_name,
776 method,
777 insteadof,
778 } => TraitAdaptationKind::Precedence {
779 trait_name: folder.fold_name(trait_name),
780 method: folder.fold_name(method),
781 insteadof: insteadof.iter().map(|n| folder.fold_name(n)).collect(),
782 },
783 TraitAdaptationKind::Alias {
784 trait_name,
785 method,
786 new_modifier,
787 new_name,
788 } => TraitAdaptationKind::Alias {
789 trait_name: trait_name.as_ref().map(|n| folder.fold_name(n)),
790 method: folder.fold_name(method),
791 new_modifier: *new_modifier,
792 new_name: new_name.as_ref().map(|n| folder.fold_name(n)),
793 },
794 },
795 span: a.span,
796 })
797 .collect(),
798 adaptations_brace_start: t.adaptations_brace_start,
799 doc_comment: t.doc_comment.clone(),
800 }
801}
802
803fn fold_owned_class_decl<F: FoldOwned + ?Sized>(folder: &mut F, cls: &ClassDecl) -> ClassDecl {
804 ClassDecl {
805 name: cls.name.clone(),
806 modifiers: cls.modifiers.clone(),
807 extends: cls.extends.as_ref().map(|n| folder.fold_name(n)),
808 implements: cls.implements.iter().map(|n| folder.fold_name(n)).collect(),
809 body: ClassBody {
810 members: fold_owned_members(folder, &cls.body.members),
811 span: cls.body.span,
812 },
813 attributes: fold_owned_attrs(folder, &cls.attributes),
814 doc_comment: cls.doc_comment.clone(),
815 }
816}
817
818fn fold_owned_interface_decl<F: FoldOwned + ?Sized>(
819 folder: &mut F,
820 iface: &InterfaceDecl,
821) -> InterfaceDecl {
822 InterfaceDecl {
823 name: iface.name.clone(),
824 extends: iface.extends.iter().map(|n| folder.fold_name(n)).collect(),
825 body: ClassBody {
826 members: fold_owned_members(folder, &iface.body.members),
827 span: iface.body.span,
828 },
829 attributes: fold_owned_attrs(folder, &iface.attributes),
830 doc_comment: iface.doc_comment.clone(),
831 }
832}
833
834fn fold_owned_trait_decl<F: FoldOwned + ?Sized>(folder: &mut F, tr: &TraitDecl) -> TraitDecl {
835 TraitDecl {
836 name: tr.name.clone(),
837 body: ClassBody {
838 members: fold_owned_members(folder, &tr.body.members),
839 span: tr.body.span,
840 },
841 attributes: fold_owned_attrs(folder, &tr.attributes),
842 doc_comment: tr.doc_comment.clone(),
843 }
844}
845
846fn fold_owned_enum_decl<F: FoldOwned + ?Sized>(folder: &mut F, en: &EnumDecl) -> EnumDecl {
847 EnumDecl {
848 name: en.name.clone(),
849 scalar_type: en.scalar_type.as_ref().map(|n| folder.fold_name(n)),
850 implements: en.implements.iter().map(|n| folder.fold_name(n)).collect(),
851 body: EnumBody {
852 members: en
853 .body
854 .members
855 .iter()
856 .map(|m| folder.fold_enum_member(m))
857 .collect(),
858 span: en.body.span,
859 },
860 attributes: fold_owned_attrs(folder, &en.attributes),
861 doc_comment: en.doc_comment.clone(),
862 }
863}
864
865#[cfg(test)]
870mod tests {
871 use super::*;
872 use crate::ast::AssignOp;
873 use crate::Span;
874
875 fn dummy_var(name: &str) -> Expr {
876 Expr {
877 kind: ExprKind::Variable(Box::from(name)),
878 span: Span::DUMMY,
879 }
880 }
881
882 fn dummy_int(n: i64) -> Expr {
883 Expr {
884 kind: ExprKind::Int(n),
885 span: Span::DUMMY,
886 }
887 }
888
889 fn assign(target: Expr, value: Expr) -> Expr {
890 Expr {
891 kind: ExprKind::Assign(AssignExpr {
892 target: Box::new(target),
893 op: AssignOp::Assign,
894 value: Box::new(value),
895 by_ref: false,
896 }),
897 span: Span::DUMMY,
898 }
899 }
900
901 fn expr_stmt(e: Expr) -> Stmt {
902 Stmt {
903 kind: StmtKind::Expression(Box::new(e)),
904 span: Span::DUMMY,
905 doc_comment: None,
906 }
907 }
908
909 fn empty_block() -> Block {
910 Block {
911 stmts: Box::from([]),
912 span: Span::DUMMY,
913 }
914 }
915
916 fn program(stmts: impl IntoIterator<Item = Stmt>) -> Program {
917 Program {
918 stmts: stmts.into_iter().collect(),
919 span: Span::DUMMY,
920 }
921 }
922
923 #[test]
925 fn identity_fold_roundtrip() {
926 let p = program([expr_stmt(assign(dummy_var("x"), dummy_var("y")))]);
927 struct Identity;
928 impl FoldOwned for Identity {}
929 let folded = Identity.fold_program(&p);
930 assert_eq!(
931 serde_json::to_string(&folded).unwrap(),
932 serde_json::to_string(&p).unwrap(),
933 );
934 }
935
936 #[test]
938 fn negate_ints() {
939 let p = program([expr_stmt(assign(dummy_var("x"), dummy_int(42)))]);
940 struct NegateInts;
941 impl FoldOwned for NegateInts {
942 fn fold_expr(&mut self, expr: &Expr) -> Expr {
943 if let ExprKind::Int(n) = &expr.kind {
944 return Expr {
945 kind: ExprKind::Int(-n),
946 span: expr.span,
947 };
948 }
949 fold_owned_expr(self, expr)
950 }
951 }
952 let folded = NegateInts.fold_program(&p);
953 let stmt = &folded.stmts[0];
954 if let StmtKind::Expression(expr) = &stmt.kind {
955 if let ExprKind::Assign(a) = &expr.kind {
956 if let ExprKind::Int(n) = &a.value.kind {
957 assert_eq!(*n, -42);
958 return;
959 }
960 }
961 }
962 panic!("expected negated int assignment");
963 }
964
965 #[test]
967 fn rename_variable() {
968 let p = program([expr_stmt(assign(dummy_var("x"), dummy_var("x")))]);
970 struct Rename;
971 impl FoldOwned for Rename {
972 fn fold_expr(&mut self, expr: &Expr) -> Expr {
973 if let ExprKind::Variable(name) = &expr.kind {
974 if name.as_ref() == "x" {
975 return Expr {
976 kind: ExprKind::Variable(Box::from("renamed")),
977 span: expr.span,
978 };
979 }
980 }
981 fold_owned_expr(self, expr)
982 }
983 }
984 let folded = Rename.fold_program(&p);
985 let stmt = &folded.stmts[0];
986 if let StmtKind::Expression(expr) = &stmt.kind {
987 if let ExprKind::Assign(a) = &expr.kind {
988 assert!(matches!(&a.target.kind, ExprKind::Variable(n) if n.as_ref() == "renamed"));
989 assert!(matches!(&a.value.kind, ExprKind::Variable(n) if n.as_ref() == "renamed"));
990 return;
991 }
992 }
993 panic!("expected renamed variable assignment");
994 }
995
996 #[test]
998 fn rename_variable_in_closure_use() {
999 let closure = Expr {
1001 kind: ExprKind::Closure(Box::new(ClosureExpr {
1002 is_static: false,
1003 by_ref: false,
1004 params: Box::from([]),
1005 use_vars: Box::from([ClosureUseVar {
1006 name: Box::from("x"),
1007 by_ref: false,
1008 span: Span::DUMMY,
1009 }]),
1010 return_type: None,
1011 body: Box::new(empty_block()),
1012 attributes: Box::from([]),
1013 })),
1014 span: Span::DUMMY,
1015 };
1016 let p = program([expr_stmt(closure)]);
1017
1018 struct Rename;
1019 impl FoldOwned for Rename {
1020 fn fold_closure_use_var(&mut self, var: &ClosureUseVar) -> ClosureUseVar {
1021 ClosureUseVar {
1022 name: if var.name.as_ref() == "x" {
1023 Box::from("renamed")
1024 } else {
1025 var.name.clone()
1026 },
1027 by_ref: var.by_ref,
1028 span: var.span,
1029 }
1030 }
1031 }
1032
1033 let folded = Rename.fold_program(&p);
1034 if let StmtKind::Expression(expr) = &folded.stmts[0].kind {
1035 if let ExprKind::Closure(c) = &expr.kind {
1036 assert_eq!(c.use_vars[0].name.as_ref(), "renamed");
1037 return;
1038 }
1039 }
1040 panic!("expected closure with renamed use-var");
1041 }
1042}