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