1use std::ops::ControlFlow;
2
3use crate::ast::*;
4
5pub trait Visitor<'arena, 'src> {
36 fn visit_program(&mut self, program: &Program<'arena, 'src>) -> ControlFlow<()> {
37 walk_program(self, program)
38 }
39
40 fn visit_stmt(&mut self, stmt: &Stmt<'arena, 'src>) -> ControlFlow<()> {
41 walk_stmt(self, stmt)
42 }
43
44 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
45 walk_expr(self, expr)
46 }
47
48 fn visit_param(&mut self, param: &Param<'arena, 'src>) -> ControlFlow<()> {
49 walk_param(self, param)
50 }
51
52 fn visit_arg(&mut self, arg: &Arg<'arena, 'src>) -> ControlFlow<()> {
53 walk_arg(self, arg)
54 }
55
56 fn visit_class_member(&mut self, member: &ClassMember<'arena, 'src>) -> ControlFlow<()> {
57 walk_class_member(self, member)
58 }
59
60 fn visit_enum_member(&mut self, member: &EnumMember<'arena, 'src>) -> ControlFlow<()> {
61 walk_enum_member(self, member)
62 }
63
64 fn visit_property_hook(&mut self, hook: &PropertyHook<'arena, 'src>) -> ControlFlow<()> {
65 walk_property_hook(self, hook)
66 }
67
68 fn visit_type_hint(&mut self, type_hint: &TypeHint<'arena, 'src>) -> ControlFlow<()> {
69 walk_type_hint(self, type_hint)
70 }
71
72 fn visit_attribute(&mut self, attribute: &Attribute<'arena, 'src>) -> ControlFlow<()> {
73 walk_attribute(self, attribute)
74 }
75
76 fn visit_catch_clause(&mut self, catch: &CatchClause<'arena, 'src>) -> ControlFlow<()> {
77 walk_catch_clause(self, catch)
78 }
79
80 fn visit_match_arm(&mut self, arm: &MatchArm<'arena, 'src>) -> ControlFlow<()> {
81 walk_match_arm(self, arm)
82 }
83
84 fn visit_closure_use_var(&mut self, _var: &ClosureUseVar<'src>) -> ControlFlow<()> {
85 ControlFlow::Continue(())
86 }
87
88 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl<'arena, 'src>) -> ControlFlow<()> {
89 walk_trait_use(self, trait_use)
90 }
91
92 fn visit_trait_adaptation(
93 &mut self,
94 _adaptation: &TraitAdaptation<'arena, 'src>,
95 ) -> ControlFlow<()> {
96 ControlFlow::Continue(())
97 }
98
99 fn visit_name(&mut self, _name: &Name<'arena, 'src>) -> ControlFlow<()> {
100 ControlFlow::Continue(())
101 }
102
103 fn visit_comment(&mut self, _comment: &Comment<'src>) -> ControlFlow<()> {
108 ControlFlow::Continue(())
109 }
110}
111
112pub fn walk_name<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
121 visitor: &mut V,
122 name: &Name<'arena, 'src>,
123) -> ControlFlow<()> {
124 visitor.visit_name(name)
125}
126
127pub fn walk_comments<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
140 visitor: &mut V,
141 comments: &[Comment<'src>],
142) -> ControlFlow<()> {
143 for comment in comments {
144 visitor.visit_comment(comment)?;
145 }
146 ControlFlow::Continue(())
147}
148
149pub fn walk_program<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
165 visitor: &mut V,
166 program: &Program<'arena, 'src>,
167) -> ControlFlow<()> {
168 for stmt in program.stmts.iter() {
169 visitor.visit_stmt(stmt)?;
170 }
171 ControlFlow::Continue(())
172}
173
174pub fn walk_stmt<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
179 visitor: &mut V,
180 stmt: &Stmt<'arena, 'src>,
181) -> ControlFlow<()> {
182 match &stmt.kind {
183 StmtKind::Expression(expr) => {
184 visitor.visit_expr(expr)?;
185 }
186 StmtKind::Echo(exprs) => {
187 for expr in exprs.iter() {
188 visitor.visit_expr(expr)?;
189 }
190 }
191 StmtKind::Return(expr) => {
192 if let Some(expr) = expr {
193 visitor.visit_expr(expr)?;
194 }
195 }
196 StmtKind::Block(stmts) => {
197 for stmt in stmts.iter() {
198 visitor.visit_stmt(stmt)?;
199 }
200 }
201 StmtKind::If(if_stmt) => {
202 visitor.visit_expr(&if_stmt.condition)?;
203 visitor.visit_stmt(if_stmt.then_branch)?;
204 for elseif in if_stmt.elseif_branches.iter() {
205 visitor.visit_expr(&elseif.condition)?;
206 visitor.visit_stmt(&elseif.body)?;
207 }
208 if let Some(else_branch) = &if_stmt.else_branch {
209 visitor.visit_stmt(else_branch)?;
210 }
211 }
212 StmtKind::While(while_stmt) => {
213 visitor.visit_expr(&while_stmt.condition)?;
214 visitor.visit_stmt(while_stmt.body)?;
215 }
216 StmtKind::For(for_stmt) => {
217 for expr in for_stmt.init.iter() {
218 visitor.visit_expr(expr)?;
219 }
220 for expr in for_stmt.condition.iter() {
221 visitor.visit_expr(expr)?;
222 }
223 for expr in for_stmt.update.iter() {
224 visitor.visit_expr(expr)?;
225 }
226 visitor.visit_stmt(for_stmt.body)?;
227 }
228 StmtKind::Foreach(foreach_stmt) => {
229 visitor.visit_expr(&foreach_stmt.expr)?;
230 if let Some(key) = &foreach_stmt.key {
231 visitor.visit_expr(key)?;
232 }
233 visitor.visit_expr(&foreach_stmt.value)?;
234 visitor.visit_stmt(foreach_stmt.body)?;
235 }
236 StmtKind::DoWhile(do_while) => {
237 visitor.visit_stmt(do_while.body)?;
238 visitor.visit_expr(&do_while.condition)?;
239 }
240 StmtKind::Function(func) => {
241 walk_function_like(visitor, &func.attributes, &func.params, &func.return_type)?;
242 for stmt in func.body.iter() {
243 visitor.visit_stmt(stmt)?;
244 }
245 }
246 StmtKind::Break(expr) | StmtKind::Continue(expr) => {
247 if let Some(expr) = expr {
248 visitor.visit_expr(expr)?;
249 }
250 }
251 StmtKind::Switch(switch_stmt) => {
252 visitor.visit_expr(&switch_stmt.expr)?;
253 for case in switch_stmt.cases.iter() {
254 if let Some(value) = &case.value {
255 visitor.visit_expr(value)?;
256 }
257 for stmt in case.body.iter() {
258 visitor.visit_stmt(stmt)?;
259 }
260 }
261 }
262 StmtKind::Throw(expr) => {
263 visitor.visit_expr(expr)?;
264 }
265 StmtKind::TryCatch(tc) => {
266 for stmt in tc.body.iter() {
267 visitor.visit_stmt(stmt)?;
268 }
269 for catch in tc.catches.iter() {
270 visitor.visit_catch_clause(catch)?;
271 }
272 if let Some(finally) = &tc.finally {
273 for stmt in finally.iter() {
274 visitor.visit_stmt(stmt)?;
275 }
276 }
277 }
278 StmtKind::Declare(decl) => {
279 for (_, expr) in decl.directives.iter() {
280 visitor.visit_expr(expr)?;
281 }
282 if let Some(body) = decl.body {
283 visitor.visit_stmt(body)?;
284 }
285 }
286 StmtKind::Unset(exprs) | StmtKind::Global(exprs) => {
287 for expr in exprs.iter() {
288 visitor.visit_expr(expr)?;
289 }
290 }
291 StmtKind::Class(class) => {
292 walk_attributes(visitor, &class.attributes)?;
293 if let Some(extends) = &class.extends {
294 visitor.visit_name(extends)?;
295 }
296 for name in class.implements.iter() {
297 visitor.visit_name(name)?;
298 }
299 for member in class.members.iter() {
300 visitor.visit_class_member(member)?;
301 }
302 }
303 StmtKind::Interface(iface) => {
304 walk_attributes(visitor, &iface.attributes)?;
305 for name in iface.extends.iter() {
306 visitor.visit_name(name)?;
307 }
308 for member in iface.members.iter() {
309 visitor.visit_class_member(member)?;
310 }
311 }
312 StmtKind::Trait(trait_decl) => {
313 walk_attributes(visitor, &trait_decl.attributes)?;
314 for member in trait_decl.members.iter() {
315 visitor.visit_class_member(member)?;
316 }
317 }
318 StmtKind::Enum(enum_decl) => {
319 walk_attributes(visitor, &enum_decl.attributes)?;
320 if let Some(scalar_type) = &enum_decl.scalar_type {
321 visitor.visit_name(scalar_type)?;
322 }
323 for name in enum_decl.implements.iter() {
324 visitor.visit_name(name)?;
325 }
326 for member in enum_decl.members.iter() {
327 visitor.visit_enum_member(member)?;
328 }
329 }
330 StmtKind::Namespace(ns) => {
331 if let NamespaceBody::Braced(stmts) = &ns.body {
332 for stmt in stmts.iter() {
333 visitor.visit_stmt(stmt)?;
334 }
335 }
336 }
337 StmtKind::Const(items) => {
338 for item in items.iter() {
339 walk_attributes(visitor, &item.attributes)?;
340 visitor.visit_expr(&item.value)?;
341 }
342 }
343 StmtKind::StaticVar(vars) => {
344 for var in vars.iter() {
345 if let Some(default) = &var.default {
346 visitor.visit_expr(default)?;
347 }
348 }
349 }
350 StmtKind::Use(decl) => {
351 for item in decl.uses.iter() {
352 visitor.visit_name(&item.name)?;
353 }
354 }
355 StmtKind::Goto(_)
356 | StmtKind::Label(_)
357 | StmtKind::Nop
358 | StmtKind::InlineHtml(_)
359 | StmtKind::HaltCompiler(_)
360 | StmtKind::Error => {}
361 }
362 ControlFlow::Continue(())
363}
364
365pub fn walk_expr<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
370 visitor: &mut V,
371 expr: &Expr<'arena, 'src>,
372) -> ControlFlow<()> {
373 match &expr.kind {
374 ExprKind::Assign(assign) => {
375 visitor.visit_expr(assign.target)?;
376 visitor.visit_expr(assign.value)?;
377 }
378 ExprKind::Binary(binary) => {
379 visitor.visit_expr(binary.left)?;
380 visitor.visit_expr(binary.right)?;
381 }
382 ExprKind::UnaryPrefix(unary) => {
383 visitor.visit_expr(unary.operand)?;
384 }
385 ExprKind::UnaryPostfix(unary) => {
386 visitor.visit_expr(unary.operand)?;
387 }
388 ExprKind::Ternary(ternary) => {
389 visitor.visit_expr(ternary.condition)?;
390 if let Some(then_expr) = &ternary.then_expr {
391 visitor.visit_expr(then_expr)?;
392 }
393 visitor.visit_expr(ternary.else_expr)?;
394 }
395 ExprKind::NullCoalesce(nc) => {
396 visitor.visit_expr(nc.left)?;
397 visitor.visit_expr(nc.right)?;
398 }
399 ExprKind::FunctionCall(call) => {
400 visitor.visit_expr(call.name)?;
401 for arg in call.args.iter() {
402 visitor.visit_arg(arg)?;
403 }
404 }
405 ExprKind::Array(elements) => {
406 for elem in elements.iter() {
407 if let Some(key) = &elem.key {
408 visitor.visit_expr(key)?;
409 }
410 visitor.visit_expr(&elem.value)?;
411 }
412 }
413 ExprKind::ArrayAccess(access) => {
414 visitor.visit_expr(access.array)?;
415 if let Some(index) = &access.index {
416 visitor.visit_expr(index)?;
417 }
418 }
419 ExprKind::Print(expr) => {
420 visitor.visit_expr(expr)?;
421 }
422 ExprKind::Parenthesized(expr) => {
423 visitor.visit_expr(expr)?;
424 }
425 ExprKind::Cast(_, expr) => {
426 visitor.visit_expr(expr)?;
427 }
428 ExprKind::ErrorSuppress(expr) => {
429 visitor.visit_expr(expr)?;
430 }
431 ExprKind::Isset(exprs) => {
432 for expr in exprs.iter() {
433 visitor.visit_expr(expr)?;
434 }
435 }
436 ExprKind::Empty(expr) => {
437 visitor.visit_expr(expr)?;
438 }
439 ExprKind::Include(_, expr) => {
440 visitor.visit_expr(expr)?;
441 }
442 ExprKind::Eval(expr) => {
443 visitor.visit_expr(expr)?;
444 }
445 ExprKind::Exit(expr) => {
446 if let Some(expr) = expr {
447 visitor.visit_expr(expr)?;
448 }
449 }
450 ExprKind::Clone(expr) => {
451 visitor.visit_expr(expr)?;
452 }
453 ExprKind::CloneWith(object, overrides) => {
454 visitor.visit_expr(object)?;
455 visitor.visit_expr(overrides)?;
456 }
457 ExprKind::New(new_expr) => {
458 visitor.visit_expr(new_expr.class)?;
459 for arg in new_expr.args.iter() {
460 visitor.visit_arg(arg)?;
461 }
462 }
463 ExprKind::PropertyAccess(access) | ExprKind::NullsafePropertyAccess(access) => {
464 visitor.visit_expr(access.object)?;
465 visitor.visit_expr(access.property)?;
466 }
467 ExprKind::MethodCall(call) | ExprKind::NullsafeMethodCall(call) => {
468 visitor.visit_expr(call.object)?;
469 visitor.visit_expr(call.method)?;
470 for arg in call.args.iter() {
471 visitor.visit_arg(arg)?;
472 }
473 }
474 ExprKind::StaticPropertyAccess(access) | ExprKind::ClassConstAccess(access) => {
475 visitor.visit_expr(access.class)?;
476 visitor.visit_expr(access.member)?;
477 }
478 ExprKind::ClassConstAccessDynamic { class, member }
479 | ExprKind::StaticPropertyAccessDynamic { class, member } => {
480 visitor.visit_expr(class)?;
481 visitor.visit_expr(member)?;
482 }
483 ExprKind::StaticMethodCall(call) => {
484 visitor.visit_expr(call.class)?;
485 visitor.visit_expr(call.method)?;
486 for arg in call.args.iter() {
487 visitor.visit_arg(arg)?;
488 }
489 }
490 ExprKind::StaticDynMethodCall(call) => {
491 visitor.visit_expr(call.class)?;
492 visitor.visit_expr(call.method)?;
493 for arg in call.args.iter() {
494 visitor.visit_arg(arg)?;
495 }
496 }
497 ExprKind::Closure(closure) => {
498 walk_function_like(
499 visitor,
500 &closure.attributes,
501 &closure.params,
502 &closure.return_type,
503 )?;
504 for use_var in closure.use_vars.iter() {
505 visitor.visit_closure_use_var(use_var)?;
506 }
507 for stmt in closure.body.iter() {
508 visitor.visit_stmt(stmt)?;
509 }
510 }
511 ExprKind::ArrowFunction(arrow) => {
512 walk_function_like(
513 visitor,
514 &arrow.attributes,
515 &arrow.params,
516 &arrow.return_type,
517 )?;
518 visitor.visit_expr(arrow.body)?;
519 }
520 ExprKind::Match(match_expr) => {
521 visitor.visit_expr(match_expr.subject)?;
522 for arm in match_expr.arms.iter() {
523 visitor.visit_match_arm(arm)?;
524 }
525 }
526 ExprKind::ThrowExpr(expr) => {
527 visitor.visit_expr(expr)?;
528 }
529 ExprKind::Yield(yield_expr) => {
530 if let Some(key) = &yield_expr.key {
531 visitor.visit_expr(key)?;
532 }
533 if let Some(value) = &yield_expr.value {
534 visitor.visit_expr(value)?;
535 }
536 }
537 ExprKind::AnonymousClass(class) => {
538 walk_attributes(visitor, &class.attributes)?;
539 for member in class.members.iter() {
540 visitor.visit_class_member(member)?;
541 }
542 }
543 ExprKind::InterpolatedString(parts)
544 | ExprKind::Heredoc { parts, .. }
545 | ExprKind::ShellExec(parts) => {
546 for part in parts.iter() {
547 if let StringPart::Expr(e) = part {
548 visitor.visit_expr(e)?;
549 }
550 }
551 }
552 ExprKind::VariableVariable(inner) => {
553 visitor.visit_expr(inner)?;
554 }
555 ExprKind::CallableCreate(cc) => match &cc.kind {
556 CallableCreateKind::Function(name) => visitor.visit_expr(name)?,
557 CallableCreateKind::Method { object, method }
558 | CallableCreateKind::NullsafeMethod { object, method } => {
559 visitor.visit_expr(object)?;
560 visitor.visit_expr(method)?;
561 }
562 CallableCreateKind::StaticMethod { class, method } => {
563 visitor.visit_expr(class)?;
564 visitor.visit_expr(method)?;
565 }
566 },
567 ExprKind::Int(_)
568 | ExprKind::Float(_)
569 | ExprKind::String(_)
570 | ExprKind::Bool(_)
571 | ExprKind::Null
572 | ExprKind::Omit
573 | ExprKind::Variable(_)
574 | ExprKind::Identifier(_)
575 | ExprKind::MagicConst(_)
576 | ExprKind::Nowdoc { .. }
577 | ExprKind::Error => {}
578 }
579 ControlFlow::Continue(())
580}
581
582pub fn walk_param<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
584 visitor: &mut V,
585 param: &Param<'arena, 'src>,
586) -> ControlFlow<()> {
587 walk_attributes(visitor, ¶m.attributes)?;
588 if let Some(type_hint) = ¶m.type_hint {
589 visitor.visit_type_hint(type_hint)?;
590 }
591 if let Some(default) = ¶m.default {
592 visitor.visit_expr(default)?;
593 }
594 for hook in param.hooks.iter() {
595 visitor.visit_property_hook(hook)?;
596 }
597 ControlFlow::Continue(())
598}
599
600pub fn walk_arg<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
602 visitor: &mut V,
603 arg: &Arg<'arena, 'src>,
604) -> ControlFlow<()> {
605 visitor.visit_expr(&arg.value)
606}
607
608pub fn walk_class_member<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
610 visitor: &mut V,
611 member: &ClassMember<'arena, 'src>,
612) -> ControlFlow<()> {
613 match &member.kind {
614 ClassMemberKind::Property(prop) => {
615 walk_property_decl(visitor, prop)?;
616 }
617 ClassMemberKind::Method(method) => {
618 walk_method_decl(visitor, method)?;
619 }
620 ClassMemberKind::ClassConst(cc) => {
621 walk_class_const_decl(visitor, cc)?;
622 }
623 ClassMemberKind::TraitUse(trait_use) => {
624 visitor.visit_trait_use(trait_use)?;
625 }
626 }
627 ControlFlow::Continue(())
628}
629
630pub fn walk_property_hook<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
632 visitor: &mut V,
633 hook: &PropertyHook<'arena, 'src>,
634) -> ControlFlow<()> {
635 walk_attributes(visitor, &hook.attributes)?;
636 for param in hook.params.iter() {
637 visitor.visit_param(param)?;
638 }
639 match &hook.body {
640 PropertyHookBody::Block(stmts) => {
641 for stmt in stmts.iter() {
642 visitor.visit_stmt(stmt)?;
643 }
644 }
645 PropertyHookBody::Expression(expr) => {
646 visitor.visit_expr(expr)?;
647 }
648 PropertyHookBody::Abstract => {}
649 }
650 ControlFlow::Continue(())
651}
652
653pub fn walk_enum_member<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
655 visitor: &mut V,
656 member: &EnumMember<'arena, 'src>,
657) -> ControlFlow<()> {
658 match &member.kind {
659 EnumMemberKind::Case(case) => {
660 walk_attributes(visitor, &case.attributes)?;
661 if let Some(value) = &case.value {
662 visitor.visit_expr(value)?;
663 }
664 }
665 EnumMemberKind::Method(method) => {
666 walk_method_decl(visitor, method)?;
667 }
668 EnumMemberKind::ClassConst(cc) => {
669 walk_class_const_decl(visitor, cc)?;
670 }
671 EnumMemberKind::TraitUse(trait_use) => {
672 visitor.visit_trait_use(trait_use)?;
673 }
674 }
675 ControlFlow::Continue(())
676}
677
678pub fn walk_type_hint<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
680 visitor: &mut V,
681 type_hint: &TypeHint<'arena, 'src>,
682) -> ControlFlow<()> {
683 match &type_hint.kind {
684 TypeHintKind::Nullable(inner) => {
685 visitor.visit_type_hint(inner)?;
686 }
687 TypeHintKind::Union(types) | TypeHintKind::Intersection(types) => {
688 for ty in types.iter() {
689 visitor.visit_type_hint(ty)?;
690 }
691 }
692 TypeHintKind::Named(name) => {
693 visitor.visit_name(name)?;
694 }
695 TypeHintKind::Keyword(_, _) => {}
696 }
697 ControlFlow::Continue(())
698}
699
700pub fn walk_attribute<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
702 visitor: &mut V,
703 attribute: &Attribute<'arena, 'src>,
704) -> ControlFlow<()> {
705 visitor.visit_name(&attribute.name)?;
706 for arg in attribute.args.iter() {
707 visitor.visit_arg(arg)?;
708 }
709 ControlFlow::Continue(())
710}
711
712pub fn walk_catch_clause<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
714 visitor: &mut V,
715 catch: &CatchClause<'arena, 'src>,
716) -> ControlFlow<()> {
717 for ty in catch.types.iter() {
718 visitor.visit_name(ty)?;
719 }
720 for stmt in catch.body.iter() {
721 visitor.visit_stmt(stmt)?;
722 }
723 ControlFlow::Continue(())
724}
725
726pub fn walk_match_arm<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
728 visitor: &mut V,
729 arm: &MatchArm<'arena, 'src>,
730) -> ControlFlow<()> {
731 if let Some(conditions) = &arm.conditions {
732 for cond in conditions.iter() {
733 visitor.visit_expr(cond)?;
734 }
735 }
736 visitor.visit_expr(&arm.body)
737}
738
739pub fn walk_trait_use<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
741 visitor: &mut V,
742 trait_use: &TraitUseDecl<'arena, 'src>,
743) -> ControlFlow<()> {
744 for name in trait_use.traits.iter() {
745 visitor.visit_name(name)?;
746 }
747 for adaptation in trait_use.adaptations.iter() {
748 visitor.visit_trait_adaptation(adaptation)?;
749 }
750 ControlFlow::Continue(())
751}
752
753fn walk_function_like<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
760 visitor: &mut V,
761 attributes: &[Attribute<'arena, 'src>],
762 params: &[Param<'arena, 'src>],
763 return_type: &Option<TypeHint<'arena, 'src>>,
764) -> ControlFlow<()> {
765 walk_attributes(visitor, attributes)?;
766 for param in params.iter() {
767 visitor.visit_param(param)?;
768 }
769 if let Some(ret) = return_type {
770 visitor.visit_type_hint(ret)?;
771 }
772 ControlFlow::Continue(())
773}
774
775fn walk_method_decl<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
777 visitor: &mut V,
778 method: &MethodDecl<'arena, 'src>,
779) -> ControlFlow<()> {
780 walk_function_like(
781 visitor,
782 &method.attributes,
783 &method.params,
784 &method.return_type,
785 )?;
786 if let Some(body) = &method.body {
787 for stmt in body.iter() {
788 visitor.visit_stmt(stmt)?;
789 }
790 }
791 ControlFlow::Continue(())
792}
793
794fn walk_class_const_decl<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
796 visitor: &mut V,
797 cc: &ClassConstDecl<'arena, 'src>,
798) -> ControlFlow<()> {
799 walk_attributes(visitor, &cc.attributes)?;
800 if let Some(type_hint) = &cc.type_hint {
801 visitor.visit_type_hint(type_hint)?;
802 }
803 visitor.visit_expr(&cc.value)
804}
805
806fn walk_property_decl<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
808 visitor: &mut V,
809 prop: &PropertyDecl<'arena, 'src>,
810) -> ControlFlow<()> {
811 walk_attributes(visitor, &prop.attributes)?;
812 if let Some(type_hint) = &prop.type_hint {
813 visitor.visit_type_hint(type_hint)?;
814 }
815 if let Some(default) = &prop.default {
816 visitor.visit_expr(default)?;
817 }
818 for hook in prop.hooks.iter() {
819 visitor.visit_property_hook(hook)?;
820 }
821 ControlFlow::Continue(())
822}
823
824fn walk_attributes<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
825 visitor: &mut V,
826 attributes: &[Attribute<'arena, 'src>],
827) -> ControlFlow<()> {
828 for attr in attributes.iter() {
829 visitor.visit_attribute(attr)?;
830 }
831 ControlFlow::Continue(())
832}
833
834#[derive(Debug, Clone, Copy, Default)]
850pub struct Scope<'src> {
851 pub namespace: Option<&'src str>,
856 pub class_name: Option<&'src str>,
858 pub function_name: Option<&'src str>,
860}
861
862pub trait ScopeVisitor<'arena, 'src> {
899 fn visit_program(
900 &mut self,
901 _program: &Program<'arena, 'src>,
902 _scope: &Scope<'src>,
903 ) -> ControlFlow<()> {
904 ControlFlow::Continue(())
905 }
906 fn visit_stmt(&mut self, _stmt: &Stmt<'arena, 'src>, _scope: &Scope<'src>) -> ControlFlow<()> {
907 ControlFlow::Continue(())
908 }
909 fn visit_expr(&mut self, _expr: &Expr<'arena, 'src>, _scope: &Scope<'src>) -> ControlFlow<()> {
910 ControlFlow::Continue(())
911 }
912 fn visit_param(
913 &mut self,
914 _param: &Param<'arena, 'src>,
915 _scope: &Scope<'src>,
916 ) -> ControlFlow<()> {
917 ControlFlow::Continue(())
918 }
919 fn visit_arg(&mut self, _arg: &Arg<'arena, 'src>, _scope: &Scope<'src>) -> ControlFlow<()> {
920 ControlFlow::Continue(())
921 }
922 fn visit_class_member(
923 &mut self,
924 _member: &ClassMember<'arena, 'src>,
925 _scope: &Scope<'src>,
926 ) -> ControlFlow<()> {
927 ControlFlow::Continue(())
928 }
929 fn visit_enum_member(
930 &mut self,
931 _member: &EnumMember<'arena, 'src>,
932 _scope: &Scope<'src>,
933 ) -> ControlFlow<()> {
934 ControlFlow::Continue(())
935 }
936 fn visit_property_hook(
937 &mut self,
938 _hook: &PropertyHook<'arena, 'src>,
939 _scope: &Scope<'src>,
940 ) -> ControlFlow<()> {
941 ControlFlow::Continue(())
942 }
943 fn visit_type_hint(
944 &mut self,
945 _type_hint: &TypeHint<'arena, 'src>,
946 _scope: &Scope<'src>,
947 ) -> ControlFlow<()> {
948 ControlFlow::Continue(())
949 }
950 fn visit_attribute(
951 &mut self,
952 _attribute: &Attribute<'arena, 'src>,
953 _scope: &Scope<'src>,
954 ) -> ControlFlow<()> {
955 ControlFlow::Continue(())
956 }
957 fn visit_catch_clause(
958 &mut self,
959 _catch: &CatchClause<'arena, 'src>,
960 _scope: &Scope<'src>,
961 ) -> ControlFlow<()> {
962 ControlFlow::Continue(())
963 }
964 fn visit_match_arm(
965 &mut self,
966 _arm: &MatchArm<'arena, 'src>,
967 _scope: &Scope<'src>,
968 ) -> ControlFlow<()> {
969 ControlFlow::Continue(())
970 }
971 fn visit_closure_use_var(
972 &mut self,
973 _var: &ClosureUseVar<'src>,
974 _scope: &Scope<'src>,
975 ) -> ControlFlow<()> {
976 ControlFlow::Continue(())
977 }
978
979 fn visit_trait_use(
980 &mut self,
981 _trait_use: &TraitUseDecl<'arena, 'src>,
982 _scope: &Scope<'src>,
983 ) -> ControlFlow<()> {
984 ControlFlow::Continue(())
985 }
986
987 fn visit_trait_adaptation(
988 &mut self,
989 _adaptation: &TraitAdaptation<'arena, 'src>,
990 _scope: &Scope<'src>,
991 ) -> ControlFlow<()> {
992 ControlFlow::Continue(())
993 }
994
995 fn visit_comment(&mut self, _comment: &Comment<'src>, _scope: &Scope<'src>) -> ControlFlow<()> {
996 ControlFlow::Continue(())
997 }
998}
999
1000pub struct ScopeWalker<'src, V> {
1023 inner: V,
1024 scope: Scope<'src>,
1025 src: &'src str,
1026}
1027
1028impl<'src, V> ScopeWalker<'src, V> {
1029 pub fn new(src: &'src str, inner: V) -> Self {
1036 Self {
1037 inner,
1038 scope: Scope::default(),
1039 src,
1040 }
1041 }
1042
1043 pub fn into_inner(self) -> V {
1045 self.inner
1046 }
1047
1048 pub fn inner(&self) -> &V {
1050 &self.inner
1051 }
1052
1053 pub fn inner_mut(&mut self) -> &mut V {
1055 &mut self.inner
1056 }
1057}
1058
1059impl<'arena, 'src, V: ScopeVisitor<'arena, 'src>> ScopeWalker<'src, V> {
1060 pub fn walk(&mut self, program: &Program<'arena, 'src>) -> ControlFlow<()> {
1062 self.visit_program(program)
1063 }
1064}
1065
1066impl<'arena, 'src, V: ScopeVisitor<'arena, 'src>> Visitor<'arena, 'src> for ScopeWalker<'src, V> {
1067 fn visit_program(&mut self, program: &Program<'arena, 'src>) -> ControlFlow<()> {
1068 self.inner.visit_program(program, &self.scope)?;
1069 walk_program(self, program)
1070 }
1071
1072 fn visit_stmt(&mut self, stmt: &Stmt<'arena, 'src>) -> ControlFlow<()> {
1073 self.inner.visit_stmt(stmt, &self.scope)?;
1074 match &stmt.kind {
1075 StmtKind::Function(func) => {
1076 let prev_fn = std::mem::replace(&mut self.scope.function_name, func.name.as_str());
1077 walk_stmt(self, stmt)?;
1078 self.scope.function_name = prev_fn;
1079 }
1080 StmtKind::Class(class) => {
1081 let prev_class = self.scope.class_name;
1082 let prev_fn = self.scope.function_name.take();
1083 self.scope.class_name = class.name.and_then(|n| n.as_str());
1084 walk_stmt(self, stmt)?;
1085 self.scope.class_name = prev_class;
1086 self.scope.function_name = prev_fn;
1087 }
1088 StmtKind::Interface(iface) => {
1089 let prev_class = std::mem::replace(&mut self.scope.class_name, iface.name.as_str());
1090 let prev_fn = self.scope.function_name.take();
1091 walk_stmt(self, stmt)?;
1092 self.scope.class_name = prev_class;
1093 self.scope.function_name = prev_fn;
1094 }
1095 StmtKind::Trait(trait_decl) => {
1096 let prev_class =
1097 std::mem::replace(&mut self.scope.class_name, trait_decl.name.as_str());
1098 let prev_fn = self.scope.function_name.take();
1099 walk_stmt(self, stmt)?;
1100 self.scope.class_name = prev_class;
1101 self.scope.function_name = prev_fn;
1102 }
1103 StmtKind::Enum(enum_decl) => {
1104 let prev_class =
1105 std::mem::replace(&mut self.scope.class_name, enum_decl.name.as_str());
1106 let prev_fn = self.scope.function_name.take();
1107 walk_stmt(self, stmt)?;
1108 self.scope.class_name = prev_class;
1109 self.scope.function_name = prev_fn;
1110 }
1111 StmtKind::Namespace(ns) => {
1112 let ns_str = ns.name.as_ref().map(|n| n.src_repr(self.src));
1113 match &ns.body {
1114 NamespaceBody::Braced(_) => {
1115 let prev_ns = self.scope.namespace;
1116 let prev_class = self.scope.class_name.take();
1117 let prev_fn = self.scope.function_name.take();
1118 self.scope.namespace = ns_str;
1119 walk_stmt(self, stmt)?;
1120 self.scope.namespace = prev_ns;
1121 self.scope.class_name = prev_class;
1122 self.scope.function_name = prev_fn;
1123 }
1124 NamespaceBody::Simple => {
1125 self.scope.namespace = ns_str;
1128 self.scope.class_name = None;
1129 self.scope.function_name = None;
1130 }
1131 }
1132 }
1133 _ => {
1134 walk_stmt(self, stmt)?;
1135 }
1136 }
1137 ControlFlow::Continue(())
1138 }
1139
1140 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1141 self.inner.visit_expr(expr, &self.scope)?;
1142 match &expr.kind {
1143 ExprKind::Closure(_) | ExprKind::ArrowFunction(_) => {
1144 let prev_fn = self.scope.function_name.take();
1145 walk_expr(self, expr)?;
1146 self.scope.function_name = prev_fn;
1147 }
1148 ExprKind::AnonymousClass(_) => {
1149 let prev_class = self.scope.class_name.take();
1150 let prev_fn = self.scope.function_name.take();
1151 walk_expr(self, expr)?;
1152 self.scope.class_name = prev_class;
1153 self.scope.function_name = prev_fn;
1154 }
1155 _ => {
1156 walk_expr(self, expr)?;
1157 }
1158 }
1159 ControlFlow::Continue(())
1160 }
1161
1162 fn visit_class_member(&mut self, member: &ClassMember<'arena, 'src>) -> ControlFlow<()> {
1163 self.inner.visit_class_member(member, &self.scope)?;
1164 if let ClassMemberKind::Method(method) = &member.kind {
1165 let prev_fn = std::mem::replace(&mut self.scope.function_name, method.name.as_str());
1166 walk_class_member(self, member)?;
1167 self.scope.function_name = prev_fn;
1168 } else {
1169 walk_class_member(self, member)?;
1170 }
1171 ControlFlow::Continue(())
1172 }
1173
1174 fn visit_enum_member(&mut self, member: &EnumMember<'arena, 'src>) -> ControlFlow<()> {
1175 self.inner.visit_enum_member(member, &self.scope)?;
1176 if let EnumMemberKind::Method(method) = &member.kind {
1177 let prev_fn = std::mem::replace(&mut self.scope.function_name, method.name.as_str());
1178 walk_enum_member(self, member)?;
1179 self.scope.function_name = prev_fn;
1180 } else {
1181 walk_enum_member(self, member)?;
1182 }
1183 ControlFlow::Continue(())
1184 }
1185
1186 fn visit_param(&mut self, param: &Param<'arena, 'src>) -> ControlFlow<()> {
1187 self.inner.visit_param(param, &self.scope)?;
1188 walk_param(self, param)
1189 }
1190
1191 fn visit_arg(&mut self, arg: &Arg<'arena, 'src>) -> ControlFlow<()> {
1192 self.inner.visit_arg(arg, &self.scope)?;
1193 walk_arg(self, arg)
1194 }
1195
1196 fn visit_property_hook(&mut self, hook: &PropertyHook<'arena, 'src>) -> ControlFlow<()> {
1197 self.inner.visit_property_hook(hook, &self.scope)?;
1198 walk_property_hook(self, hook)
1199 }
1200
1201 fn visit_type_hint(&mut self, type_hint: &TypeHint<'arena, 'src>) -> ControlFlow<()> {
1202 self.inner.visit_type_hint(type_hint, &self.scope)?;
1203 walk_type_hint(self, type_hint)
1204 }
1205
1206 fn visit_attribute(&mut self, attribute: &Attribute<'arena, 'src>) -> ControlFlow<()> {
1207 self.inner.visit_attribute(attribute, &self.scope)?;
1208 walk_attribute(self, attribute)
1209 }
1210
1211 fn visit_catch_clause(&mut self, catch: &CatchClause<'arena, 'src>) -> ControlFlow<()> {
1212 self.inner.visit_catch_clause(catch, &self.scope)?;
1213 walk_catch_clause(self, catch)
1214 }
1215
1216 fn visit_match_arm(&mut self, arm: &MatchArm<'arena, 'src>) -> ControlFlow<()> {
1217 self.inner.visit_match_arm(arm, &self.scope)?;
1218 walk_match_arm(self, arm)
1219 }
1220
1221 fn visit_closure_use_var(&mut self, var: &ClosureUseVar<'src>) -> ControlFlow<()> {
1222 self.inner.visit_closure_use_var(var, &self.scope)
1223 }
1224
1225 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl<'arena, 'src>) -> ControlFlow<()> {
1226 self.inner.visit_trait_use(trait_use, &self.scope)?;
1227 walk_trait_use(self, trait_use)
1228 }
1229
1230 fn visit_trait_adaptation(
1231 &mut self,
1232 adaptation: &TraitAdaptation<'arena, 'src>,
1233 ) -> ControlFlow<()> {
1234 self.inner.visit_trait_adaptation(adaptation, &self.scope)
1235 }
1236
1237 fn visit_comment(&mut self, comment: &Comment<'src>) -> ControlFlow<()> {
1238 self.inner.visit_comment(comment, &self.scope)
1239 }
1240}
1241
1242#[cfg(test)]
1243mod tests {
1244 use super::*;
1245 use crate::Span;
1246 struct VarCounter {
1251 count: usize,
1252 }
1253
1254 impl<'arena, 'src> Visitor<'arena, 'src> for VarCounter {
1255 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1256 if matches!(&expr.kind, ExprKind::Variable(_)) {
1257 self.count += 1;
1258 }
1259 walk_expr(self, expr)
1260 }
1261 }
1262
1263 #[test]
1264 fn counts_variables() {
1265 let arena = bumpalo::Bump::new();
1266 let var_x = arena.alloc(Expr {
1267 kind: ExprKind::Variable(NameStr::__src("x")),
1268 span: Span::DUMMY,
1269 });
1270 let var_y = arena.alloc(Expr {
1271 kind: ExprKind::Variable(NameStr::__src("y")),
1272 span: Span::DUMMY,
1273 });
1274 let var_z = arena.alloc(Expr {
1275 kind: ExprKind::Variable(NameStr::__src("z")),
1276 span: Span::DUMMY,
1277 });
1278 let binary = arena.alloc(Expr {
1279 kind: ExprKind::Binary(BinaryExpr {
1280 left: var_y,
1281 op: BinaryOp::Add,
1282 right: var_z,
1283 }),
1284 span: Span::DUMMY,
1285 });
1286 let assign = arena.alloc(Expr {
1287 kind: ExprKind::Assign(AssignExpr {
1288 target: var_x,
1289 op: AssignOp::Assign,
1290 value: binary,
1291 by_ref: false,
1292 }),
1293 span: Span::DUMMY,
1294 });
1295 let mut stmts = ArenaVec::new_in(&arena);
1296 stmts.push(Stmt {
1297 kind: StmtKind::Expression(assign),
1298 span: Span::DUMMY,
1299 });
1300 let program = Program {
1301 stmts,
1302 span: Span::DUMMY,
1303 };
1304
1305 let mut v = VarCounter { count: 0 };
1306 let _ = v.visit_program(&program);
1307 assert_eq!(v.count, 3);
1308 }
1309
1310 #[test]
1311 fn early_termination() {
1312 let arena = bumpalo::Bump::new();
1313 let var_a = arena.alloc(Expr {
1314 kind: ExprKind::Variable(NameStr::__src("a")),
1315 span: Span::DUMMY,
1316 });
1317 let var_b = arena.alloc(Expr {
1318 kind: ExprKind::Variable(NameStr::__src("b")),
1319 span: Span::DUMMY,
1320 });
1321 let binary = arena.alloc(Expr {
1322 kind: ExprKind::Binary(BinaryExpr {
1323 left: var_a,
1324 op: BinaryOp::Add,
1325 right: var_b,
1326 }),
1327 span: Span::DUMMY,
1328 });
1329 let mut stmts = ArenaVec::new_in(&arena);
1330 stmts.push(Stmt {
1331 kind: StmtKind::Expression(binary),
1332 span: Span::DUMMY,
1333 });
1334 let program = Program {
1335 stmts,
1336 span: Span::DUMMY,
1337 };
1338
1339 struct FindFirst {
1340 found: Option<String>,
1341 }
1342 impl<'arena, 'src> Visitor<'arena, 'src> for FindFirst {
1343 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1344 if let ExprKind::Variable(name) = &expr.kind {
1345 self.found = Some(name.to_string());
1346 return ControlFlow::Break(());
1347 }
1348 walk_expr(self, expr)
1349 }
1350 }
1351
1352 let mut finder = FindFirst { found: None };
1353 let result = finder.visit_program(&program);
1354 assert!(result.is_break());
1355 assert_eq!(finder.found.as_deref(), Some("a"));
1356 }
1357
1358 #[test]
1359 fn skip_subtree() {
1360 let arena = bumpalo::Bump::new();
1361 let one = arena.alloc(Expr {
1363 kind: ExprKind::Int(1),
1364 span: Span::DUMMY,
1365 });
1366 let two = arena.alloc(Expr {
1367 kind: ExprKind::Int(2),
1368 span: Span::DUMMY,
1369 });
1370 let top = arena.alloc(Expr {
1371 kind: ExprKind::Binary(BinaryExpr {
1372 left: one,
1373 op: BinaryOp::Add,
1374 right: two,
1375 }),
1376 span: Span::DUMMY,
1377 });
1378 let three = arena.alloc(Expr {
1379 kind: ExprKind::Int(3),
1380 span: Span::DUMMY,
1381 });
1382 let four = arena.alloc(Expr {
1383 kind: ExprKind::Int(4),
1384 span: Span::DUMMY,
1385 });
1386 let inner = arena.alloc(Expr {
1387 kind: ExprKind::Binary(BinaryExpr {
1388 left: three,
1389 op: BinaryOp::Add,
1390 right: four,
1391 }),
1392 span: Span::DUMMY,
1393 });
1394 let mut func_body = ArenaVec::new_in(&arena);
1395 func_body.push(Stmt {
1396 kind: StmtKind::Expression(inner),
1397 span: Span::DUMMY,
1398 });
1399 let func = arena.alloc(FunctionDecl {
1400 name: Ident::name("foo"),
1401 params: ArenaVec::new_in(&arena),
1402 body: func_body,
1403 return_type: None,
1404 by_ref: false,
1405 attributes: ArenaVec::new_in(&arena),
1406 doc_comment: None,
1407 });
1408 let mut stmts = ArenaVec::new_in(&arena);
1409 stmts.push(Stmt {
1410 kind: StmtKind::Expression(top),
1411 span: Span::DUMMY,
1412 });
1413 stmts.push(Stmt {
1414 kind: StmtKind::Function(func),
1415 span: Span::DUMMY,
1416 });
1417 let program = Program {
1418 stmts,
1419 span: Span::DUMMY,
1420 };
1421
1422 struct SkipFunctions {
1423 expr_count: usize,
1424 }
1425 impl<'arena, 'src> Visitor<'arena, 'src> for SkipFunctions {
1426 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1427 self.expr_count += 1;
1428 walk_expr(self, expr)
1429 }
1430 fn visit_stmt(&mut self, stmt: &Stmt<'arena, 'src>) -> ControlFlow<()> {
1431 if matches!(&stmt.kind, StmtKind::Function(_)) {
1432 return ControlFlow::Continue(());
1433 }
1434 walk_stmt(self, stmt)
1435 }
1436 }
1437
1438 let mut v = SkipFunctions { expr_count: 0 };
1439 let _ = v.visit_program(&program);
1440 assert_eq!(v.expr_count, 3);
1442 }
1443}