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