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