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