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