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