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