1use bumpalo::Bump;
68
69use crate::ast::*;
70
71pub trait Fold<'src> {
83 fn fold_program<'new>(
85 &mut self,
86 arena: &'new Bump,
87 program: &Program<'_, 'src>,
88 ) -> Program<'new, 'src> {
89 fold_program(self, arena, program)
90 }
91
92 fn fold_stmt<'new>(&mut self, arena: &'new Bump, stmt: &Stmt<'_, 'src>) -> Stmt<'new, 'src> {
94 fold_stmt(self, arena, stmt)
95 }
96
97 fn fold_block<'new>(
99 &mut self,
100 arena: &'new Bump,
101 block: &Block<'_, 'src>,
102 ) -> Block<'new, 'src> {
103 fold_block(self, arena, block)
104 }
105
106 fn fold_expr<'new>(&mut self, arena: &'new Bump, expr: &Expr<'_, 'src>) -> Expr<'new, 'src> {
108 fold_expr(self, arena, expr)
109 }
110
111 fn fold_param<'new>(
113 &mut self,
114 arena: &'new Bump,
115 param: &Param<'_, 'src>,
116 ) -> Param<'new, 'src> {
117 fold_param(self, arena, param)
118 }
119
120 fn fold_arg<'new>(&mut self, arena: &'new Bump, arg: &Arg<'_, 'src>) -> Arg<'new, 'src> {
122 fold_arg(self, arena, arg)
123 }
124
125 fn fold_class_member<'new>(
127 &mut self,
128 arena: &'new Bump,
129 member: &ClassMember<'_, 'src>,
130 ) -> ClassMember<'new, 'src> {
131 fold_class_member(self, arena, member)
132 }
133
134 fn fold_enum_member<'new>(
136 &mut self,
137 arena: &'new Bump,
138 member: &EnumMember<'_, 'src>,
139 ) -> EnumMember<'new, 'src> {
140 fold_enum_member(self, arena, member)
141 }
142
143 fn fold_property_hook<'new>(
145 &mut self,
146 arena: &'new Bump,
147 hook: &PropertyHook<'_, 'src>,
148 ) -> PropertyHook<'new, 'src> {
149 fold_property_hook(self, arena, hook)
150 }
151
152 fn fold_type_hint<'new>(
154 &mut self,
155 arena: &'new Bump,
156 type_hint: &TypeHint<'_, 'src>,
157 ) -> TypeHint<'new, 'src> {
158 fold_type_hint(self, arena, type_hint)
159 }
160
161 fn fold_attribute<'new>(
163 &mut self,
164 arena: &'new Bump,
165 attribute: &Attribute<'_, 'src>,
166 ) -> Attribute<'new, 'src> {
167 fold_attribute(self, arena, attribute)
168 }
169
170 fn fold_catch_clause<'new>(
172 &mut self,
173 arena: &'new Bump,
174 catch: &CatchClause<'_, 'src>,
175 ) -> CatchClause<'new, 'src> {
176 fold_catch_clause(self, arena, catch)
177 }
178
179 fn fold_match_arm<'new>(
181 &mut self,
182 arena: &'new Bump,
183 arm: &MatchArm<'_, 'src>,
184 ) -> MatchArm<'new, 'src> {
185 fold_match_arm(self, arena, arm)
186 }
187
188 fn fold_closure_use_var(&mut self, var: &ClosureUseVar<'src>) -> ClosureUseVar<'src> {
190 var.clone()
191 }
192
193 fn fold_trait_use<'new>(
195 &mut self,
196 arena: &'new Bump,
197 trait_use: &TraitUseDecl<'_, 'src>,
198 ) -> TraitUseDecl<'new, 'src> {
199 fold_trait_use(self, arena, trait_use)
200 }
201
202 fn fold_trait_adaptation<'new>(
204 &mut self,
205 arena: &'new Bump,
206 adaptation: &TraitAdaptation<'_, 'src>,
207 ) -> TraitAdaptation<'new, 'src> {
208 fold_trait_adaptation(self, arena, adaptation)
209 }
210
211 fn fold_name<'new>(&mut self, arena: &'new Bump, name: &Name<'_, 'src>) -> Name<'new, 'src> {
213 fold_name(self, arena, name)
214 }
215}
216
217pub fn fold_program<'new, 'src, F: Fold<'src> + ?Sized>(
223 folder: &mut F,
224 arena: &'new Bump,
225 program: &Program<'_, 'src>,
226) -> Program<'new, 'src> {
227 Program {
228 stmts: fold_stmts(folder, arena, &program.stmts),
229 span: program.span,
230 }
231}
232
233pub fn fold_stmt<'new, 'src, F: Fold<'src> + ?Sized>(
235 folder: &mut F,
236 arena: &'new Bump,
237 stmt: &Stmt<'_, 'src>,
238) -> Stmt<'new, 'src> {
239 let kind = match &stmt.kind {
240 StmtKind::Expression(expr) => {
241 StmtKind::Expression(arena.alloc(folder.fold_expr(arena, expr)))
242 }
243 StmtKind::Echo(exprs) => StmtKind::Echo(fold_exprs(folder, arena, exprs)),
244 StmtKind::Return(expr) => {
245 StmtKind::Return(expr.map(|e| &*arena.alloc(folder.fold_expr(arena, e))))
246 }
247 StmtKind::Block(block) => StmtKind::Block(arena.alloc(folder.fold_block(arena, block))),
248 StmtKind::If(if_stmt) => {
249 let mut elseif_branches =
250 ArenaVec::with_capacity_in(if_stmt.elseif_branches.len(), arena);
251 for branch in if_stmt.elseif_branches.iter() {
252 elseif_branches.push(ElseIfBranch {
253 condition: folder.fold_expr(arena, &branch.condition),
254 body: folder.fold_stmt(arena, &branch.body),
255 span: branch.span,
256 });
257 }
258 let new_if = arena.alloc(IfStmt {
259 condition: folder.fold_expr(arena, &if_stmt.condition),
260 then_branch: arena.alloc(folder.fold_stmt(arena, if_stmt.then_branch)),
261 elseif_branches,
262 else_branch: if_stmt
263 .else_branch
264 .map(|b| &*arena.alloc(folder.fold_stmt(arena, b))),
265 else_kw_start: if_stmt.else_kw_start,
266 uses_alternative: if_stmt.uses_alternative,
267 });
268 StmtKind::If(new_if)
269 }
270 StmtKind::While(w) => {
271 let new_w = arena.alloc(WhileStmt {
272 condition: folder.fold_expr(arena, &w.condition),
273 body: arena.alloc(folder.fold_stmt(arena, w.body)),
274 uses_alternative: w.uses_alternative,
275 });
276 StmtKind::While(new_w)
277 }
278 StmtKind::For(f) => {
279 let new_f = arena.alloc(ForStmt {
280 init: fold_exprs(folder, arena, &f.init),
281 condition: fold_exprs(folder, arena, &f.condition),
282 update: fold_exprs(folder, arena, &f.update),
283 body: arena.alloc(folder.fold_stmt(arena, f.body)),
284 uses_alternative: f.uses_alternative,
285 });
286 StmtKind::For(new_f)
287 }
288 StmtKind::Foreach(fe) => {
289 let new_fe = arena.alloc(ForeachStmt {
290 expr: folder.fold_expr(arena, &fe.expr),
291 key: fe.key.as_ref().map(|k| folder.fold_expr(arena, k)),
292 value: folder.fold_expr(arena, &fe.value),
293 body: arena.alloc(folder.fold_stmt(arena, fe.body)),
294 uses_alternative: fe.uses_alternative,
295 });
296 StmtKind::Foreach(new_fe)
297 }
298 StmtKind::DoWhile(dw) => {
299 let new_dw = arena.alloc(DoWhileStmt {
300 body: arena.alloc(folder.fold_stmt(arena, dw.body)),
301 condition: folder.fold_expr(arena, &dw.condition),
302 });
303 StmtKind::DoWhile(new_dw)
304 }
305 StmtKind::Function(func) => {
306 StmtKind::Function(arena.alloc(fold_function_decl(folder, arena, func)))
307 }
308 StmtKind::Break(expr) => {
309 StmtKind::Break(expr.map(|e| &*arena.alloc(folder.fold_expr(arena, e))))
310 }
311 StmtKind::Continue(expr) => {
312 StmtKind::Continue(expr.map(|e| &*arena.alloc(folder.fold_expr(arena, e))))
313 }
314 StmtKind::Switch(sw) => {
315 let mut cases = ArenaVec::with_capacity_in(sw.body.cases.len(), arena);
316 for case in sw.body.cases.iter() {
317 cases.push(SwitchCase {
318 value: case.value.as_ref().map(|v| folder.fold_expr(arena, v)),
319 body: fold_stmts(folder, arena, &case.body),
320 span: case.span,
321 });
322 }
323 let new_sw = arena.alloc(SwitchStmt {
324 expr: folder.fold_expr(arena, &sw.expr),
325 body: SwitchBody {
326 cases,
327 span: sw.body.span,
328 },
329 uses_alternative: sw.uses_alternative,
330 });
331 StmtKind::Switch(new_sw)
332 }
333 StmtKind::Goto(ident) => StmtKind::Goto(*ident),
334 StmtKind::Label(s) => StmtKind::Label(arena.alloc_str(s)),
335 StmtKind::Declare(decl) => {
336 let mut directives = ArenaVec::with_capacity_in(decl.directives.len(), arena);
337 for (name, expr) in decl.directives.iter() {
338 directives.push((*name, folder.fold_expr(arena, expr)));
339 }
340 let new_decl = arena.alloc(DeclareStmt {
341 directives,
342 body: decl.body.map(|b| &*arena.alloc(folder.fold_stmt(arena, b))),
343 uses_alternative: decl.uses_alternative,
344 });
345 StmtKind::Declare(new_decl)
346 }
347 StmtKind::Unset(exprs) => StmtKind::Unset(fold_exprs(folder, arena, exprs)),
348 StmtKind::Throw(expr) => StmtKind::Throw(arena.alloc(folder.fold_expr(arena, expr))),
349 StmtKind::TryCatch(tc) => {
350 let mut catches = ArenaVec::with_capacity_in(tc.catches.len(), arena);
351 for catch in tc.catches.iter() {
352 catches.push(folder.fold_catch_clause(arena, catch));
353 }
354 let new_tc = arena.alloc(TryCatchStmt {
355 body: arena.alloc(folder.fold_block(arena, tc.body)),
356 catches,
357 finally: tc
358 .finally
359 .map(|f| &*arena.alloc(folder.fold_block(arena, f))),
360 finally_kw_start: tc.finally_kw_start,
361 });
362 StmtKind::TryCatch(new_tc)
363 }
364 StmtKind::Global(exprs) => StmtKind::Global(fold_exprs(folder, arena, exprs)),
365 StmtKind::Class(class) => {
366 StmtKind::Class(arena.alloc(fold_class_decl(folder, arena, class)))
367 }
368 StmtKind::Interface(iface) => {
369 StmtKind::Interface(arena.alloc(fold_interface_decl(folder, arena, iface)))
370 }
371 StmtKind::Trait(t) => StmtKind::Trait(arena.alloc(fold_trait_decl(folder, arena, t))),
372 StmtKind::Enum(e) => StmtKind::Enum(arena.alloc(fold_enum_decl(folder, arena, e))),
373 StmtKind::Namespace(ns) => {
374 let new_ns = arena.alloc(NamespaceDecl {
375 name: ns.name.as_ref().map(|n| folder.fold_name(arena, n)),
376 body: match &ns.body {
377 NamespaceBody::Braced(block) => {
378 NamespaceBody::Braced(arena.alloc(folder.fold_block(arena, block)))
379 }
380 NamespaceBody::Simple => NamespaceBody::Simple,
381 },
382 });
383 StmtKind::Namespace(new_ns)
384 }
385 StmtKind::Use(use_decl) => {
386 let mut uses = ArenaVec::with_capacity_in(use_decl.uses.len(), arena);
387 for item in use_decl.uses.iter() {
388 uses.push(UseItem {
389 name: folder.fold_name(arena, &item.name),
390 alias: item.alias,
391 kind: item.kind,
392 span: item.span,
393 });
394 }
395 let new_use = arena.alloc(UseDecl {
396 kind: use_decl.kind,
397 uses,
398 });
399 StmtKind::Use(new_use)
400 }
401 StmtKind::Const(items) => {
402 let mut new_items = ArenaVec::with_capacity_in(items.len(), arena);
403 for item in items.iter() {
404 new_items.push(ConstItem {
405 name: item.name,
406 value: folder.fold_expr(arena, &item.value),
407 attributes: fold_attrs(folder, arena, &item.attributes),
408 span: item.span,
409 doc_comment: item.doc_comment.as_ref().map(fold_comment),
410 });
411 }
412 StmtKind::Const(new_items)
413 }
414 StmtKind::StaticVar(vars) => {
415 let mut new_vars = ArenaVec::with_capacity_in(vars.len(), arena);
416 for var in vars.iter() {
417 new_vars.push(StaticVar {
418 name: var.name,
419 default: var.default.as_ref().map(|d| folder.fold_expr(arena, d)),
420 span: var.span,
421 });
422 }
423 StmtKind::StaticVar(new_vars)
424 }
425 StmtKind::HaltCompiler(s) => StmtKind::HaltCompiler(s),
426 StmtKind::Nop => StmtKind::Nop,
427 StmtKind::InlineHtml(s) => StmtKind::InlineHtml(s),
428 StmtKind::Error => StmtKind::Error,
429 };
430 Stmt {
431 kind,
432 span: stmt.span,
433 doc_comment: stmt.doc_comment.map(|c| &*arena.alloc(fold_comment(c))),
434 }
435}
436
437pub fn fold_expr<'new, 'src, F: Fold<'src> + ?Sized>(
439 folder: &mut F,
440 arena: &'new Bump,
441 expr: &Expr<'_, 'src>,
442) -> Expr<'new, 'src> {
443 let kind = match &expr.kind {
444 ExprKind::Int(n) => ExprKind::Int(*n),
445 ExprKind::Float(f) => ExprKind::Float(*f),
446 ExprKind::String(s) => ExprKind::String(arena.alloc_str(s)),
447 ExprKind::InterpolatedString(parts) => {
448 ExprKind::InterpolatedString(fold_string_parts(folder, arena, parts))
449 }
450 ExprKind::Heredoc { label, parts } => ExprKind::Heredoc {
451 label,
452 parts: fold_string_parts(folder, arena, parts),
453 },
454 ExprKind::Nowdoc { label, value } => ExprKind::Nowdoc {
455 label,
456 value: arena.alloc_str(value),
457 },
458 ExprKind::ShellExec(parts) => ExprKind::ShellExec(fold_string_parts(folder, arena, parts)),
459 ExprKind::Bool(b) => ExprKind::Bool(*b),
460 ExprKind::Null => ExprKind::Null,
461 ExprKind::Variable(name) => ExprKind::Variable(fold_name_str(*name, arena)),
462 ExprKind::VariableVariable(inner) => {
463 ExprKind::VariableVariable(arena.alloc(folder.fold_expr(arena, inner)))
464 }
465 ExprKind::Identifier(name) => ExprKind::Identifier(fold_name_str(*name, arena)),
466 ExprKind::Assign(assign) => ExprKind::Assign(AssignExpr {
467 target: arena.alloc(folder.fold_expr(arena, assign.target)),
468 op: assign.op,
469 value: arena.alloc(folder.fold_expr(arena, assign.value)),
470 by_ref: assign.by_ref,
471 }),
472 ExprKind::Binary(binary) => ExprKind::Binary(BinaryExpr {
473 left: arena.alloc(folder.fold_expr(arena, binary.left)),
474 op: binary.op,
475 right: arena.alloc(folder.fold_expr(arena, binary.right)),
476 }),
477 ExprKind::UnaryPrefix(u) => ExprKind::UnaryPrefix(UnaryPrefixExpr {
478 op: u.op,
479 operand: arena.alloc(folder.fold_expr(arena, u.operand)),
480 }),
481 ExprKind::UnaryPostfix(u) => ExprKind::UnaryPostfix(UnaryPostfixExpr {
482 operand: arena.alloc(folder.fold_expr(arena, u.operand)),
483 op: u.op,
484 }),
485 ExprKind::Ternary(t) => ExprKind::Ternary(TernaryExpr {
486 condition: arena.alloc(folder.fold_expr(arena, t.condition)),
487 then_expr: t
488 .then_expr
489 .map(|e| &*arena.alloc(folder.fold_expr(arena, e))),
490 else_expr: arena.alloc(folder.fold_expr(arena, t.else_expr)),
491 }),
492 ExprKind::NullCoalesce(nc) => ExprKind::NullCoalesce(NullCoalesceExpr {
493 left: arena.alloc(folder.fold_expr(arena, nc.left)),
494 right: arena.alloc(folder.fold_expr(arena, nc.right)),
495 }),
496 ExprKind::FunctionCall(call) => ExprKind::FunctionCall(FunctionCallExpr {
497 name: arena.alloc(folder.fold_expr(arena, call.name)),
498 args: fold_args(folder, arena, &call.args),
499 }),
500 ExprKind::Array(elements) => {
501 let mut new_elements = ArenaVec::with_capacity_in(elements.len(), arena);
502 for elem in elements.iter() {
503 new_elements.push(ArrayElement {
504 key: elem.key.as_ref().map(|k| folder.fold_expr(arena, k)),
505 value: folder.fold_expr(arena, &elem.value),
506 unpack: elem.unpack,
507 by_ref: elem.by_ref,
508 span: elem.span,
509 });
510 }
511 ExprKind::Array(new_elements)
512 }
513 ExprKind::ArrayAccess(access) => ExprKind::ArrayAccess(ArrayAccessExpr {
514 array: arena.alloc(folder.fold_expr(arena, access.array)),
515 index: access
516 .index
517 .map(|i| &*arena.alloc(folder.fold_expr(arena, i))),
518 }),
519 ExprKind::Print(e) => ExprKind::Print(arena.alloc(folder.fold_expr(arena, e))),
520 ExprKind::Parenthesized(e) => {
521 ExprKind::Parenthesized(arena.alloc(folder.fold_expr(arena, e)))
522 }
523 ExprKind::Cast(kind, e) => ExprKind::Cast(*kind, arena.alloc(folder.fold_expr(arena, e))),
524 ExprKind::ErrorSuppress(e) => {
525 ExprKind::ErrorSuppress(arena.alloc(folder.fold_expr(arena, e)))
526 }
527 ExprKind::Isset(exprs) => ExprKind::Isset(fold_exprs(folder, arena, exprs)),
528 ExprKind::Empty(e) => ExprKind::Empty(arena.alloc(folder.fold_expr(arena, e))),
529 ExprKind::Include(kind, e) => {
530 ExprKind::Include(*kind, arena.alloc(folder.fold_expr(arena, e)))
531 }
532 ExprKind::Eval(e) => ExprKind::Eval(arena.alloc(folder.fold_expr(arena, e))),
533 ExprKind::Exit(e) => ExprKind::Exit(e.map(|e| &*arena.alloc(folder.fold_expr(arena, e)))),
534 ExprKind::MagicConst(k) => ExprKind::MagicConst(*k),
535 ExprKind::Clone(e) => ExprKind::Clone(arena.alloc(folder.fold_expr(arena, e))),
536 ExprKind::CloneWith(obj, overrides) => ExprKind::CloneWith(
537 arena.alloc(folder.fold_expr(arena, obj)),
538 arena.alloc(folder.fold_expr(arena, overrides)),
539 ),
540 ExprKind::New(new_expr) => ExprKind::New(NewExpr {
541 class: arena.alloc(folder.fold_expr(arena, new_expr.class)),
542 args: fold_args(folder, arena, &new_expr.args),
543 }),
544 ExprKind::PropertyAccess(access) => ExprKind::PropertyAccess(PropertyAccessExpr {
545 object: arena.alloc(folder.fold_expr(arena, access.object)),
546 property: arena.alloc(folder.fold_expr(arena, access.property)),
547 }),
548 ExprKind::NullsafePropertyAccess(access) => {
549 ExprKind::NullsafePropertyAccess(PropertyAccessExpr {
550 object: arena.alloc(folder.fold_expr(arena, access.object)),
551 property: arena.alloc(folder.fold_expr(arena, access.property)),
552 })
553 }
554 ExprKind::MethodCall(call) => ExprKind::MethodCall(arena.alloc(MethodCallExpr {
555 object: arena.alloc(folder.fold_expr(arena, call.object)),
556 method: arena.alloc(folder.fold_expr(arena, call.method)),
557 args: fold_args(folder, arena, &call.args),
558 })),
559 ExprKind::NullsafeMethodCall(call) => {
560 ExprKind::NullsafeMethodCall(arena.alloc(MethodCallExpr {
561 object: arena.alloc(folder.fold_expr(arena, call.object)),
562 method: arena.alloc(folder.fold_expr(arena, call.method)),
563 args: fold_args(folder, arena, &call.args),
564 }))
565 }
566 ExprKind::StaticPropertyAccess(access) => {
567 ExprKind::StaticPropertyAccess(StaticAccessExpr {
568 class: arena.alloc(folder.fold_expr(arena, access.class)),
569 member: arena.alloc(folder.fold_expr(arena, access.member)),
570 })
571 }
572 ExprKind::StaticMethodCall(call) => {
573 ExprKind::StaticMethodCall(arena.alloc(StaticMethodCallExpr {
574 class: arena.alloc(folder.fold_expr(arena, call.class)),
575 method: arena.alloc(folder.fold_expr(arena, call.method)),
576 args: fold_args(folder, arena, &call.args),
577 }))
578 }
579 ExprKind::StaticDynMethodCall(call) => {
580 ExprKind::StaticDynMethodCall(arena.alloc(StaticDynMethodCallExpr {
581 class: arena.alloc(folder.fold_expr(arena, call.class)),
582 method: arena.alloc(folder.fold_expr(arena, call.method)),
583 args: fold_args(folder, arena, &call.args),
584 }))
585 }
586 ExprKind::ClassConstAccess(access) => ExprKind::ClassConstAccess(StaticAccessExpr {
587 class: arena.alloc(folder.fold_expr(arena, access.class)),
588 member: arena.alloc(folder.fold_expr(arena, access.member)),
589 }),
590 ExprKind::ClassConstAccessDynamic { class, member } => ExprKind::ClassConstAccessDynamic {
591 class: arena.alloc(folder.fold_expr(arena, class)),
592 member: arena.alloc(folder.fold_expr(arena, member)),
593 },
594 ExprKind::StaticPropertyAccessDynamic { class, member } => {
595 ExprKind::StaticPropertyAccessDynamic {
596 class: arena.alloc(folder.fold_expr(arena, class)),
597 member: arena.alloc(folder.fold_expr(arena, member)),
598 }
599 }
600 ExprKind::Closure(closure) => {
601 let mut use_vars = ArenaVec::with_capacity_in(closure.use_vars.len(), arena);
602 for var in closure.use_vars.iter() {
603 use_vars.push(folder.fold_closure_use_var(var));
604 }
605 let new_closure = arena.alloc(ClosureExpr {
606 is_static: closure.is_static,
607 by_ref: closure.by_ref,
608 params: fold_params(folder, arena, &closure.params),
609 use_vars,
610 return_type: closure
611 .return_type
612 .as_ref()
613 .map(|t| folder.fold_type_hint(arena, t)),
614 body: arena.alloc(folder.fold_block(arena, closure.body)),
615 attributes: fold_attrs(folder, arena, &closure.attributes),
616 });
617 ExprKind::Closure(new_closure)
618 }
619 ExprKind::ArrowFunction(arrow) => {
620 let new_arrow = arena.alloc(ArrowFunctionExpr {
621 is_static: arrow.is_static,
622 by_ref: arrow.by_ref,
623 params: fold_params(folder, arena, &arrow.params),
624 return_type: arrow
625 .return_type
626 .as_ref()
627 .map(|t| folder.fold_type_hint(arena, t)),
628 body: arena.alloc(folder.fold_expr(arena, arrow.body)),
629 attributes: fold_attrs(folder, arena, &arrow.attributes),
630 });
631 ExprKind::ArrowFunction(new_arrow)
632 }
633 ExprKind::Match(match_expr) => ExprKind::Match(MatchExpr {
634 subject: arena.alloc(folder.fold_expr(arena, match_expr.subject)),
635 arms: {
636 let mut arms = ArenaVec::with_capacity_in(match_expr.arms.len(), arena);
637 for arm in match_expr.arms.iter() {
638 arms.push(folder.fold_match_arm(arena, arm));
639 }
640 arms
641 },
642 brace_start: match_expr.brace_start,
643 }),
644 ExprKind::ThrowExpr(e) => ExprKind::ThrowExpr(arena.alloc(folder.fold_expr(arena, e))),
645 ExprKind::Yield(y) => ExprKind::Yield(YieldExpr {
646 key: y.key.map(|k| &*arena.alloc(folder.fold_expr(arena, k))),
647 value: y.value.map(|v| &*arena.alloc(folder.fold_expr(arena, v))),
648 is_from: y.is_from,
649 }),
650 ExprKind::AnonymousClass(class) => {
651 ExprKind::AnonymousClass(arena.alloc(fold_class_decl(folder, arena, class)))
652 }
653 ExprKind::CallableCreate(cc) => {
654 let kind = match &cc.kind {
655 CallableCreateKind::Function(name) => {
656 CallableCreateKind::Function(arena.alloc(folder.fold_expr(arena, name)))
657 }
658 CallableCreateKind::Method { object, method } => CallableCreateKind::Method {
659 object: arena.alloc(folder.fold_expr(arena, object)),
660 method: arena.alloc(folder.fold_expr(arena, method)),
661 },
662 CallableCreateKind::NullsafeMethod { object, method } => {
663 CallableCreateKind::NullsafeMethod {
664 object: arena.alloc(folder.fold_expr(arena, object)),
665 method: arena.alloc(folder.fold_expr(arena, method)),
666 }
667 }
668 CallableCreateKind::StaticMethod { class, method } => {
669 CallableCreateKind::StaticMethod {
670 class: arena.alloc(folder.fold_expr(arena, class)),
671 method: arena.alloc(folder.fold_expr(arena, method)),
672 }
673 }
674 };
675 ExprKind::CallableCreate(CallableCreateExpr { kind })
676 }
677 ExprKind::Omit => ExprKind::Omit,
678 ExprKind::Error => ExprKind::Error,
679 };
680 Expr {
681 kind,
682 span: expr.span,
683 }
684}
685
686pub fn fold_param<'new, 'src, F: Fold<'src> + ?Sized>(
688 folder: &mut F,
689 arena: &'new Bump,
690 param: &Param<'_, 'src>,
691) -> Param<'new, 'src> {
692 Param {
693 name: param.name,
694 type_hint: param
695 .type_hint
696 .as_ref()
697 .map(|t| folder.fold_type_hint(arena, t)),
698 default: param.default.as_ref().map(|d| folder.fold_expr(arena, d)),
699 by_ref: param.by_ref,
700 variadic: param.variadic,
701 is_readonly: param.is_readonly,
702 is_final: param.is_final,
703 visibility: param.visibility,
704 set_visibility: param.set_visibility,
705 attributes: fold_attrs(folder, arena, ¶m.attributes),
706 hooks: fold_hooks(folder, arena, ¶m.hooks),
707 doc_comment: param.doc_comment,
708 span: param.span,
709 }
710}
711
712pub fn fold_arg<'new, 'src, F: Fold<'src> + ?Sized>(
714 folder: &mut F,
715 arena: &'new Bump,
716 arg: &Arg<'_, 'src>,
717) -> Arg<'new, 'src> {
718 Arg {
719 name: arg.name.as_ref().map(|n| folder.fold_name(arena, n)),
720 value: arg.value.as_ref().map(|v| folder.fold_expr(arena, v)),
721 unpack: arg.unpack,
722 by_ref: arg.by_ref,
723 span: arg.span,
724 }
725}
726
727pub fn fold_class_member<'new, 'src, F: Fold<'src> + ?Sized>(
729 folder: &mut F,
730 arena: &'new Bump,
731 member: &ClassMember<'_, 'src>,
732) -> ClassMember<'new, 'src> {
733 let kind = match &member.kind {
734 ClassMemberKind::Property(prop) => {
735 ClassMemberKind::Property(fold_property_decl(folder, arena, prop))
736 }
737 ClassMemberKind::Method(method) => {
738 ClassMemberKind::Method(fold_method_decl(folder, arena, method))
739 }
740 ClassMemberKind::ClassConst(cc) => {
741 ClassMemberKind::ClassConst(fold_class_const_decl(folder, arena, cc))
742 }
743 ClassMemberKind::TraitUse(tu) => {
744 ClassMemberKind::TraitUse(folder.fold_trait_use(arena, tu))
745 }
746 };
747 ClassMember {
748 kind,
749 span: member.span,
750 }
751}
752
753pub fn fold_enum_member<'new, 'src, F: Fold<'src> + ?Sized>(
755 folder: &mut F,
756 arena: &'new Bump,
757 member: &EnumMember<'_, 'src>,
758) -> EnumMember<'new, 'src> {
759 let kind = match &member.kind {
760 EnumMemberKind::Case(case) => EnumMemberKind::Case(EnumCase {
761 name: case.name,
762 value: case.value.as_ref().map(|v| folder.fold_expr(arena, v)),
763 attributes: fold_attrs(folder, arena, &case.attributes),
764 doc_comment: case.doc_comment.as_ref().map(fold_comment),
765 }),
766 EnumMemberKind::Method(method) => {
767 EnumMemberKind::Method(fold_method_decl(folder, arena, method))
768 }
769 EnumMemberKind::ClassConst(cc) => {
770 EnumMemberKind::ClassConst(fold_class_const_decl(folder, arena, cc))
771 }
772 EnumMemberKind::TraitUse(tu) => EnumMemberKind::TraitUse(folder.fold_trait_use(arena, tu)),
773 };
774 EnumMember {
775 kind,
776 span: member.span,
777 }
778}
779
780pub fn fold_property_hook<'new, 'src, F: Fold<'src> + ?Sized>(
782 folder: &mut F,
783 arena: &'new Bump,
784 hook: &PropertyHook<'_, 'src>,
785) -> PropertyHook<'new, 'src> {
786 let body = match &hook.body {
787 PropertyHookBody::Block(block) => {
788 PropertyHookBody::Block(arena.alloc(fold_block(folder, arena, block)))
789 }
790 PropertyHookBody::Expression(expr) => {
791 PropertyHookBody::Expression(folder.fold_expr(arena, expr))
792 }
793 PropertyHookBody::Abstract => PropertyHookBody::Abstract,
794 };
795 PropertyHook {
796 kind: hook.kind,
797 body,
798 is_final: hook.is_final,
799 by_ref: hook.by_ref,
800 params: fold_params(folder, arena, &hook.params),
801 attributes: fold_attrs(folder, arena, &hook.attributes),
802 span: hook.span,
803 }
804}
805
806pub fn fold_type_hint<'new, 'src, F: Fold<'src> + ?Sized>(
808 folder: &mut F,
809 arena: &'new Bump,
810 type_hint: &TypeHint<'_, 'src>,
811) -> TypeHint<'new, 'src> {
812 let kind = match &type_hint.kind {
813 TypeHintKind::Named(name) => TypeHintKind::Named(folder.fold_name(arena, name)),
814 TypeHintKind::Keyword(builtin, span) => TypeHintKind::Keyword(*builtin, *span),
815 TypeHintKind::Nullable(inner) => {
816 TypeHintKind::Nullable(arena.alloc(folder.fold_type_hint(arena, inner)))
817 }
818 TypeHintKind::Union(types) => {
819 let mut new_types = ArenaVec::with_capacity_in(types.len(), arena);
820 for t in types.iter() {
821 new_types.push(folder.fold_type_hint(arena, t));
822 }
823 TypeHintKind::Union(new_types)
824 }
825 TypeHintKind::Intersection(types) => {
826 let mut new_types = ArenaVec::with_capacity_in(types.len(), arena);
827 for t in types.iter() {
828 new_types.push(folder.fold_type_hint(arena, t));
829 }
830 TypeHintKind::Intersection(new_types)
831 }
832 };
833 TypeHint {
834 kind,
835 span: type_hint.span,
836 }
837}
838
839pub fn fold_attribute<'new, 'src, F: Fold<'src> + ?Sized>(
841 folder: &mut F,
842 arena: &'new Bump,
843 attribute: &Attribute<'_, 'src>,
844) -> Attribute<'new, 'src> {
845 Attribute {
846 name: folder.fold_name(arena, &attribute.name),
847 args: fold_args(folder, arena, &attribute.args),
848 span: attribute.span,
849 }
850}
851
852pub fn fold_catch_clause<'new, 'src, F: Fold<'src> + ?Sized>(
854 folder: &mut F,
855 arena: &'new Bump,
856 catch: &CatchClause<'_, 'src>,
857) -> CatchClause<'new, 'src> {
858 let mut types = ArenaVec::with_capacity_in(catch.types.len(), arena);
859 for ty in catch.types.iter() {
860 types.push(folder.fold_name(arena, ty));
861 }
862 CatchClause {
863 types,
864 var: catch.var,
865 body: arena.alloc(folder.fold_block(arena, catch.body)),
866 span: catch.span,
867 }
868}
869
870pub fn fold_match_arm<'new, 'src, F: Fold<'src> + ?Sized>(
872 folder: &mut F,
873 arena: &'new Bump,
874 arm: &MatchArm<'_, 'src>,
875) -> MatchArm<'new, 'src> {
876 let conditions = arm.conditions.as_ref().map(|conds| {
877 let mut new_conds = ArenaVec::with_capacity_in(conds.len(), arena);
878 for c in conds.iter() {
879 new_conds.push(folder.fold_expr(arena, c));
880 }
881 new_conds
882 });
883 MatchArm {
884 conditions,
885 body: folder.fold_expr(arena, &arm.body),
886 span: arm.span,
887 }
888}
889
890pub fn fold_trait_use<'new, 'src, F: Fold<'src> + ?Sized>(
892 folder: &mut F,
893 arena: &'new Bump,
894 trait_use: &TraitUseDecl<'_, 'src>,
895) -> TraitUseDecl<'new, 'src> {
896 let mut traits = ArenaVec::with_capacity_in(trait_use.traits.len(), arena);
897 for t in trait_use.traits.iter() {
898 traits.push(folder.fold_name(arena, t));
899 }
900 let mut adaptations = ArenaVec::with_capacity_in(trait_use.adaptations.len(), arena);
901 for a in trait_use.adaptations.iter() {
902 adaptations.push(folder.fold_trait_adaptation(arena, a));
903 }
904 TraitUseDecl {
905 traits,
906 adaptations,
907 adaptations_brace_start: trait_use.adaptations_brace_start,
908 doc_comment: trait_use.doc_comment.as_ref().map(fold_comment),
909 }
910}
911
912pub fn fold_trait_adaptation<'new, 'src, F: Fold<'src> + ?Sized>(
914 folder: &mut F,
915 arena: &'new Bump,
916 adaptation: &TraitAdaptation<'_, 'src>,
917) -> TraitAdaptation<'new, 'src> {
918 let kind = match &adaptation.kind {
919 TraitAdaptationKind::Precedence {
920 trait_name,
921 method,
922 insteadof,
923 } => {
924 let mut new_insteadof = ArenaVec::with_capacity_in(insteadof.len(), arena);
925 for n in insteadof.iter() {
926 new_insteadof.push(folder.fold_name(arena, n));
927 }
928 TraitAdaptationKind::Precedence {
929 trait_name: folder.fold_name(arena, trait_name),
930 method: folder.fold_name(arena, method),
931 insteadof: new_insteadof,
932 }
933 }
934 TraitAdaptationKind::Alias {
935 trait_name,
936 method,
937 new_modifier,
938 new_name,
939 } => TraitAdaptationKind::Alias {
940 trait_name: trait_name.as_ref().map(|n| folder.fold_name(arena, n)),
941 method: folder.fold_name(arena, method),
942 new_modifier: *new_modifier,
943 new_name: new_name.as_ref().map(|n| folder.fold_name(arena, n)),
944 },
945 };
946 TraitAdaptation {
947 kind,
948 span: adaptation.span,
949 }
950}
951
952pub fn fold_name<'new, 'src, F: Fold<'src> + ?Sized>(
954 _folder: &mut F,
955 arena: &'new Bump,
956 name: &Name<'_, 'src>,
957) -> Name<'new, 'src> {
958 match name {
959 Name::Simple { value, span } => Name::Simple { value, span: *span },
960 Name::Complex { parts, kind, span } => {
961 let mut new_parts = ArenaVec::with_capacity_in(parts.len(), arena);
962 for &part in parts.iter() {
963 new_parts.push(part);
964 }
965 Name::Complex {
966 parts: new_parts,
967 kind: *kind,
968 span: *span,
969 }
970 }
971 Name::Error { span } => Name::Error { span: *span },
972 }
973}
974
975fn fold_function_decl<'new, 'src, F: Fold<'src> + ?Sized>(
980 folder: &mut F,
981 arena: &'new Bump,
982 func: &FunctionDecl<'_, 'src>,
983) -> FunctionDecl<'new, 'src> {
984 FunctionDecl {
985 name: func.name,
986 params: fold_params(folder, arena, &func.params),
987 body: arena.alloc(folder.fold_block(arena, func.body)),
988 return_type: func
989 .return_type
990 .as_ref()
991 .map(|t| folder.fold_type_hint(arena, t)),
992 by_ref: func.by_ref,
993 attributes: fold_attrs(folder, arena, &func.attributes),
994 doc_comment: func.doc_comment.as_ref().map(fold_comment),
995 }
996}
997
998fn fold_method_decl<'new, 'src, F: Fold<'src> + ?Sized>(
999 folder: &mut F,
1000 arena: &'new Bump,
1001 method: &MethodDecl<'_, 'src>,
1002) -> MethodDecl<'new, 'src> {
1003 MethodDecl {
1004 name: method.name,
1005 visibility: method.visibility,
1006 is_static: method.is_static,
1007 is_abstract: method.is_abstract,
1008 is_final: method.is_final,
1009 by_ref: method.by_ref,
1010 params: fold_params(folder, arena, &method.params),
1011 return_type: method
1012 .return_type
1013 .as_ref()
1014 .map(|t| folder.fold_type_hint(arena, t)),
1015 body: method
1016 .body
1017 .map(|b| &*arena.alloc(folder.fold_block(arena, b))),
1018 attributes: fold_attrs(folder, arena, &method.attributes),
1019 doc_comment: method.doc_comment.as_ref().map(fold_comment),
1020 }
1021}
1022
1023fn fold_property_decl<'new, 'src, F: Fold<'src> + ?Sized>(
1024 folder: &mut F,
1025 arena: &'new Bump,
1026 prop: &PropertyDecl<'_, 'src>,
1027) -> PropertyDecl<'new, 'src> {
1028 PropertyDecl {
1029 name: prop.name,
1030 visibility: prop.visibility,
1031 set_visibility: prop.set_visibility,
1032 is_static: prop.is_static,
1033 is_readonly: prop.is_readonly,
1034 type_hint: prop
1035 .type_hint
1036 .as_ref()
1037 .map(|t| folder.fold_type_hint(arena, t)),
1038 default: prop.default.as_ref().map(|d| folder.fold_expr(arena, d)),
1039 attributes: fold_attrs(folder, arena, &prop.attributes),
1040 hooks: fold_hooks(folder, arena, &prop.hooks),
1041 doc_comment: prop.doc_comment.as_ref().map(fold_comment),
1042 }
1043}
1044
1045fn fold_class_const_decl<'new, 'src, F: Fold<'src> + ?Sized>(
1046 folder: &mut F,
1047 arena: &'new Bump,
1048 cc: &ClassConstDecl<'_, 'src>,
1049) -> ClassConstDecl<'new, 'src> {
1050 ClassConstDecl {
1051 name: cc.name,
1052 visibility: cc.visibility,
1053 is_final: cc.is_final,
1054 type_hint: cc
1055 .type_hint
1056 .map(|t| &*arena.alloc(folder.fold_type_hint(arena, t))),
1057 value: folder.fold_expr(arena, &cc.value),
1058 attributes: fold_attrs(folder, arena, &cc.attributes),
1059 doc_comment: cc.doc_comment.as_ref().map(fold_comment),
1060 }
1061}
1062
1063fn fold_class_decl<'new, 'src, F: Fold<'src> + ?Sized>(
1064 folder: &mut F,
1065 arena: &'new Bump,
1066 class: &ClassDecl<'_, 'src>,
1067) -> ClassDecl<'new, 'src> {
1068 let mut members = ArenaVec::with_capacity_in(class.body.members.len(), arena);
1069 for member in class.body.members.iter() {
1070 members.push(folder.fold_class_member(arena, member));
1071 }
1072 ClassDecl {
1073 name: class.name,
1074 modifiers: class.modifiers.clone(),
1075 extends: class.extends.as_ref().map(|n| folder.fold_name(arena, n)),
1076 implements: {
1077 let mut v = ArenaVec::with_capacity_in(class.implements.len(), arena);
1078 for n in class.implements.iter() {
1079 v.push(folder.fold_name(arena, n));
1080 }
1081 v
1082 },
1083 body: ClassBody {
1084 members,
1085 span: class.body.span,
1086 },
1087 attributes: fold_attrs(folder, arena, &class.attributes),
1088 doc_comment: class.doc_comment.as_ref().map(fold_comment),
1089 }
1090}
1091
1092fn fold_interface_decl<'new, 'src, F: Fold<'src> + ?Sized>(
1093 folder: &mut F,
1094 arena: &'new Bump,
1095 iface: &InterfaceDecl<'_, 'src>,
1096) -> InterfaceDecl<'new, 'src> {
1097 let mut extends = ArenaVec::with_capacity_in(iface.extends.len(), arena);
1098 for n in iface.extends.iter() {
1099 extends.push(folder.fold_name(arena, n));
1100 }
1101 let mut members = ArenaVec::with_capacity_in(iface.body.members.len(), arena);
1102 for member in iface.body.members.iter() {
1103 members.push(folder.fold_class_member(arena, member));
1104 }
1105 InterfaceDecl {
1106 name: iface.name,
1107 extends,
1108 body: ClassBody {
1109 members,
1110 span: iface.body.span,
1111 },
1112 attributes: fold_attrs(folder, arena, &iface.attributes),
1113 doc_comment: iface.doc_comment.as_ref().map(fold_comment),
1114 }
1115}
1116
1117fn fold_trait_decl<'new, 'src, F: Fold<'src> + ?Sized>(
1118 folder: &mut F,
1119 arena: &'new Bump,
1120 t: &TraitDecl<'_, 'src>,
1121) -> TraitDecl<'new, 'src> {
1122 let mut members = ArenaVec::with_capacity_in(t.body.members.len(), arena);
1123 for member in t.body.members.iter() {
1124 members.push(folder.fold_class_member(arena, member));
1125 }
1126 TraitDecl {
1127 name: t.name,
1128 body: ClassBody {
1129 members,
1130 span: t.body.span,
1131 },
1132 attributes: fold_attrs(folder, arena, &t.attributes),
1133 doc_comment: t.doc_comment.as_ref().map(fold_comment),
1134 }
1135}
1136
1137fn fold_enum_decl<'new, 'src, F: Fold<'src> + ?Sized>(
1138 folder: &mut F,
1139 arena: &'new Bump,
1140 e: &EnumDecl<'_, 'src>,
1141) -> EnumDecl<'new, 'src> {
1142 let mut members = ArenaVec::with_capacity_in(e.body.members.len(), arena);
1143 for member in e.body.members.iter() {
1144 members.push(folder.fold_enum_member(arena, member));
1145 }
1146 EnumDecl {
1147 name: e.name,
1148 scalar_type: e.scalar_type.as_ref().map(|n| folder.fold_name(arena, n)),
1149 implements: {
1150 let mut v = ArenaVec::with_capacity_in(e.implements.len(), arena);
1151 for n in e.implements.iter() {
1152 v.push(folder.fold_name(arena, n));
1153 }
1154 v
1155 },
1156 body: EnumBody {
1157 members,
1158 span: e.body.span,
1159 },
1160 attributes: fold_attrs(folder, arena, &e.attributes),
1161 doc_comment: e.doc_comment.as_ref().map(fold_comment),
1162 }
1163}
1164
1165fn fold_stmts<'new, 'src, F: Fold<'src> + ?Sized>(
1170 folder: &mut F,
1171 arena: &'new Bump,
1172 stmts: &[Stmt<'_, 'src>],
1173) -> ArenaVec<'new, Stmt<'new, 'src>> {
1174 let mut vec = ArenaVec::with_capacity_in(stmts.len(), arena);
1175 for stmt in stmts {
1176 vec.push(folder.fold_stmt(arena, stmt));
1177 }
1178 vec
1179}
1180
1181pub fn fold_block<'new, 'src, F: Fold<'src> + ?Sized>(
1183 folder: &mut F,
1184 arena: &'new Bump,
1185 block: &Block<'_, 'src>,
1186) -> Block<'new, 'src> {
1187 Block {
1188 stmts: fold_stmts(folder, arena, &block.stmts),
1189 span: block.span,
1190 }
1191}
1192
1193fn fold_exprs<'new, 'src, F: Fold<'src> + ?Sized>(
1194 folder: &mut F,
1195 arena: &'new Bump,
1196 exprs: &[Expr<'_, 'src>],
1197) -> ArenaVec<'new, Expr<'new, 'src>> {
1198 let mut vec = ArenaVec::with_capacity_in(exprs.len(), arena);
1199 for expr in exprs {
1200 vec.push(folder.fold_expr(arena, expr));
1201 }
1202 vec
1203}
1204
1205fn fold_args<'new, 'src, F: Fold<'src> + ?Sized>(
1206 folder: &mut F,
1207 arena: &'new Bump,
1208 args: &[Arg<'_, 'src>],
1209) -> ArenaVec<'new, Arg<'new, 'src>> {
1210 let mut vec = ArenaVec::with_capacity_in(args.len(), arena);
1211 for arg in args {
1212 vec.push(folder.fold_arg(arena, arg));
1213 }
1214 vec
1215}
1216
1217fn fold_params<'new, 'src, F: Fold<'src> + ?Sized>(
1218 folder: &mut F,
1219 arena: &'new Bump,
1220 params: &[Param<'_, 'src>],
1221) -> ArenaVec<'new, Param<'new, 'src>> {
1222 let mut vec = ArenaVec::with_capacity_in(params.len(), arena);
1223 for param in params {
1224 vec.push(folder.fold_param(arena, param));
1225 }
1226 vec
1227}
1228
1229fn fold_attrs<'new, 'src, F: Fold<'src> + ?Sized>(
1230 folder: &mut F,
1231 arena: &'new Bump,
1232 attrs: &[Attribute<'_, 'src>],
1233) -> ArenaVec<'new, Attribute<'new, 'src>> {
1234 let mut vec = ArenaVec::with_capacity_in(attrs.len(), arena);
1235 for attr in attrs {
1236 vec.push(folder.fold_attribute(arena, attr));
1237 }
1238 vec
1239}
1240
1241fn fold_hooks<'new, 'src, F: Fold<'src> + ?Sized>(
1242 folder: &mut F,
1243 arena: &'new Bump,
1244 hooks: &[PropertyHook<'_, 'src>],
1245) -> ArenaVec<'new, PropertyHook<'new, 'src>> {
1246 let mut vec = ArenaVec::with_capacity_in(hooks.len(), arena);
1247 for hook in hooks {
1248 vec.push(folder.fold_property_hook(arena, hook));
1249 }
1250 vec
1251}
1252
1253fn fold_string_parts<'new, 'src, F: Fold<'src> + ?Sized>(
1254 folder: &mut F,
1255 arena: &'new Bump,
1256 parts: &[StringPart<'_, 'src>],
1257) -> ArenaVec<'new, StringPart<'new, 'src>> {
1258 let mut vec = ArenaVec::with_capacity_in(parts.len(), arena);
1259 for part in parts {
1260 vec.push(match part {
1261 StringPart::Literal(s) => StringPart::Literal(arena.alloc_str(s)),
1262 StringPart::Expr(e) => StringPart::Expr(folder.fold_expr(arena, e)),
1263 });
1264 }
1265 vec
1266}
1267
1268pub fn fold_name_str<'new, 'src>(
1274 name: NameStr<'_, 'src>,
1275 arena: &'new Bump,
1276) -> NameStr<'new, 'src> {
1277 if let Some(s) = name.__into_src_str() {
1278 NameStr::__src(s)
1279 } else {
1280 NameStr::__arena(arena.alloc_str(name.as_str()))
1281 }
1282}
1283
1284fn fold_comment<'src>(comment: &Comment<'src>) -> Comment<'src> {
1285 Comment {
1286 kind: comment.kind,
1287 text: comment.text,
1288 span: comment.span,
1289 }
1290}