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