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_block(&mut self, block: &Block<'arena, 'src>) -> ControlFlow<()> {
45 walk_block(self, block)
46 }
47
48 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
49 walk_expr(self, expr)
50 }
51
52 fn visit_param(&mut self, param: &Param<'arena, 'src>) -> ControlFlow<()> {
53 walk_param(self, param)
54 }
55
56 fn visit_arg(&mut self, arg: &Arg<'arena, 'src>) -> ControlFlow<()> {
57 walk_arg(self, arg)
58 }
59
60 fn visit_class_member(&mut self, member: &ClassMember<'arena, 'src>) -> ControlFlow<()> {
61 walk_class_member(self, member)
62 }
63
64 fn visit_enum_member(&mut self, member: &EnumMember<'arena, 'src>) -> ControlFlow<()> {
65 walk_enum_member(self, member)
66 }
67
68 fn visit_property_hook(&mut self, hook: &PropertyHook<'arena, 'src>) -> ControlFlow<()> {
69 walk_property_hook(self, hook)
70 }
71
72 fn visit_type_hint(&mut self, type_hint: &TypeHint<'arena, 'src>) -> ControlFlow<()> {
73 walk_type_hint(self, type_hint)
74 }
75
76 fn visit_attribute(&mut self, attribute: &Attribute<'arena, 'src>) -> ControlFlow<()> {
77 walk_attribute(self, attribute)
78 }
79
80 fn visit_catch_clause(&mut self, catch: &CatchClause<'arena, 'src>) -> ControlFlow<()> {
81 walk_catch_clause(self, catch)
82 }
83
84 fn visit_match_arm(&mut self, arm: &MatchArm<'arena, 'src>) -> ControlFlow<()> {
85 walk_match_arm(self, arm)
86 }
87
88 fn visit_closure_use_var(&mut self, _var: &ClosureUseVar<'src>) -> ControlFlow<()> {
89 ControlFlow::Continue(())
90 }
91
92 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl<'arena, 'src>) -> ControlFlow<()> {
93 walk_trait_use(self, trait_use)
94 }
95
96 fn visit_trait_adaptation(
97 &mut self,
98 _adaptation: &TraitAdaptation<'arena, 'src>,
99 ) -> ControlFlow<()> {
100 ControlFlow::Continue(())
101 }
102
103 fn visit_name(&mut self, _name: &Name<'arena, 'src>) -> ControlFlow<()> {
104 ControlFlow::Continue(())
105 }
106
107 fn visit_comment(&mut self, _comment: &Comment<'src>) -> ControlFlow<()> {
112 ControlFlow::Continue(())
113 }
114}
115
116pub fn walk_name<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
125 visitor: &mut V,
126 name: &Name<'arena, 'src>,
127) -> ControlFlow<()> {
128 visitor.visit_name(name)
129}
130
131pub fn walk_comments<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
144 visitor: &mut V,
145 comments: &[Comment<'src>],
146) -> ControlFlow<()> {
147 for comment in comments {
148 visitor.visit_comment(comment)?;
149 }
150 ControlFlow::Continue(())
151}
152
153pub fn walk_program<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
169 visitor: &mut V,
170 program: &Program<'arena, 'src>,
171) -> ControlFlow<()> {
172 for stmt in program.stmts.iter() {
173 visitor.visit_stmt(stmt)?;
174 }
175 ControlFlow::Continue(())
176}
177
178pub fn walk_block<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
180 visitor: &mut V,
181 block: &Block<'arena, 'src>,
182) -> ControlFlow<()> {
183 for stmt in block.stmts.iter() {
184 visitor.visit_stmt(stmt)?;
185 }
186 ControlFlow::Continue(())
187}
188
189pub fn walk_stmt<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
194 visitor: &mut V,
195 stmt: &Stmt<'arena, 'src>,
196) -> ControlFlow<()> {
197 match &stmt.kind {
198 StmtKind::Expression(expr) => {
199 visitor.visit_expr(expr)?;
200 }
201 StmtKind::Echo(exprs) => {
202 for expr in exprs.iter() {
203 visitor.visit_expr(expr)?;
204 }
205 }
206 StmtKind::Return(expr) => {
207 if let Some(expr) = expr {
208 visitor.visit_expr(expr)?;
209 }
210 }
211 StmtKind::Block(block) => {
212 visitor.visit_block(block)?;
213 }
214 StmtKind::If(if_stmt) => {
215 visitor.visit_expr(&if_stmt.condition)?;
216 visitor.visit_stmt(if_stmt.then_branch)?;
217 for elseif in if_stmt.elseif_branches.iter() {
218 visitor.visit_expr(&elseif.condition)?;
219 visitor.visit_stmt(&elseif.body)?;
220 }
221 if let Some(else_branch) = &if_stmt.else_branch {
222 visitor.visit_stmt(else_branch)?;
223 }
224 }
225 StmtKind::While(while_stmt) => {
226 visitor.visit_expr(&while_stmt.condition)?;
227 visitor.visit_stmt(while_stmt.body)?;
228 }
229 StmtKind::For(for_stmt) => {
230 for expr in for_stmt.init.iter() {
231 visitor.visit_expr(expr)?;
232 }
233 for expr in for_stmt.condition.iter() {
234 visitor.visit_expr(expr)?;
235 }
236 for expr in for_stmt.update.iter() {
237 visitor.visit_expr(expr)?;
238 }
239 visitor.visit_stmt(for_stmt.body)?;
240 }
241 StmtKind::Foreach(foreach_stmt) => {
242 visitor.visit_expr(&foreach_stmt.expr)?;
243 if let Some(key) = &foreach_stmt.key {
244 visitor.visit_expr(key)?;
245 }
246 visitor.visit_expr(&foreach_stmt.value)?;
247 visitor.visit_stmt(foreach_stmt.body)?;
248 }
249 StmtKind::DoWhile(do_while) => {
250 visitor.visit_stmt(do_while.body)?;
251 visitor.visit_expr(&do_while.condition)?;
252 }
253 StmtKind::Function(func) => {
254 walk_function_like(visitor, &func.attributes, &func.params, &func.return_type)?;
255 visitor.visit_block(func.body)?;
256 }
257 StmtKind::Break(expr) | StmtKind::Continue(expr) => {
258 if let Some(expr) = expr {
259 visitor.visit_expr(expr)?;
260 }
261 }
262 StmtKind::Switch(switch_stmt) => {
263 visitor.visit_expr(&switch_stmt.expr)?;
264 for case in switch_stmt.body.cases.iter() {
265 if let Some(value) = &case.value {
266 visitor.visit_expr(value)?;
267 }
268 for stmt in case.body.iter() {
269 visitor.visit_stmt(stmt)?;
270 }
271 }
272 }
273 StmtKind::Throw(expr) => {
274 visitor.visit_expr(expr)?;
275 }
276 StmtKind::TryCatch(tc) => {
277 visitor.visit_block(tc.body)?;
278 for catch in tc.catches.iter() {
279 visitor.visit_catch_clause(catch)?;
280 }
281 if let Some(finally) = tc.finally {
282 visitor.visit_block(finally)?;
283 }
284 }
285 StmtKind::Declare(decl) => {
286 for (_, expr) in decl.directives.iter() {
287 visitor.visit_expr(expr)?;
288 }
289 if let Some(body) = decl.body {
290 visitor.visit_stmt(body)?;
291 }
292 }
293 StmtKind::Unset(exprs) | StmtKind::Global(exprs) => {
294 for expr in exprs.iter() {
295 visitor.visit_expr(expr)?;
296 }
297 }
298 StmtKind::Class(class) => {
299 walk_attributes(visitor, &class.attributes)?;
300 if let Some(extends) = &class.extends {
301 visitor.visit_name(extends)?;
302 }
303 for name in class.implements.iter() {
304 visitor.visit_name(name)?;
305 }
306 for member in class.body.members.iter() {
307 visitor.visit_class_member(member)?;
308 }
309 }
310 StmtKind::Interface(iface) => {
311 walk_attributes(visitor, &iface.attributes)?;
312 for name in iface.extends.iter() {
313 visitor.visit_name(name)?;
314 }
315 for member in iface.body.members.iter() {
316 visitor.visit_class_member(member)?;
317 }
318 }
319 StmtKind::Trait(trait_decl) => {
320 walk_attributes(visitor, &trait_decl.attributes)?;
321 for member in trait_decl.body.members.iter() {
322 visitor.visit_class_member(member)?;
323 }
324 }
325 StmtKind::Enum(enum_decl) => {
326 walk_attributes(visitor, &enum_decl.attributes)?;
327 if let Some(scalar_type) = &enum_decl.scalar_type {
328 visitor.visit_name(scalar_type)?;
329 }
330 for name in enum_decl.implements.iter() {
331 visitor.visit_name(name)?;
332 }
333 for member in enum_decl.body.members.iter() {
334 visitor.visit_enum_member(member)?;
335 }
336 }
337 StmtKind::Namespace(ns) => {
338 if let NamespaceBody::Braced(block) = &ns.body {
339 visitor.visit_block(block)?;
340 }
341 }
342 StmtKind::Const(items) => {
343 for item in items.iter() {
344 walk_attributes(visitor, &item.attributes)?;
345 visitor.visit_expr(&item.value)?;
346 }
347 }
348 StmtKind::StaticVar(vars) => {
349 for var in vars.iter() {
350 if let Some(default) = &var.default {
351 visitor.visit_expr(default)?;
352 }
353 }
354 }
355 StmtKind::Use(decl) => {
356 for item in decl.uses.iter() {
357 visitor.visit_name(&item.name)?;
358 }
359 }
360 StmtKind::Goto(_)
361 | StmtKind::Label(_)
362 | StmtKind::Nop
363 | StmtKind::InlineHtml(_)
364 | StmtKind::HaltCompiler(_)
365 | StmtKind::Error => {}
366 }
367 ControlFlow::Continue(())
368}
369
370pub fn walk_expr<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
375 visitor: &mut V,
376 expr: &Expr<'arena, 'src>,
377) -> ControlFlow<()> {
378 match &expr.kind {
379 ExprKind::Assign(assign) => {
380 visitor.visit_expr(assign.target)?;
381 visitor.visit_expr(assign.value)?;
382 }
383 ExprKind::Binary(binary) => {
384 visitor.visit_expr(binary.left)?;
385 visitor.visit_expr(binary.right)?;
386 }
387 ExprKind::UnaryPrefix(unary) => {
388 visitor.visit_expr(unary.operand)?;
389 }
390 ExprKind::UnaryPostfix(unary) => {
391 visitor.visit_expr(unary.operand)?;
392 }
393 ExprKind::Ternary(ternary) => {
394 visitor.visit_expr(ternary.condition)?;
395 if let Some(then_expr) = &ternary.then_expr {
396 visitor.visit_expr(then_expr)?;
397 }
398 visitor.visit_expr(ternary.else_expr)?;
399 }
400 ExprKind::NullCoalesce(nc) => {
401 visitor.visit_expr(nc.left)?;
402 visitor.visit_expr(nc.right)?;
403 }
404 ExprKind::FunctionCall(call) => {
405 visitor.visit_expr(call.name)?;
406 for arg in call.args.iter() {
407 visitor.visit_arg(arg)?;
408 }
409 }
410 ExprKind::Array(elements) => {
411 for elem in elements.iter() {
412 if let Some(key) = &elem.key {
413 visitor.visit_expr(key)?;
414 }
415 visitor.visit_expr(&elem.value)?;
416 }
417 }
418 ExprKind::ArrayAccess(access) => {
419 visitor.visit_expr(access.array)?;
420 if let Some(index) = &access.index {
421 visitor.visit_expr(index)?;
422 }
423 }
424 ExprKind::Print(expr) => {
425 visitor.visit_expr(expr)?;
426 }
427 ExprKind::Parenthesized(expr) => {
428 visitor.visit_expr(expr)?;
429 }
430 ExprKind::Cast(_, expr) => {
431 visitor.visit_expr(expr)?;
432 }
433 ExprKind::ErrorSuppress(expr) => {
434 visitor.visit_expr(expr)?;
435 }
436 ExprKind::Isset(exprs) => {
437 for expr in exprs.iter() {
438 visitor.visit_expr(expr)?;
439 }
440 }
441 ExprKind::Empty(expr) => {
442 visitor.visit_expr(expr)?;
443 }
444 ExprKind::Include(_, expr) => {
445 visitor.visit_expr(expr)?;
446 }
447 ExprKind::Eval(expr) => {
448 visitor.visit_expr(expr)?;
449 }
450 ExprKind::Exit(expr) => {
451 if let Some(expr) = expr {
452 visitor.visit_expr(expr)?;
453 }
454 }
455 ExprKind::Clone(expr) => {
456 visitor.visit_expr(expr)?;
457 }
458 ExprKind::CloneWith(object, overrides) => {
459 visitor.visit_expr(object)?;
460 visitor.visit_expr(overrides)?;
461 }
462 ExprKind::New(new_expr) => {
463 visitor.visit_expr(new_expr.class)?;
464 for arg in new_expr.args.iter() {
465 visitor.visit_arg(arg)?;
466 }
467 }
468 ExprKind::PropertyAccess(access) | ExprKind::NullsafePropertyAccess(access) => {
469 visitor.visit_expr(access.object)?;
470 visitor.visit_expr(access.property)?;
471 }
472 ExprKind::MethodCall(call) | ExprKind::NullsafeMethodCall(call) => {
473 visitor.visit_expr(call.object)?;
474 visitor.visit_expr(call.method)?;
475 for arg in call.args.iter() {
476 visitor.visit_arg(arg)?;
477 }
478 }
479 ExprKind::StaticPropertyAccess(access) | ExprKind::ClassConstAccess(access) => {
480 visitor.visit_expr(access.class)?;
481 visitor.visit_expr(access.member)?;
482 }
483 ExprKind::ClassConstAccessDynamic { class, member }
484 | ExprKind::StaticPropertyAccessDynamic { class, member } => {
485 visitor.visit_expr(class)?;
486 visitor.visit_expr(member)?;
487 }
488 ExprKind::StaticMethodCall(call) => {
489 visitor.visit_expr(call.class)?;
490 visitor.visit_expr(call.method)?;
491 for arg in call.args.iter() {
492 visitor.visit_arg(arg)?;
493 }
494 }
495 ExprKind::StaticDynMethodCall(call) => {
496 visitor.visit_expr(call.class)?;
497 visitor.visit_expr(call.method)?;
498 for arg in call.args.iter() {
499 visitor.visit_arg(arg)?;
500 }
501 }
502 ExprKind::Closure(closure) => {
503 walk_function_like(
504 visitor,
505 &closure.attributes,
506 &closure.params,
507 &closure.return_type,
508 )?;
509 for use_var in closure.use_vars.iter() {
510 visitor.visit_closure_use_var(use_var)?;
511 }
512 visitor.visit_block(closure.body)?;
513 }
514 ExprKind::ArrowFunction(arrow) => {
515 walk_function_like(
516 visitor,
517 &arrow.attributes,
518 &arrow.params,
519 &arrow.return_type,
520 )?;
521 visitor.visit_expr(arrow.body)?;
522 }
523 ExprKind::Match(match_expr) => {
524 visitor.visit_expr(match_expr.subject)?;
525 for arm in match_expr.arms.iter() {
526 visitor.visit_match_arm(arm)?;
527 }
528 }
529 ExprKind::ThrowExpr(expr) => {
530 visitor.visit_expr(expr)?;
531 }
532 ExprKind::Yield(yield_expr) => {
533 if let Some(key) = &yield_expr.key {
534 visitor.visit_expr(key)?;
535 }
536 if let Some(value) = &yield_expr.value {
537 visitor.visit_expr(value)?;
538 }
539 }
540 ExprKind::AnonymousClass(class) => {
541 walk_attributes(visitor, &class.attributes)?;
542 for member in class.body.members.iter() {
543 visitor.visit_class_member(member)?;
544 }
545 }
546 ExprKind::InterpolatedString(parts)
547 | ExprKind::Heredoc { parts, .. }
548 | ExprKind::ShellExec(parts) => {
549 for part in parts.iter() {
550 if let StringPart::Expr(e) = part {
551 visitor.visit_expr(e)?;
552 }
553 }
554 }
555 ExprKind::VariableVariable(inner) => {
556 visitor.visit_expr(inner)?;
557 }
558 ExprKind::CallableCreate(cc) => match &cc.kind {
559 CallableCreateKind::Function(name) => visitor.visit_expr(name)?,
560 CallableCreateKind::Method { object, method }
561 | CallableCreateKind::NullsafeMethod { object, method } => {
562 visitor.visit_expr(object)?;
563 visitor.visit_expr(method)?;
564 }
565 CallableCreateKind::StaticMethod { class, method } => {
566 visitor.visit_expr(class)?;
567 visitor.visit_expr(method)?;
568 }
569 },
570 ExprKind::Int(_)
571 | ExprKind::Float(_)
572 | ExprKind::String(_)
573 | ExprKind::Bool(_)
574 | ExprKind::Null
575 | ExprKind::Omit
576 | ExprKind::Variable(_)
577 | ExprKind::Identifier(_)
578 | ExprKind::MagicConst(_)
579 | ExprKind::Nowdoc { .. }
580 | ExprKind::Error => {}
581 }
582 ControlFlow::Continue(())
583}
584
585pub fn walk_param<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
587 visitor: &mut V,
588 param: &Param<'arena, 'src>,
589) -> ControlFlow<()> {
590 walk_attributes(visitor, ¶m.attributes)?;
591 if let Some(type_hint) = ¶m.type_hint {
592 visitor.visit_type_hint(type_hint)?;
593 }
594 if let Some(default) = ¶m.default {
595 visitor.visit_expr(default)?;
596 }
597 for hook in param.hooks.iter() {
598 visitor.visit_property_hook(hook)?;
599 }
600 ControlFlow::Continue(())
601}
602
603pub fn walk_arg<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
605 visitor: &mut V,
606 arg: &Arg<'arena, 'src>,
607) -> ControlFlow<()> {
608 match &arg.value {
609 Some(value) => visitor.visit_expr(value),
610 None => ControlFlow::Continue(()),
611 }
612}
613
614pub fn walk_class_member<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
616 visitor: &mut V,
617 member: &ClassMember<'arena, 'src>,
618) -> ControlFlow<()> {
619 match &member.kind {
620 ClassMemberKind::Property(prop) => {
621 walk_property_decl(visitor, prop)?;
622 }
623 ClassMemberKind::Method(method) => {
624 walk_method_decl(visitor, method)?;
625 }
626 ClassMemberKind::ClassConst(cc) => {
627 walk_class_const_decl(visitor, cc)?;
628 }
629 ClassMemberKind::TraitUse(trait_use) => {
630 visitor.visit_trait_use(trait_use)?;
631 }
632 }
633 ControlFlow::Continue(())
634}
635
636pub fn walk_property_hook<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
638 visitor: &mut V,
639 hook: &PropertyHook<'arena, 'src>,
640) -> ControlFlow<()> {
641 walk_attributes(visitor, &hook.attributes)?;
642 for param in hook.params.iter() {
643 visitor.visit_param(param)?;
644 }
645 match &hook.body {
646 PropertyHookBody::Block(block) => {
647 visitor.visit_block(block)?;
648 }
649 PropertyHookBody::Expression(expr) => {
650 visitor.visit_expr(expr)?;
651 }
652 PropertyHookBody::Abstract => {}
653 }
654 ControlFlow::Continue(())
655}
656
657pub fn walk_enum_member<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
659 visitor: &mut V,
660 member: &EnumMember<'arena, 'src>,
661) -> ControlFlow<()> {
662 match &member.kind {
663 EnumMemberKind::Case(case) => {
664 walk_attributes(visitor, &case.attributes)?;
665 if let Some(value) = &case.value {
666 visitor.visit_expr(value)?;
667 }
668 }
669 EnumMemberKind::Method(method) => {
670 walk_method_decl(visitor, method)?;
671 }
672 EnumMemberKind::ClassConst(cc) => {
673 walk_class_const_decl(visitor, cc)?;
674 }
675 EnumMemberKind::TraitUse(trait_use) => {
676 visitor.visit_trait_use(trait_use)?;
677 }
678 }
679 ControlFlow::Continue(())
680}
681
682pub fn walk_type_hint<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
684 visitor: &mut V,
685 type_hint: &TypeHint<'arena, 'src>,
686) -> ControlFlow<()> {
687 match &type_hint.kind {
688 TypeHintKind::Nullable(inner) => {
689 visitor.visit_type_hint(inner)?;
690 }
691 TypeHintKind::Union(types) | TypeHintKind::Intersection(types) => {
692 for ty in types.iter() {
693 visitor.visit_type_hint(ty)?;
694 }
695 }
696 TypeHintKind::Named(name) => {
697 visitor.visit_name(name)?;
698 }
699 TypeHintKind::Keyword(_, _) => {}
700 }
701 ControlFlow::Continue(())
702}
703
704pub fn walk_attribute<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
706 visitor: &mut V,
707 attribute: &Attribute<'arena, 'src>,
708) -> ControlFlow<()> {
709 visitor.visit_name(&attribute.name)?;
710 for arg in attribute.args.iter() {
711 visitor.visit_arg(arg)?;
712 }
713 ControlFlow::Continue(())
714}
715
716pub fn walk_catch_clause<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
718 visitor: &mut V,
719 catch: &CatchClause<'arena, 'src>,
720) -> ControlFlow<()> {
721 for ty in catch.types.iter() {
722 visitor.visit_name(ty)?;
723 }
724 visitor.visit_block(catch.body)?;
725 ControlFlow::Continue(())
726}
727
728pub fn walk_match_arm<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
730 visitor: &mut V,
731 arm: &MatchArm<'arena, 'src>,
732) -> ControlFlow<()> {
733 if let Some(conditions) = &arm.conditions {
734 for cond in conditions.iter() {
735 visitor.visit_expr(cond)?;
736 }
737 }
738 visitor.visit_expr(&arm.body)
739}
740
741pub fn walk_trait_use<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
743 visitor: &mut V,
744 trait_use: &TraitUseDecl<'arena, 'src>,
745) -> ControlFlow<()> {
746 for name in trait_use.traits.iter() {
747 visitor.visit_name(name)?;
748 }
749 for adaptation in trait_use.adaptations.iter() {
750 visitor.visit_trait_adaptation(adaptation)?;
751 }
752 ControlFlow::Continue(())
753}
754
755fn walk_function_like<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
762 visitor: &mut V,
763 attributes: &[Attribute<'arena, 'src>],
764 params: &[Param<'arena, 'src>],
765 return_type: &Option<TypeHint<'arena, 'src>>,
766) -> ControlFlow<()> {
767 walk_attributes(visitor, attributes)?;
768 for param in params.iter() {
769 visitor.visit_param(param)?;
770 }
771 if let Some(ret) = return_type {
772 visitor.visit_type_hint(ret)?;
773 }
774 ControlFlow::Continue(())
775}
776
777fn walk_method_decl<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
779 visitor: &mut V,
780 method: &MethodDecl<'arena, 'src>,
781) -> ControlFlow<()> {
782 walk_function_like(
783 visitor,
784 &method.attributes,
785 &method.params,
786 &method.return_type,
787 )?;
788 if let Some(body) = method.body {
789 visitor.visit_block(body)?;
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 doc_comment: None,
1300 });
1301 let program = Program {
1302 stmts,
1303 span: Span::DUMMY,
1304 };
1305
1306 let mut v = VarCounter { count: 0 };
1307 let _ = v.visit_program(&program);
1308 assert_eq!(v.count, 3);
1309 }
1310
1311 #[test]
1312 fn early_termination() {
1313 let arena = bumpalo::Bump::new();
1314 let var_a = arena.alloc(Expr {
1315 kind: ExprKind::Variable(NameStr::__src("a")),
1316 span: Span::DUMMY,
1317 });
1318 let var_b = arena.alloc(Expr {
1319 kind: ExprKind::Variable(NameStr::__src("b")),
1320 span: Span::DUMMY,
1321 });
1322 let binary = arena.alloc(Expr {
1323 kind: ExprKind::Binary(BinaryExpr {
1324 left: var_a,
1325 op: BinaryOp::Add,
1326 right: var_b,
1327 }),
1328 span: Span::DUMMY,
1329 });
1330 let mut stmts = ArenaVec::new_in(&arena);
1331 stmts.push(Stmt {
1332 kind: StmtKind::Expression(binary),
1333 span: Span::DUMMY,
1334 doc_comment: None,
1335 });
1336 let program = Program {
1337 stmts,
1338 span: Span::DUMMY,
1339 };
1340
1341 struct FindFirst {
1342 found: Option<String>,
1343 }
1344 impl<'arena, 'src> Visitor<'arena, 'src> for FindFirst {
1345 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1346 if let ExprKind::Variable(name) = &expr.kind {
1347 self.found = Some(name.to_string());
1348 return ControlFlow::Break(());
1349 }
1350 walk_expr(self, expr)
1351 }
1352 }
1353
1354 let mut finder = FindFirst { found: None };
1355 let result = finder.visit_program(&program);
1356 assert!(result.is_break());
1357 assert_eq!(finder.found.as_deref(), Some("a"));
1358 }
1359
1360 #[test]
1361 fn skip_subtree() {
1362 let arena = bumpalo::Bump::new();
1363 let one = arena.alloc(Expr {
1365 kind: ExprKind::Int(1),
1366 span: Span::DUMMY,
1367 });
1368 let two = arena.alloc(Expr {
1369 kind: ExprKind::Int(2),
1370 span: Span::DUMMY,
1371 });
1372 let top = arena.alloc(Expr {
1373 kind: ExprKind::Binary(BinaryExpr {
1374 left: one,
1375 op: BinaryOp::Add,
1376 right: two,
1377 }),
1378 span: Span::DUMMY,
1379 });
1380 let three = arena.alloc(Expr {
1381 kind: ExprKind::Int(3),
1382 span: Span::DUMMY,
1383 });
1384 let four = arena.alloc(Expr {
1385 kind: ExprKind::Int(4),
1386 span: Span::DUMMY,
1387 });
1388 let inner = arena.alloc(Expr {
1389 kind: ExprKind::Binary(BinaryExpr {
1390 left: three,
1391 op: BinaryOp::Add,
1392 right: four,
1393 }),
1394 span: Span::DUMMY,
1395 });
1396 let mut func_body_stmts = ArenaVec::new_in(&arena);
1397 func_body_stmts.push(Stmt {
1398 kind: StmtKind::Expression(inner),
1399 span: Span::DUMMY,
1400 doc_comment: None,
1401 });
1402 let func_body = arena.alloc(Block {
1403 stmts: func_body_stmts,
1404 span: Span::DUMMY,
1405 });
1406 let func = arena.alloc(FunctionDecl {
1407 name: Ident::name("foo"),
1408 params: ArenaVec::new_in(&arena),
1409 body: func_body,
1410 return_type: None,
1411 by_ref: false,
1412 attributes: ArenaVec::new_in(&arena),
1413 doc_comment: None,
1414 });
1415 let mut stmts = ArenaVec::new_in(&arena);
1416 stmts.push(Stmt {
1417 kind: StmtKind::Expression(top),
1418 span: Span::DUMMY,
1419 doc_comment: None,
1420 });
1421 stmts.push(Stmt {
1422 kind: StmtKind::Function(func),
1423 span: Span::DUMMY,
1424 doc_comment: None,
1425 });
1426 let program = Program {
1427 stmts,
1428 span: Span::DUMMY,
1429 };
1430
1431 struct SkipFunctions {
1432 expr_count: usize,
1433 }
1434 impl<'arena, 'src> Visitor<'arena, 'src> for SkipFunctions {
1435 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1436 self.expr_count += 1;
1437 walk_expr(self, expr)
1438 }
1439 fn visit_stmt(&mut self, stmt: &Stmt<'arena, 'src>) -> ControlFlow<()> {
1440 if matches!(&stmt.kind, StmtKind::Function(_)) {
1441 return ControlFlow::Continue(());
1442 }
1443 walk_stmt(self, stmt)
1444 }
1445 }
1446
1447 let mut v = SkipFunctions { expr_count: 0 };
1448 let _ = v.visit_program(&program);
1449 assert_eq!(v.expr_count, 3);
1451 }
1452}