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