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 key_by_ref: s.key_by_ref,
319 value: folder.fold_expr(s.value),
320 by_ref: s.by_ref,
321 by_ref_span: s.by_ref_span,
322 body: fold_boxed_stmt(folder, s.body),
323 uses_alternative: s.uses_alternative,
324 }))
325 }
326 StmtKind::DoWhile(s) => {
327 let s = *s;
328 StmtKind::DoWhile(Box::new(DoWhileStmt {
329 body: fold_boxed_stmt(folder, s.body),
330 condition: folder.fold_expr(s.condition),
331 }))
332 }
333 StmtKind::Function(f) => {
334 let f = *f;
335 StmtKind::Function(Box::new(FunctionDecl {
336 name: f.name,
337 params: fold_owned_params(folder, f.params),
338 missing_param_list: f.missing_param_list,
339 body: fold_boxed_block(folder, f.body),
340 return_type: f.return_type.map(|t| folder.fold_type_hint(t)),
341 by_ref: f.by_ref,
342 attributes: fold_owned_attrs(folder, f.attributes),
343 doc_comment: f.doc_comment,
344 }))
345 }
346 StmtKind::Break(e) => StmtKind::Break(fold_opt_boxed_expr(folder, e)),
347 StmtKind::Continue(e) => StmtKind::Continue(fold_opt_boxed_expr(folder, e)),
348 StmtKind::Switch(s) => {
349 let s = *s;
350 StmtKind::Switch(Box::new(SwitchStmt {
351 expr: folder.fold_expr(s.expr),
352 body: SwitchBody {
353 cases: map_slice(s.body.cases, |c| SwitchCase {
354 value: c.value.map(|v| folder.fold_expr(v)),
355 body: folder.fold_stmts(c.body),
356 span: c.span,
357 }),
358 span: s.body.span,
359 },
360 uses_alternative: s.uses_alternative,
361 }))
362 }
363 StmtKind::Goto(ident) => StmtKind::Goto(ident),
364 StmtKind::Label(ident) => StmtKind::Label(ident),
365 StmtKind::Declare(d) => {
366 let d = *d;
367 StmtKind::Declare(Box::new(DeclareStmt {
368 directives: map_slice(d.directives, |(k, v)| (k, folder.fold_expr(v))),
369 body: d.body.map(|b| fold_boxed_stmt(folder, b)),
370 uses_alternative: d.uses_alternative,
371 }))
372 }
373 StmtKind::Unset(exprs) => StmtKind::Unset(fold_owned_exprs(folder, exprs)),
374 StmtKind::Throw(e) => StmtKind::Throw(fold_boxed_expr(folder, e)),
375 StmtKind::TryCatch(t) => {
376 let t = *t;
377 StmtKind::TryCatch(Box::new(TryCatchStmt {
378 body: fold_boxed_block(folder, t.body),
379 catches: map_slice(t.catches, |c| folder.fold_catch_clause(c)),
380 finally: t.finally.map(|f| fold_boxed_block(folder, f)),
381 finally_kw_span: t.finally_kw_span,
382 }))
383 }
384 StmtKind::Global(exprs) => StmtKind::Global(fold_owned_exprs(folder, exprs)),
385 StmtKind::Class(cls) => StmtKind::Class(Box::new(fold_owned_class_decl(folder, *cls))),
386 StmtKind::Interface(iface) => {
387 StmtKind::Interface(Box::new(fold_owned_interface_decl(folder, *iface)))
388 }
389 StmtKind::Trait(tr) => StmtKind::Trait(Box::new(fold_owned_trait_decl(folder, *tr))),
390 StmtKind::Enum(en) => StmtKind::Enum(Box::new(fold_owned_enum_decl(folder, *en))),
391 StmtKind::Namespace(ns) => {
392 let ns = *ns;
393 StmtKind::Namespace(Box::new(NamespaceDecl {
394 name: ns.name.map(|n| folder.fold_name(n)),
395 body: match ns.body {
396 NamespaceBody::Braced(block) => {
397 NamespaceBody::Braced(fold_boxed_block(folder, block))
398 }
399 NamespaceBody::Simple => NamespaceBody::Simple,
400 },
401 }))
402 }
403 StmtKind::Use(u) => {
404 let u = *u;
405 StmtKind::Use(Box::new(UseDecl {
406 kind: u.kind,
407 uses: map_slice(u.uses, |item| UseItem {
408 name: folder.fold_name(item.name),
409 alias: item.alias,
410 kind: item.kind,
411 span: item.span,
412 }),
413 }))
414 }
415 StmtKind::Const(items) => StmtKind::Const(map_slice(items, |item| ConstItem {
416 name: item.name,
417 value: folder.fold_expr(item.value),
418 attributes: fold_owned_attrs(folder, item.attributes),
419 span: item.span,
420 doc_comment: item.doc_comment,
421 })),
422 StmtKind::StaticVar(vars) => StmtKind::StaticVar(map_slice(vars, |v| StaticVar {
423 name: v.name,
424 default: v.default.map(|e| folder.fold_expr(e)),
425 span: v.span,
426 })),
427 StmtKind::HaltCompiler(s) => StmtKind::HaltCompiler(s),
428 StmtKind::Nop => StmtKind::Nop,
429 StmtKind::InlineHtml(s) => StmtKind::InlineHtml(s),
430 StmtKind::Error => StmtKind::Error,
431 }
432}
433
434pub fn fold_owned_expr<F: OwnedFold + ?Sized>(folder: &mut F, expr: Expr) -> Expr {
436 let span = expr.span;
437 Expr {
438 kind: fold_owned_expr_kind(folder, expr.into_kind()),
439 span,
440 }
441}
442
443fn fold_owned_expr_kind<F: OwnedFold + ?Sized>(folder: &mut F, k: ExprKind) -> ExprKind {
444 match k {
445 ExprKind::Int(v) => ExprKind::Int(v),
446 ExprKind::Float(v) => ExprKind::Float(v),
447 ExprKind::String(s) => ExprKind::String(s),
448 ExprKind::InterpolatedString(parts) => {
449 ExprKind::InterpolatedString(fold_owned_string_parts(folder, parts))
450 }
451 ExprKind::Heredoc { label, parts } => ExprKind::Heredoc {
452 label,
453 parts: fold_owned_string_parts(folder, parts),
454 },
455 ExprKind::Nowdoc { label, value } => ExprKind::Nowdoc { label, value },
456 ExprKind::ShellExec(parts) => ExprKind::ShellExec(fold_owned_string_parts(folder, parts)),
457 ExprKind::Bool(v) => ExprKind::Bool(v),
458 ExprKind::Null => ExprKind::Null,
459 ExprKind::Variable(s) => ExprKind::Variable(s),
460 ExprKind::VariableVariable(inner) => {
461 ExprKind::VariableVariable(fold_boxed_expr(folder, inner))
462 }
463 ExprKind::Identifier(s) => ExprKind::Identifier(s),
464 ExprKind::Name(name) => ExprKind::Name(folder.fold_name(name)),
465 ExprKind::Assign(a) => ExprKind::Assign(AssignExpr {
466 target: fold_boxed_expr(folder, a.target),
467 op: a.op,
468 value: fold_boxed_expr(folder, a.value),
469 by_ref: a.by_ref,
470 }),
471 ExprKind::Binary(b) => ExprKind::Binary(BinaryExpr {
472 left: fold_boxed_expr(folder, b.left),
473 op: b.op,
474 right: fold_boxed_expr(folder, b.right),
475 }),
476 ExprKind::UnaryPrefix(u) => ExprKind::UnaryPrefix(UnaryPrefixExpr {
477 op: u.op,
478 operand: fold_boxed_expr(folder, u.operand),
479 }),
480 ExprKind::UnaryPostfix(u) => ExprKind::UnaryPostfix(UnaryPostfixExpr {
481 operand: fold_boxed_expr(folder, u.operand),
482 op: u.op,
483 }),
484 ExprKind::Ternary(t) => ExprKind::Ternary(TernaryExpr {
485 condition: fold_boxed_expr(folder, t.condition),
486 then_expr: fold_opt_boxed_expr(folder, t.then_expr),
487 else_expr: fold_boxed_expr(folder, t.else_expr),
488 }),
489 ExprKind::NullCoalesce(n) => ExprKind::NullCoalesce(NullCoalesceExpr {
490 left: fold_boxed_expr(folder, n.left),
491 right: fold_boxed_expr(folder, n.right),
492 }),
493 ExprKind::FunctionCall(f) => ExprKind::FunctionCall(FunctionCallExpr {
494 name: fold_boxed_expr(folder, f.name),
495 args: fold_owned_args(folder, f.args),
496 }),
497 ExprKind::Array(elems) => ExprKind::Array(map_slice(elems, |e| ArrayElement {
498 key: e.key.map(|k| folder.fold_expr(k)),
499 kind: match e.kind {
500 ArrayElementKind::Value(v) => ArrayElementKind::Value(folder.fold_expr(v)),
501 ArrayElementKind::Spread(v) => ArrayElementKind::Spread(folder.fold_expr(v)),
502 ArrayElementKind::ByRef(v) => ArrayElementKind::ByRef(folder.fold_expr(v)),
503 },
504 span: e.span,
505 })),
506 ExprKind::ArrayAccess(a) => ExprKind::ArrayAccess(ArrayAccessExpr {
507 array: fold_boxed_expr(folder, a.array),
508 index: fold_opt_boxed_expr(folder, a.index),
509 }),
510 ExprKind::Print(e) => ExprKind::Print(fold_boxed_expr(folder, e)),
511 ExprKind::Parenthesized(e) => ExprKind::Parenthesized(fold_boxed_expr(folder, e)),
512 ExprKind::Cast(kind, e) => ExprKind::Cast(kind, fold_boxed_expr(folder, e)),
513 ExprKind::ErrorSuppress(e) => ExprKind::ErrorSuppress(fold_boxed_expr(folder, e)),
514 ExprKind::Isset(exprs) => ExprKind::Isset(fold_owned_exprs(folder, exprs)),
515 ExprKind::Empty(e) => ExprKind::Empty(fold_boxed_expr(folder, e)),
516 ExprKind::Include(kind, e) => ExprKind::Include(kind, fold_boxed_expr(folder, e)),
517 ExprKind::Eval(e) => ExprKind::Eval(fold_boxed_expr(folder, e)),
518 ExprKind::Exit(e) => ExprKind::Exit(fold_opt_boxed_expr(folder, e)),
519 ExprKind::MagicConst(m) => ExprKind::MagicConst(m),
520 ExprKind::Clone(e) => ExprKind::Clone(fold_boxed_expr(folder, e)),
521 ExprKind::CloneWith(obj, props) => {
522 ExprKind::CloneWith(fold_boxed_expr(folder, obj), fold_boxed_expr(folder, props))
523 }
524 ExprKind::New(n) => ExprKind::New(NewExpr {
525 class: fold_boxed_expr(folder, n.class),
526 args: fold_owned_args(folder, n.args),
527 }),
528 ExprKind::PropertyAccess(p) => ExprKind::PropertyAccess(PropertyAccessExpr {
529 object: fold_boxed_expr(folder, p.object),
530 property: fold_boxed_expr(folder, p.property),
531 }),
532 ExprKind::NullsafePropertyAccess(p) => {
533 ExprKind::NullsafePropertyAccess(PropertyAccessExpr {
534 object: fold_boxed_expr(folder, p.object),
535 property: fold_boxed_expr(folder, p.property),
536 })
537 }
538 ExprKind::MethodCall(m) => {
539 let m = *m;
540 ExprKind::MethodCall(Box::new(MethodCallExpr {
541 object: fold_boxed_expr(folder, m.object),
542 method: fold_boxed_expr(folder, m.method),
543 args: fold_owned_args(folder, m.args),
544 }))
545 }
546 ExprKind::NullsafeMethodCall(m) => {
547 let m = *m;
548 ExprKind::NullsafeMethodCall(Box::new(MethodCallExpr {
549 object: fold_boxed_expr(folder, m.object),
550 method: fold_boxed_expr(folder, m.method),
551 args: fold_owned_args(folder, m.args),
552 }))
553 }
554 ExprKind::StaticPropertyAccess(s) => ExprKind::StaticPropertyAccess(StaticAccessExpr {
555 class: fold_boxed_expr(folder, s.class),
556 member: fold_boxed_expr(folder, s.member),
557 }),
558 ExprKind::StaticMethodCall(s) => {
559 let s = *s;
560 ExprKind::StaticMethodCall(Box::new(StaticMethodCallExpr {
561 class: fold_boxed_expr(folder, s.class),
562 method: fold_boxed_expr(folder, s.method),
563 args: fold_owned_args(folder, s.args),
564 }))
565 }
566 ExprKind::StaticMethodCallDynamic(s) => {
567 let s = *s;
568 ExprKind::StaticMethodCallDynamic(Box::new(StaticMethodCallDynamicExpr {
569 class: fold_boxed_expr(folder, s.class),
570 method: fold_boxed_expr(folder, s.method),
571 args: fold_owned_args(folder, s.args),
572 }))
573 }
574 ExprKind::ClassConstAccess(s) => ExprKind::ClassConstAccess(StaticAccessExpr {
575 class: fold_boxed_expr(folder, s.class),
576 member: fold_boxed_expr(folder, s.member),
577 }),
578 ExprKind::ClassConstAccessDynamic { class, member } => ExprKind::ClassConstAccessDynamic {
579 class: fold_boxed_expr(folder, class),
580 member: fold_boxed_expr(folder, member),
581 },
582 ExprKind::StaticPropertyAccessDynamic { class, member } => {
583 ExprKind::StaticPropertyAccessDynamic {
584 class: fold_boxed_expr(folder, class),
585 member: fold_boxed_expr(folder, member),
586 }
587 }
588 ExprKind::Closure(c) => {
589 let c = *c;
590 ExprKind::Closure(Box::new(ClosureExpr {
591 is_static: c.is_static,
592 by_ref: c.by_ref,
593 params: fold_owned_params(folder, c.params),
594 missing_param_list: c.missing_param_list,
595 use_vars: map_slice(c.use_vars, |v| folder.fold_closure_use_var(v)),
596 return_type: c.return_type.map(|t| folder.fold_type_hint(t)),
597 body: fold_boxed_block(folder, c.body),
598 attributes: fold_owned_attrs(folder, c.attributes),
599 }))
600 }
601 ExprKind::ArrowFunction(f) => {
602 let f = *f;
603 ExprKind::ArrowFunction(Box::new(ArrowFunctionExpr {
604 is_static: f.is_static,
605 by_ref: f.by_ref,
606 params: fold_owned_params(folder, f.params),
607 missing_param_list: f.missing_param_list,
608 return_type: f.return_type.map(|t| folder.fold_type_hint(t)),
609 body: fold_boxed_expr(folder, f.body),
610 attributes: fold_owned_attrs(folder, f.attributes),
611 }))
612 }
613 ExprKind::Match(m) => ExprKind::Match(MatchExpr {
614 subject: fold_boxed_expr(folder, m.subject),
615 arms: map_slice(m.arms, |arm| folder.fold_match_arm(arm)),
616 brace_span: m.brace_span,
617 }),
618 ExprKind::Throw(e) => ExprKind::Throw(fold_boxed_expr(folder, e)),
619 ExprKind::Yield(y) => ExprKind::Yield(YieldExpr {
620 key: fold_opt_boxed_expr(folder, y.key),
621 value: fold_opt_boxed_expr(folder, y.value),
622 is_from: y.is_from,
623 }),
624 ExprKind::AnonymousClass(cls) => {
625 ExprKind::AnonymousClass(Box::new(fold_owned_class_decl(folder, *cls)))
626 }
627 ExprKind::CallableCreate(c) => ExprKind::CallableCreate(CallableCreateExpr {
628 kind: match c.kind {
629 CallableCreateKind::Function(e) => {
630 CallableCreateKind::Function(fold_boxed_expr(folder, e))
631 }
632 CallableCreateKind::Method { object, method } => CallableCreateKind::Method {
633 object: fold_boxed_expr(folder, object),
634 method: fold_boxed_expr(folder, method),
635 },
636 CallableCreateKind::NullsafeMethod { object, method } => {
637 CallableCreateKind::NullsafeMethod {
638 object: fold_boxed_expr(folder, object),
639 method: fold_boxed_expr(folder, method),
640 }
641 }
642 CallableCreateKind::StaticMethod { class, method } => {
643 CallableCreateKind::StaticMethod {
644 class: fold_boxed_expr(folder, class),
645 method: fold_boxed_expr(folder, method),
646 }
647 }
648 },
649 }),
650 ExprKind::Omit => ExprKind::Omit,
651 ExprKind::Error => ExprKind::Error,
652 }
653}
654
655pub fn fold_owned_param<F: OwnedFold + ?Sized>(folder: &mut F, p: Param) -> Param {
657 Param {
658 name: p.name,
659 type_hint: p.type_hint.map(|t| folder.fold_type_hint(t)),
660 default: p.default.map(|e| folder.fold_expr(e)),
661 by_ref: p.by_ref,
662 variadic: p.variadic,
663 modifiers: p.modifiers,
664 attributes: fold_owned_attrs(folder, p.attributes),
665 hooks: fold_owned_hooks(folder, p.hooks),
666 empty_hook_list: p.empty_hook_list,
667 doc_comment: p.doc_comment,
668 span: p.span,
669 }
670}
671
672pub fn fold_owned_arg<F: OwnedFold + ?Sized>(folder: &mut F, arg: Arg) -> Arg {
674 Arg {
675 name: arg.name.map(|n| folder.fold_name(n)),
676 kind: match arg.kind {
677 ArgKind::Value(e) => ArgKind::Value(folder.fold_expr(e)),
678 ArgKind::Spread(e) => ArgKind::Spread(folder.fold_expr(e)),
679 ArgKind::ByRef(e) => ArgKind::ByRef(folder.fold_expr(e)),
680 ArgKind::Placeholder => ArgKind::Placeholder,
681 ArgKind::SpreadPlaceholder => ArgKind::SpreadPlaceholder,
682 },
683 span: arg.span,
684 }
685}
686
687pub fn fold_owned_closure_use_var<F: OwnedFold + ?Sized>(
689 _folder: &mut F,
690 var: ClosureUseVar,
691) -> ClosureUseVar {
692 var
693}
694
695pub fn fold_owned_name<F: OwnedFold + ?Sized>(_folder: &mut F, name: Name) -> Name {
697 name
698}
699
700fn fold_owned_method<F: OwnedFold + ?Sized>(folder: &mut F, m: MethodDecl) -> MethodDecl {
702 MethodDecl {
703 name: m.name,
704 modifiers: m.modifiers,
705 by_ref: m.by_ref,
706 params: fold_owned_params(folder, m.params),
707 missing_param_list: m.missing_param_list,
708 return_type: m.return_type.map(|t| folder.fold_type_hint(t)),
709 body: m.body.map(|b| fold_boxed_block(folder, b)),
710 attributes: fold_owned_attrs(folder, m.attributes),
711 doc_comment: m.doc_comment,
712 }
713}
714
715fn fold_owned_property<F: OwnedFold + ?Sized>(folder: &mut F, p: PropertyDecl) -> PropertyDecl {
716 PropertyDecl {
717 name: p.name,
718 modifiers: p.modifiers,
719 type_hint: p.type_hint.map(|t| folder.fold_type_hint(t)),
720 default: p.default.map(|e| folder.fold_expr(e)),
721 attributes: fold_owned_attrs(folder, p.attributes),
722 hooks: fold_owned_hooks(folder, p.hooks),
723 empty_hook_list: p.empty_hook_list,
724 doc_comment: p.doc_comment,
725 }
726}
727
728pub fn fold_owned_class_member<F: OwnedFold + ?Sized>(
730 folder: &mut F,
731 member: ClassMember,
732) -> ClassMember {
733 ClassMember {
734 kind: match member.kind {
735 ClassMemberKind::Property(p) => {
736 ClassMemberKind::Property(fold_owned_property(folder, p))
737 }
738 ClassMemberKind::Method(m) => ClassMemberKind::Method(fold_owned_method(folder, m)),
739 ClassMemberKind::ClassConst(c) => {
740 ClassMemberKind::ClassConst(fold_owned_class_const(folder, c))
741 }
742 ClassMemberKind::TraitUse(t) => ClassMemberKind::TraitUse(folder.fold_trait_use(t)),
743 },
744 span: member.span,
745 }
746}
747
748pub fn fold_owned_enum_member<F: OwnedFold + ?Sized>(
750 folder: &mut F,
751 member: EnumMember,
752) -> EnumMember {
753 EnumMember {
754 kind: match member.kind {
755 EnumMemberKind::Case(c) => EnumMemberKind::Case(EnumCase {
756 name: c.name,
757 value: c.value.map(|e| folder.fold_expr(e)),
758 attributes: fold_owned_attrs(folder, c.attributes),
759 doc_comment: c.doc_comment,
760 }),
761 EnumMemberKind::Method(m) => EnumMemberKind::Method(fold_owned_method(folder, m)),
762 EnumMemberKind::ClassConst(c) => {
763 EnumMemberKind::ClassConst(fold_owned_class_const(folder, c))
764 }
765 EnumMemberKind::TraitUse(t) => EnumMemberKind::TraitUse(folder.fold_trait_use(t)),
766 EnumMemberKind::Property(p) => EnumMemberKind::Property(fold_owned_property(folder, p)),
767 },
768 span: member.span,
769 }
770}
771
772pub fn fold_owned_property_hook<F: OwnedFold + ?Sized>(
774 folder: &mut F,
775 hook: PropertyHook,
776) -> PropertyHook {
777 PropertyHook {
778 kind: hook.kind,
779 body: match hook.body {
780 PropertyHookBody::Block(block) => {
781 PropertyHookBody::Block(fold_boxed_block(folder, block))
782 }
783 PropertyHookBody::Expression(e) => PropertyHookBody::Expression(folder.fold_expr(e)),
784 PropertyHookBody::Abstract => PropertyHookBody::Abstract,
785 },
786 modifiers: hook.modifiers,
787 by_ref: hook.by_ref,
788 params: fold_owned_params(folder, hook.params),
789 empty_param_list: hook.empty_param_list,
790 attributes: fold_owned_attrs(folder, hook.attributes),
791 name_span: hook.name_span,
792 span: hook.span,
793 }
794}
795
796pub fn fold_owned_type_hint<F: OwnedFold + ?Sized>(
798 folder: &mut F,
799 type_hint: TypeHint,
800) -> TypeHint {
801 TypeHint {
802 kind: match type_hint.kind {
803 TypeHintKind::Named(n) => TypeHintKind::Named(folder.fold_name(n)),
804 TypeHintKind::Keyword(b, span) => TypeHintKind::Keyword(b, span),
805 TypeHintKind::Nullable(inner) => {
806 TypeHintKind::Nullable(Box::new(folder.fold_type_hint(*inner)))
807 }
808 TypeHintKind::Union(types) => TypeHintKind::Union(fold_owned_type_hints(folder, types)),
809 TypeHintKind::Intersection(types) => {
810 TypeHintKind::Intersection(fold_owned_type_hints(folder, types))
811 }
812 },
813 span: type_hint.span,
814 }
815}
816
817pub fn fold_owned_attribute<F: OwnedFold + ?Sized>(
819 folder: &mut F,
820 attribute: Attribute,
821) -> Attribute {
822 Attribute {
823 name: folder.fold_name(attribute.name),
824 args: fold_owned_args(folder, attribute.args),
825 span: attribute.span,
826 }
827}
828
829pub fn fold_owned_catch_clause<F: OwnedFold + ?Sized>(
831 folder: &mut F,
832 catch: CatchClause,
833) -> CatchClause {
834 CatchClause {
835 types: fold_owned_names(folder, catch.types),
836 var: catch.var,
837 body: fold_boxed_block(folder, catch.body),
838 span: catch.span,
839 }
840}
841
842pub fn fold_owned_match_arm<F: OwnedFold + ?Sized>(folder: &mut F, arm: MatchArm) -> MatchArm {
844 MatchArm {
845 conditions: arm.conditions.map(|conds| fold_owned_exprs(folder, conds)),
846 body: folder.fold_expr(arm.body),
847 span: arm.span,
848 }
849}
850
851fn fold_owned_class_const<F: OwnedFold + ?Sized>(
852 folder: &mut F,
853 c: ClassConstDecl,
854) -> ClassConstDecl {
855 ClassConstDecl {
856 name: c.name,
857 modifiers: c.modifiers,
858 type_hint: c.type_hint.map(|th| Box::new(folder.fold_type_hint(*th))),
859 value: folder.fold_expr(c.value),
860 attributes: fold_owned_attrs(folder, c.attributes),
861 doc_comment: c.doc_comment,
862 }
863}
864
865pub fn fold_owned_trait_use<F: OwnedFold + ?Sized>(
867 folder: &mut F,
868 t: TraitUseDecl,
869) -> TraitUseDecl {
870 TraitUseDecl {
871 traits: fold_owned_names(folder, t.traits),
872 adaptations: map_slice(t.adaptations, |a| folder.fold_trait_adaptation(a)),
873 adaptations_brace_span: t.adaptations_brace_span,
874 doc_comment: t.doc_comment,
875 }
876}
877
878pub fn fold_owned_trait_adaptation<F: OwnedFold + ?Sized>(
880 folder: &mut F,
881 a: TraitAdaptation,
882) -> TraitAdaptation {
883 TraitAdaptation {
884 kind: match a.kind {
885 TraitAdaptationKind::Precedence {
886 trait_name,
887 method,
888 insteadof,
889 } => TraitAdaptationKind::Precedence {
890 trait_name: folder.fold_name(trait_name),
891 method: folder.fold_name(method),
892 insteadof: fold_owned_names(folder, insteadof),
893 },
894 TraitAdaptationKind::Alias {
895 trait_name,
896 method,
897 new_modifier,
898 is_final,
899 invalid_modifier,
900 new_name,
901 } => TraitAdaptationKind::Alias {
902 trait_name: trait_name.map(|n| folder.fold_name(n)),
903 method: folder.fold_name(method),
904 new_modifier,
905 is_final,
906 invalid_modifier,
907 new_name: new_name.map(|n| folder.fold_name(n)),
908 },
909 },
910 span: a.span,
911 }
912}
913
914fn fold_owned_class_decl<F: OwnedFold + ?Sized>(folder: &mut F, cls: ClassDecl) -> ClassDecl {
915 ClassDecl {
916 name: cls.name,
917 modifiers: cls.modifiers,
918 extends: cls.extends.map(|n| folder.fold_name(n)),
919 implements: fold_owned_names(folder, cls.implements),
920 body: ClassBody {
921 members: fold_owned_members(folder, cls.body.members),
922 span: cls.body.span,
923 },
924 attributes: fold_owned_attrs(folder, cls.attributes),
925 doc_comment: cls.doc_comment,
926 }
927}
928
929fn fold_owned_interface_decl<F: OwnedFold + ?Sized>(
930 folder: &mut F,
931 iface: InterfaceDecl,
932) -> InterfaceDecl {
933 InterfaceDecl {
934 name: iface.name,
935 extends: fold_owned_names(folder, iface.extends),
936 body: ClassBody {
937 members: fold_owned_members(folder, iface.body.members),
938 span: iface.body.span,
939 },
940 attributes: fold_owned_attrs(folder, iface.attributes),
941 doc_comment: iface.doc_comment,
942 }
943}
944
945fn fold_owned_trait_decl<F: OwnedFold + ?Sized>(folder: &mut F, tr: TraitDecl) -> TraitDecl {
946 TraitDecl {
947 name: tr.name,
948 body: ClassBody {
949 members: fold_owned_members(folder, tr.body.members),
950 span: tr.body.span,
951 },
952 attributes: fold_owned_attrs(folder, tr.attributes),
953 doc_comment: tr.doc_comment,
954 }
955}
956
957fn fold_owned_enum_decl<F: OwnedFold + ?Sized>(folder: &mut F, en: EnumDecl) -> EnumDecl {
958 EnumDecl {
959 name: en.name,
960 scalar_type: en.scalar_type.map(|n| folder.fold_name(n)),
961 implements: fold_owned_names(folder, en.implements),
962 body: EnumBody {
963 members: map_slice(en.body.members, |m| folder.fold_enum_member(m)),
964 span: en.body.span,
965 },
966 attributes: fold_owned_attrs(folder, en.attributes),
967 doc_comment: en.doc_comment,
968 }
969}
970
971#[cfg(test)]
976mod tests {
977 use super::*;
978 use crate::ast::AssignOp;
979 use crate::Span;
980
981 fn dummy_var(name: &str) -> Expr {
982 Expr {
983 kind: ExprKind::Variable(Box::from(name)),
984 span: Span::DUMMY,
985 }
986 }
987
988 fn dummy_int(n: i64) -> Expr {
989 Expr {
990 kind: ExprKind::Int(n),
991 span: Span::DUMMY,
992 }
993 }
994
995 fn assign(target: Expr, value: Expr) -> Expr {
996 Expr {
997 kind: ExprKind::Assign(AssignExpr {
998 target: Box::new(target),
999 op: AssignOp::Assign,
1000 value: Box::new(value),
1001 by_ref: false,
1002 }),
1003 span: Span::DUMMY,
1004 }
1005 }
1006
1007 fn expr_stmt(e: Expr) -> Stmt {
1008 Stmt {
1009 kind: StmtKind::Expression(Box::new(e)),
1010 span: Span::DUMMY,
1011 doc_comment: None,
1012 }
1013 }
1014
1015 fn empty_block() -> Block {
1016 Block {
1017 stmts: Box::from([]),
1018 span: Span::DUMMY,
1019 }
1020 }
1021
1022 fn program(stmts: impl IntoIterator<Item = Stmt>) -> Program {
1023 Program {
1024 stmts: stmts.into_iter().collect(),
1025 span: Span::DUMMY,
1026 }
1027 }
1028
1029 #[test]
1031 fn identity_fold_roundtrip() {
1032 let p = program([expr_stmt(assign(dummy_var("x"), dummy_var("y")))]);
1033 struct Identity;
1034 impl OwnedFold for Identity {}
1035 let before = serde_json::to_string(&p).unwrap();
1036 let folded = Identity.fold_program(p);
1037 assert_eq!(serde_json::to_string(&folded).unwrap(), before);
1038 }
1039
1040 #[test]
1042 fn negate_ints() {
1043 let p = program([expr_stmt(assign(dummy_var("x"), dummy_int(42)))]);
1044 struct NegateInts;
1045 impl OwnedFold for NegateInts {
1046 fn fold_expr(&mut self, expr: Expr) -> Expr {
1047 if let ExprKind::Int(n) = &expr.kind {
1048 return Expr {
1049 kind: ExprKind::Int(-n),
1050 span: expr.span,
1051 };
1052 }
1053 fold_owned_expr(self, expr)
1054 }
1055 }
1056 let folded = NegateInts.fold_program(p);
1057 let stmt = &folded.stmts[0];
1058 if let StmtKind::Expression(expr) = &stmt.kind {
1059 if let ExprKind::Assign(a) = &expr.kind {
1060 if let ExprKind::Int(n) = &a.value.kind {
1061 assert_eq!(*n, -42);
1062 return;
1063 }
1064 }
1065 }
1066 panic!("expected negated int assignment");
1067 }
1068
1069 #[test]
1071 fn rename_variable() {
1072 let p = program([expr_stmt(assign(dummy_var("x"), dummy_var("x")))]);
1074 struct Rename;
1075 impl OwnedFold for Rename {
1076 fn fold_expr(&mut self, expr: Expr) -> Expr {
1077 if let ExprKind::Variable(name) = &expr.kind {
1078 if name.as_ref() == "x" {
1079 return Expr {
1080 kind: ExprKind::Variable(Box::from("renamed")),
1081 span: expr.span,
1082 };
1083 }
1084 }
1085 fold_owned_expr(self, expr)
1086 }
1087 }
1088 let folded = Rename.fold_program(p);
1089 let stmt = &folded.stmts[0];
1090 if let StmtKind::Expression(expr) = &stmt.kind {
1091 if let ExprKind::Assign(a) = &expr.kind {
1092 assert!(matches!(&a.target.kind, ExprKind::Variable(n) if n.as_ref() == "renamed"));
1093 assert!(matches!(&a.value.kind, ExprKind::Variable(n) if n.as_ref() == "renamed"));
1094 return;
1095 }
1096 }
1097 panic!("expected renamed variable assignment");
1098 }
1099
1100 #[test]
1102 fn rename_variable_in_closure_use() {
1103 let closure = Expr {
1105 kind: ExprKind::Closure(Box::new(ClosureExpr {
1106 is_static: false,
1107 by_ref: false,
1108 params: Box::from([]),
1109 missing_param_list: false,
1110 use_vars: Box::from([ClosureUseVar {
1111 name: Ident::new("x", Span::DUMMY),
1112 by_ref: false,
1113 span: Span::DUMMY,
1114 }]),
1115 return_type: None,
1116 body: Box::new(empty_block()),
1117 attributes: Box::from([]),
1118 })),
1119 span: Span::DUMMY,
1120 };
1121 let p = program([expr_stmt(closure)]);
1122
1123 struct Rename;
1124 impl OwnedFold for Rename {
1125 fn fold_closure_use_var(&mut self, var: ClosureUseVar) -> ClosureUseVar {
1126 ClosureUseVar {
1127 name: if var.name == "x" {
1128 Ident::new("renamed", var.name.span)
1129 } else {
1130 var.name
1131 },
1132 by_ref: var.by_ref,
1133 span: var.span,
1134 }
1135 }
1136 }
1137
1138 let folded = Rename.fold_program(p);
1139 if let StmtKind::Expression(expr) = &folded.stmts[0].kind {
1140 if let ExprKind::Closure(c) = &expr.kind {
1141 assert_eq!(c.use_vars[0].name, "renamed");
1142 return;
1143 }
1144 }
1145 panic!("expected closure with renamed use-var");
1146 }
1147
1148 #[test]
1150 fn fold_stmts_hook_can_delete_statements() {
1151 struct DropNops;
1152 impl OwnedFold for DropNops {
1153 fn fold_stmts(&mut self, stmts: Box<[Stmt]>) -> Box<[Stmt]> {
1154 let kept: Vec<Stmt> = stmts
1155 .into_vec()
1156 .into_iter()
1157 .filter(|s| !matches!(s.kind, StmtKind::Nop))
1158 .map(|s| self.fold_stmt(s))
1159 .collect();
1160 kept.into_boxed_slice()
1161 }
1162 }
1163 let nop = || Stmt {
1164 kind: StmtKind::Nop,
1165 span: Span::DUMMY,
1166 doc_comment: None,
1167 };
1168 let nested = Stmt {
1169 kind: StmtKind::Block(Box::new(Block {
1170 stmts: Box::from([nop(), expr_stmt(dummy_int(1))]),
1171 span: Span::DUMMY,
1172 })),
1173 span: Span::DUMMY,
1174 doc_comment: None,
1175 };
1176 let switch = Stmt {
1177 kind: StmtKind::Switch(Box::new(SwitchStmt {
1178 expr: dummy_int(0),
1179 body: SwitchBody {
1180 cases: Box::from([SwitchCase {
1181 value: None,
1182 body: Box::from([nop()]),
1183 span: Span::DUMMY,
1184 }]),
1185 span: Span::DUMMY,
1186 },
1187 uses_alternative: false,
1188 })),
1189 span: Span::DUMMY,
1190 doc_comment: None,
1191 };
1192 let folded = DropNops.fold_program(program([nop(), nested, switch]));
1193 assert_eq!(folded.stmts.len(), 2);
1194 let StmtKind::Block(block) = &folded.stmts[0].kind else {
1195 panic!("expected Block")
1196 };
1197 assert_eq!(block.stmts.len(), 1);
1198 let StmtKind::Switch(switch) = &folded.stmts[1].kind else {
1199 panic!("expected Switch")
1200 };
1201 assert!(switch.body.cases[0].body.is_empty());
1202 }
1203}