1use std::borrow::Cow;
2use std::ops::ControlFlow;
3
4use crate::ast::*;
5
6pub trait Visitor<'arena, 'src> {
37 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<()> {
42 walk_stmt(self, stmt)
43 }
44
45 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
46 walk_expr(self, expr)
47 }
48
49 fn visit_param(&mut self, param: &Param<'arena, 'src>) -> ControlFlow<()> {
50 walk_param(self, param)
51 }
52
53 fn visit_arg(&mut self, arg: &Arg<'arena, 'src>) -> ControlFlow<()> {
54 walk_arg(self, arg)
55 }
56
57 fn visit_class_member(&mut self, member: &ClassMember<'arena, 'src>) -> ControlFlow<()> {
58 walk_class_member(self, member)
59 }
60
61 fn visit_enum_member(&mut self, member: &EnumMember<'arena, 'src>) -> ControlFlow<()> {
62 walk_enum_member(self, member)
63 }
64
65 fn visit_property_hook(&mut self, hook: &PropertyHook<'arena, 'src>) -> ControlFlow<()> {
66 walk_property_hook(self, hook)
67 }
68
69 fn visit_type_hint(&mut self, type_hint: &TypeHint<'arena, 'src>) -> ControlFlow<()> {
70 walk_type_hint(self, type_hint)
71 }
72
73 fn visit_attribute(&mut self, attribute: &Attribute<'arena, 'src>) -> ControlFlow<()> {
74 walk_attribute(self, attribute)
75 }
76
77 fn visit_catch_clause(&mut self, catch: &CatchClause<'arena, 'src>) -> ControlFlow<()> {
78 walk_catch_clause(self, catch)
79 }
80
81 fn visit_match_arm(&mut self, arm: &MatchArm<'arena, 'src>) -> ControlFlow<()> {
82 walk_match_arm(self, arm)
83 }
84
85 fn visit_closure_use_var(&mut self, _var: &ClosureUseVar<'src>) -> ControlFlow<()> {
86 ControlFlow::Continue(())
87 }
88}
89
90pub fn walk_program<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
95 visitor: &mut V,
96 program: &Program<'arena, 'src>,
97) -> ControlFlow<()> {
98 for stmt in program.stmts.iter() {
99 visitor.visit_stmt(stmt)?;
100 }
101 ControlFlow::Continue(())
102}
103
104pub fn walk_stmt<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
105 visitor: &mut V,
106 stmt: &Stmt<'arena, 'src>,
107) -> ControlFlow<()> {
108 match &stmt.kind {
109 StmtKind::Expression(expr) => {
110 visitor.visit_expr(expr)?;
111 }
112 StmtKind::Echo(exprs) => {
113 for expr in exprs.iter() {
114 visitor.visit_expr(expr)?;
115 }
116 }
117 StmtKind::Return(expr) => {
118 if let Some(expr) = expr {
119 visitor.visit_expr(expr)?;
120 }
121 }
122 StmtKind::Block(stmts) => {
123 for stmt in stmts.iter() {
124 visitor.visit_stmt(stmt)?;
125 }
126 }
127 StmtKind::If(if_stmt) => {
128 visitor.visit_expr(&if_stmt.condition)?;
129 visitor.visit_stmt(if_stmt.then_branch)?;
130 for elseif in if_stmt.elseif_branches.iter() {
131 visitor.visit_expr(&elseif.condition)?;
132 visitor.visit_stmt(&elseif.body)?;
133 }
134 if let Some(else_branch) = &if_stmt.else_branch {
135 visitor.visit_stmt(else_branch)?;
136 }
137 }
138 StmtKind::While(while_stmt) => {
139 visitor.visit_expr(&while_stmt.condition)?;
140 visitor.visit_stmt(while_stmt.body)?;
141 }
142 StmtKind::For(for_stmt) => {
143 for expr in for_stmt.init.iter() {
144 visitor.visit_expr(expr)?;
145 }
146 for expr in for_stmt.condition.iter() {
147 visitor.visit_expr(expr)?;
148 }
149 for expr in for_stmt.update.iter() {
150 visitor.visit_expr(expr)?;
151 }
152 visitor.visit_stmt(for_stmt.body)?;
153 }
154 StmtKind::Foreach(foreach_stmt) => {
155 visitor.visit_expr(&foreach_stmt.expr)?;
156 if let Some(key) = &foreach_stmt.key {
157 visitor.visit_expr(key)?;
158 }
159 visitor.visit_expr(&foreach_stmt.value)?;
160 visitor.visit_stmt(foreach_stmt.body)?;
161 }
162 StmtKind::DoWhile(do_while) => {
163 visitor.visit_stmt(do_while.body)?;
164 visitor.visit_expr(&do_while.condition)?;
165 }
166 StmtKind::Function(func) => {
167 walk_function_like(visitor, &func.attributes, &func.params, &func.return_type)?;
168 for stmt in func.body.iter() {
169 visitor.visit_stmt(stmt)?;
170 }
171 }
172 StmtKind::Break(expr) | StmtKind::Continue(expr) => {
173 if let Some(expr) = expr {
174 visitor.visit_expr(expr)?;
175 }
176 }
177 StmtKind::Switch(switch_stmt) => {
178 visitor.visit_expr(&switch_stmt.expr)?;
179 for case in switch_stmt.cases.iter() {
180 if let Some(value) = &case.value {
181 visitor.visit_expr(value)?;
182 }
183 for stmt in case.body.iter() {
184 visitor.visit_stmt(stmt)?;
185 }
186 }
187 }
188 StmtKind::Throw(expr) => {
189 visitor.visit_expr(expr)?;
190 }
191 StmtKind::TryCatch(tc) => {
192 for stmt in tc.body.iter() {
193 visitor.visit_stmt(stmt)?;
194 }
195 for catch in tc.catches.iter() {
196 visitor.visit_catch_clause(catch)?;
197 }
198 if let Some(finally) = &tc.finally {
199 for stmt in finally.iter() {
200 visitor.visit_stmt(stmt)?;
201 }
202 }
203 }
204 StmtKind::Declare(decl) => {
205 for (_, expr) in decl.directives.iter() {
206 visitor.visit_expr(expr)?;
207 }
208 if let Some(body) = decl.body {
209 visitor.visit_stmt(body)?;
210 }
211 }
212 StmtKind::Unset(exprs) | StmtKind::Global(exprs) => {
213 for expr in exprs.iter() {
214 visitor.visit_expr(expr)?;
215 }
216 }
217 StmtKind::Class(class) => {
218 walk_attributes(visitor, &class.attributes)?;
219 for member in class.members.iter() {
220 visitor.visit_class_member(member)?;
221 }
222 }
223 StmtKind::Interface(iface) => {
224 walk_attributes(visitor, &iface.attributes)?;
225 for member in iface.members.iter() {
226 visitor.visit_class_member(member)?;
227 }
228 }
229 StmtKind::Trait(trait_decl) => {
230 walk_attributes(visitor, &trait_decl.attributes)?;
231 for member in trait_decl.members.iter() {
232 visitor.visit_class_member(member)?;
233 }
234 }
235 StmtKind::Enum(enum_decl) => {
236 walk_attributes(visitor, &enum_decl.attributes)?;
237 for member in enum_decl.members.iter() {
238 visitor.visit_enum_member(member)?;
239 }
240 }
241 StmtKind::Namespace(ns) => {
242 if let NamespaceBody::Braced(stmts) = &ns.body {
243 for stmt in stmts.iter() {
244 visitor.visit_stmt(stmt)?;
245 }
246 }
247 }
248 StmtKind::Const(items) => {
249 for item in items.iter() {
250 walk_attributes(visitor, &item.attributes)?;
251 visitor.visit_expr(&item.value)?;
252 }
253 }
254 StmtKind::StaticVar(vars) => {
255 for var in vars.iter() {
256 if let Some(default) = &var.default {
257 visitor.visit_expr(default)?;
258 }
259 }
260 }
261 StmtKind::Use(_)
262 | StmtKind::Goto(_)
263 | StmtKind::Label(_)
264 | StmtKind::Nop
265 | StmtKind::InlineHtml(_)
266 | StmtKind::HaltCompiler(_)
267 | StmtKind::Error => {}
268 }
269 ControlFlow::Continue(())
270}
271
272pub fn walk_expr<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
273 visitor: &mut V,
274 expr: &Expr<'arena, 'src>,
275) -> ControlFlow<()> {
276 match &expr.kind {
277 ExprKind::Assign(assign) => {
278 visitor.visit_expr(assign.target)?;
279 visitor.visit_expr(assign.value)?;
280 }
281 ExprKind::Binary(binary) => {
282 visitor.visit_expr(binary.left)?;
283 visitor.visit_expr(binary.right)?;
284 }
285 ExprKind::UnaryPrefix(unary) => {
286 visitor.visit_expr(unary.operand)?;
287 }
288 ExprKind::UnaryPostfix(unary) => {
289 visitor.visit_expr(unary.operand)?;
290 }
291 ExprKind::Ternary(ternary) => {
292 visitor.visit_expr(ternary.condition)?;
293 if let Some(then_expr) = &ternary.then_expr {
294 visitor.visit_expr(then_expr)?;
295 }
296 visitor.visit_expr(ternary.else_expr)?;
297 }
298 ExprKind::NullCoalesce(nc) => {
299 visitor.visit_expr(nc.left)?;
300 visitor.visit_expr(nc.right)?;
301 }
302 ExprKind::FunctionCall(call) => {
303 visitor.visit_expr(call.name)?;
304 for arg in call.args.iter() {
305 visitor.visit_arg(arg)?;
306 }
307 }
308 ExprKind::Array(elements) => {
309 for elem in elements.iter() {
310 if let Some(key) = &elem.key {
311 visitor.visit_expr(key)?;
312 }
313 visitor.visit_expr(&elem.value)?;
314 }
315 }
316 ExprKind::ArrayAccess(access) => {
317 visitor.visit_expr(access.array)?;
318 if let Some(index) = &access.index {
319 visitor.visit_expr(index)?;
320 }
321 }
322 ExprKind::Print(expr) => {
323 visitor.visit_expr(expr)?;
324 }
325 ExprKind::Parenthesized(expr) => {
326 visitor.visit_expr(expr)?;
327 }
328 ExprKind::Cast(_, expr) => {
329 visitor.visit_expr(expr)?;
330 }
331 ExprKind::ErrorSuppress(expr) => {
332 visitor.visit_expr(expr)?;
333 }
334 ExprKind::Isset(exprs) => {
335 for expr in exprs.iter() {
336 visitor.visit_expr(expr)?;
337 }
338 }
339 ExprKind::Empty(expr) => {
340 visitor.visit_expr(expr)?;
341 }
342 ExprKind::Include(_, expr) => {
343 visitor.visit_expr(expr)?;
344 }
345 ExprKind::Eval(expr) => {
346 visitor.visit_expr(expr)?;
347 }
348 ExprKind::Exit(expr) => {
349 if let Some(expr) = expr {
350 visitor.visit_expr(expr)?;
351 }
352 }
353 ExprKind::Clone(expr) => {
354 visitor.visit_expr(expr)?;
355 }
356 ExprKind::CloneWith(object, overrides) => {
357 visitor.visit_expr(object)?;
358 visitor.visit_expr(overrides)?;
359 }
360 ExprKind::New(new_expr) => {
361 visitor.visit_expr(new_expr.class)?;
362 for arg in new_expr.args.iter() {
363 visitor.visit_arg(arg)?;
364 }
365 }
366 ExprKind::PropertyAccess(access) | ExprKind::NullsafePropertyAccess(access) => {
367 visitor.visit_expr(access.object)?;
368 visitor.visit_expr(access.property)?;
369 }
370 ExprKind::MethodCall(call) | ExprKind::NullsafeMethodCall(call) => {
371 visitor.visit_expr(call.object)?;
372 visitor.visit_expr(call.method)?;
373 for arg in call.args.iter() {
374 visitor.visit_arg(arg)?;
375 }
376 }
377 ExprKind::StaticPropertyAccess(access) | ExprKind::ClassConstAccess(access) => {
378 visitor.visit_expr(access.class)?;
379 }
380 ExprKind::ClassConstAccessDynamic { class, member }
381 | ExprKind::StaticPropertyAccessDynamic { class, member } => {
382 visitor.visit_expr(class)?;
383 visitor.visit_expr(member)?;
384 }
385 ExprKind::StaticMethodCall(call) => {
386 visitor.visit_expr(call.class)?;
387 for arg in call.args.iter() {
388 visitor.visit_arg(arg)?;
389 }
390 }
391 ExprKind::Closure(closure) => {
392 walk_function_like(
393 visitor,
394 &closure.attributes,
395 &closure.params,
396 &closure.return_type,
397 )?;
398 for use_var in closure.use_vars.iter() {
399 visitor.visit_closure_use_var(use_var)?;
400 }
401 for stmt in closure.body.iter() {
402 visitor.visit_stmt(stmt)?;
403 }
404 }
405 ExprKind::ArrowFunction(arrow) => {
406 walk_function_like(
407 visitor,
408 &arrow.attributes,
409 &arrow.params,
410 &arrow.return_type,
411 )?;
412 visitor.visit_expr(arrow.body)?;
413 }
414 ExprKind::Match(match_expr) => {
415 visitor.visit_expr(match_expr.subject)?;
416 for arm in match_expr.arms.iter() {
417 visitor.visit_match_arm(arm)?;
418 }
419 }
420 ExprKind::ThrowExpr(expr) => {
421 visitor.visit_expr(expr)?;
422 }
423 ExprKind::Yield(yield_expr) => {
424 if let Some(key) = &yield_expr.key {
425 visitor.visit_expr(key)?;
426 }
427 if let Some(value) = &yield_expr.value {
428 visitor.visit_expr(value)?;
429 }
430 }
431 ExprKind::AnonymousClass(class) => {
432 walk_attributes(visitor, &class.attributes)?;
433 for member in class.members.iter() {
434 visitor.visit_class_member(member)?;
435 }
436 }
437 ExprKind::InterpolatedString(parts)
438 | ExprKind::Heredoc { parts, .. }
439 | ExprKind::ShellExec(parts) => {
440 for part in parts.iter() {
441 if let StringPart::Expr(e) = part {
442 visitor.visit_expr(e)?;
443 }
444 }
445 }
446 ExprKind::VariableVariable(inner) => {
447 visitor.visit_expr(inner)?;
448 }
449 ExprKind::CallableCreate(cc) => match &cc.kind {
450 CallableCreateKind::Function(name) => visitor.visit_expr(name)?,
451 CallableCreateKind::Method { object, method }
452 | CallableCreateKind::NullsafeMethod { object, method } => {
453 visitor.visit_expr(object)?;
454 visitor.visit_expr(method)?;
455 }
456 CallableCreateKind::StaticMethod { class, .. } => {
457 visitor.visit_expr(class)?;
458 }
459 },
460 ExprKind::Int(_)
461 | ExprKind::Float(_)
462 | ExprKind::String(_)
463 | ExprKind::Bool(_)
464 | ExprKind::Null
465 | ExprKind::Omit
466 | ExprKind::Variable(_)
467 | ExprKind::Identifier(_)
468 | ExprKind::MagicConst(_)
469 | ExprKind::Nowdoc { .. }
470 | ExprKind::Error => {}
471 }
472 ControlFlow::Continue(())
473}
474
475pub fn walk_param<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
476 visitor: &mut V,
477 param: &Param<'arena, 'src>,
478) -> ControlFlow<()> {
479 walk_attributes(visitor, ¶m.attributes)?;
480 if let Some(type_hint) = ¶m.type_hint {
481 visitor.visit_type_hint(type_hint)?;
482 }
483 if let Some(default) = ¶m.default {
484 visitor.visit_expr(default)?;
485 }
486 for hook in param.hooks.iter() {
487 visitor.visit_property_hook(hook)?;
488 }
489 ControlFlow::Continue(())
490}
491
492pub fn walk_arg<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
493 visitor: &mut V,
494 arg: &Arg<'arena, 'src>,
495) -> ControlFlow<()> {
496 visitor.visit_expr(&arg.value)
497}
498
499pub fn walk_class_member<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
500 visitor: &mut V,
501 member: &ClassMember<'arena, 'src>,
502) -> ControlFlow<()> {
503 match &member.kind {
504 ClassMemberKind::Property(prop) => {
505 walk_property_decl(visitor, prop)?;
506 }
507 ClassMemberKind::Method(method) => {
508 walk_method_decl(visitor, method)?;
509 }
510 ClassMemberKind::ClassConst(cc) => {
511 walk_class_const_decl(visitor, cc)?;
512 }
513 ClassMemberKind::TraitUse(_) => {}
514 }
515 ControlFlow::Continue(())
516}
517
518pub fn walk_property_hook<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
519 visitor: &mut V,
520 hook: &PropertyHook<'arena, 'src>,
521) -> ControlFlow<()> {
522 walk_attributes(visitor, &hook.attributes)?;
523 for param in hook.params.iter() {
524 visitor.visit_param(param)?;
525 }
526 match &hook.body {
527 PropertyHookBody::Block(stmts) => {
528 for stmt in stmts.iter() {
529 visitor.visit_stmt(stmt)?;
530 }
531 }
532 PropertyHookBody::Expression(expr) => {
533 visitor.visit_expr(expr)?;
534 }
535 PropertyHookBody::Abstract => {}
536 }
537 ControlFlow::Continue(())
538}
539
540pub fn walk_enum_member<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
541 visitor: &mut V,
542 member: &EnumMember<'arena, 'src>,
543) -> ControlFlow<()> {
544 match &member.kind {
545 EnumMemberKind::Case(case) => {
546 walk_attributes(visitor, &case.attributes)?;
547 if let Some(value) = &case.value {
548 visitor.visit_expr(value)?;
549 }
550 }
551 EnumMemberKind::Method(method) => {
552 walk_method_decl(visitor, method)?;
553 }
554 EnumMemberKind::ClassConst(cc) => {
555 walk_class_const_decl(visitor, cc)?;
556 }
557 EnumMemberKind::TraitUse(_) => {}
558 }
559 ControlFlow::Continue(())
560}
561
562pub fn walk_type_hint<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
563 visitor: &mut V,
564 type_hint: &TypeHint<'arena, 'src>,
565) -> ControlFlow<()> {
566 match &type_hint.kind {
567 TypeHintKind::Nullable(inner) => {
568 visitor.visit_type_hint(inner)?;
569 }
570 TypeHintKind::Union(types) | TypeHintKind::Intersection(types) => {
571 for ty in types.iter() {
572 visitor.visit_type_hint(ty)?;
573 }
574 }
575 TypeHintKind::Named(_) | TypeHintKind::Keyword(_, _) => {}
576 }
577 ControlFlow::Continue(())
578}
579
580pub fn walk_attribute<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
581 visitor: &mut V,
582 attribute: &Attribute<'arena, 'src>,
583) -> ControlFlow<()> {
584 for arg in attribute.args.iter() {
585 visitor.visit_arg(arg)?;
586 }
587 ControlFlow::Continue(())
588}
589
590pub fn walk_catch_clause<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
591 visitor: &mut V,
592 catch: &CatchClause<'arena, 'src>,
593) -> ControlFlow<()> {
594 for stmt in catch.body.iter() {
595 visitor.visit_stmt(stmt)?;
596 }
597 ControlFlow::Continue(())
598}
599
600pub fn walk_match_arm<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
601 visitor: &mut V,
602 arm: &MatchArm<'arena, 'src>,
603) -> ControlFlow<()> {
604 if let Some(conditions) = &arm.conditions {
605 for cond in conditions.iter() {
606 visitor.visit_expr(cond)?;
607 }
608 }
609 visitor.visit_expr(&arm.body)
610}
611
612fn walk_function_like<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
619 visitor: &mut V,
620 attributes: &[Attribute<'arena, 'src>],
621 params: &[Param<'arena, 'src>],
622 return_type: &Option<TypeHint<'arena, 'src>>,
623) -> ControlFlow<()> {
624 walk_attributes(visitor, attributes)?;
625 for param in params.iter() {
626 visitor.visit_param(param)?;
627 }
628 if let Some(ret) = return_type {
629 visitor.visit_type_hint(ret)?;
630 }
631 ControlFlow::Continue(())
632}
633
634fn walk_method_decl<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
636 visitor: &mut V,
637 method: &MethodDecl<'arena, 'src>,
638) -> ControlFlow<()> {
639 walk_function_like(
640 visitor,
641 &method.attributes,
642 &method.params,
643 &method.return_type,
644 )?;
645 if let Some(body) = &method.body {
646 for stmt in body.iter() {
647 visitor.visit_stmt(stmt)?;
648 }
649 }
650 ControlFlow::Continue(())
651}
652
653fn walk_class_const_decl<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
655 visitor: &mut V,
656 cc: &ClassConstDecl<'arena, 'src>,
657) -> ControlFlow<()> {
658 walk_attributes(visitor, &cc.attributes)?;
659 if let Some(type_hint) = &cc.type_hint {
660 visitor.visit_type_hint(type_hint)?;
661 }
662 visitor.visit_expr(&cc.value)
663}
664
665fn walk_property_decl<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
667 visitor: &mut V,
668 prop: &PropertyDecl<'arena, 'src>,
669) -> ControlFlow<()> {
670 walk_attributes(visitor, &prop.attributes)?;
671 if let Some(type_hint) = &prop.type_hint {
672 visitor.visit_type_hint(type_hint)?;
673 }
674 if let Some(default) = &prop.default {
675 visitor.visit_expr(default)?;
676 }
677 for hook in prop.hooks.iter() {
678 visitor.visit_property_hook(hook)?;
679 }
680 ControlFlow::Continue(())
681}
682
683fn walk_attributes<'arena, 'src, V: Visitor<'arena, 'src> + ?Sized>(
684 visitor: &mut V,
685 attributes: &[Attribute<'arena, 'src>],
686) -> ControlFlow<()> {
687 for attr in attributes.iter() {
688 visitor.visit_attribute(attr)?;
689 }
690 ControlFlow::Continue(())
691}
692
693#[derive(Debug, Clone, Default)]
709pub struct Scope<'src> {
710 pub namespace: Option<Cow<'src, str>>,
712 pub class_name: Option<&'src str>,
714 pub function_name: Option<&'src str>,
716}
717
718pub trait ScopeVisitor<'arena, 'src> {
755 fn visit_program(
756 &mut self,
757 _program: &Program<'arena, 'src>,
758 _scope: &Scope<'src>,
759 ) -> ControlFlow<()> {
760 ControlFlow::Continue(())
761 }
762 fn visit_stmt(&mut self, _stmt: &Stmt<'arena, 'src>, _scope: &Scope<'src>) -> ControlFlow<()> {
763 ControlFlow::Continue(())
764 }
765 fn visit_expr(&mut self, _expr: &Expr<'arena, 'src>, _scope: &Scope<'src>) -> ControlFlow<()> {
766 ControlFlow::Continue(())
767 }
768 fn visit_param(
769 &mut self,
770 _param: &Param<'arena, 'src>,
771 _scope: &Scope<'src>,
772 ) -> ControlFlow<()> {
773 ControlFlow::Continue(())
774 }
775 fn visit_arg(&mut self, _arg: &Arg<'arena, 'src>, _scope: &Scope<'src>) -> ControlFlow<()> {
776 ControlFlow::Continue(())
777 }
778 fn visit_class_member(
779 &mut self,
780 _member: &ClassMember<'arena, 'src>,
781 _scope: &Scope<'src>,
782 ) -> ControlFlow<()> {
783 ControlFlow::Continue(())
784 }
785 fn visit_enum_member(
786 &mut self,
787 _member: &EnumMember<'arena, 'src>,
788 _scope: &Scope<'src>,
789 ) -> ControlFlow<()> {
790 ControlFlow::Continue(())
791 }
792 fn visit_property_hook(
793 &mut self,
794 _hook: &PropertyHook<'arena, 'src>,
795 _scope: &Scope<'src>,
796 ) -> ControlFlow<()> {
797 ControlFlow::Continue(())
798 }
799 fn visit_type_hint(
800 &mut self,
801 _type_hint: &TypeHint<'arena, 'src>,
802 _scope: &Scope<'src>,
803 ) -> ControlFlow<()> {
804 ControlFlow::Continue(())
805 }
806 fn visit_attribute(
807 &mut self,
808 _attribute: &Attribute<'arena, 'src>,
809 _scope: &Scope<'src>,
810 ) -> ControlFlow<()> {
811 ControlFlow::Continue(())
812 }
813 fn visit_catch_clause(
814 &mut self,
815 _catch: &CatchClause<'arena, 'src>,
816 _scope: &Scope<'src>,
817 ) -> ControlFlow<()> {
818 ControlFlow::Continue(())
819 }
820 fn visit_match_arm(
821 &mut self,
822 _arm: &MatchArm<'arena, 'src>,
823 _scope: &Scope<'src>,
824 ) -> ControlFlow<()> {
825 ControlFlow::Continue(())
826 }
827 fn visit_closure_use_var(
828 &mut self,
829 _var: &ClosureUseVar<'src>,
830 _scope: &Scope<'src>,
831 ) -> ControlFlow<()> {
832 ControlFlow::Continue(())
833 }
834}
835
836pub struct ScopeWalker<'src, V> {
859 inner: V,
860 scope: Scope<'src>,
861}
862
863impl<'src, V> ScopeWalker<'src, V> {
864 pub fn new(inner: V) -> Self {
866 Self {
867 inner,
868 scope: Scope::default(),
869 }
870 }
871
872 pub fn into_inner(self) -> V {
874 self.inner
875 }
876
877 pub fn inner(&self) -> &V {
879 &self.inner
880 }
881
882 pub fn inner_mut(&mut self) -> &mut V {
884 &mut self.inner
885 }
886}
887
888impl<'arena, 'src, V: ScopeVisitor<'arena, 'src>> ScopeWalker<'src, V> {
889 pub fn walk(&mut self, program: &Program<'arena, 'src>) -> ControlFlow<()> {
891 self.visit_program(program)
892 }
893}
894
895impl<'arena, 'src, V: ScopeVisitor<'arena, 'src>> Visitor<'arena, 'src> for ScopeWalker<'src, V> {
896 fn visit_program(&mut self, program: &Program<'arena, 'src>) -> ControlFlow<()> {
897 self.inner.visit_program(program, &self.scope)?;
898 walk_program(self, program)
899 }
900
901 fn visit_stmt(&mut self, stmt: &Stmt<'arena, 'src>) -> ControlFlow<()> {
902 self.inner.visit_stmt(stmt, &self.scope)?;
903 match &stmt.kind {
904 StmtKind::Function(func) => {
905 let prev_fn = self.scope.function_name.replace(func.name);
906 walk_stmt(self, stmt)?;
907 self.scope.function_name = prev_fn;
908 }
909 StmtKind::Class(class) => {
910 let prev_class = self.scope.class_name;
911 let prev_fn = self.scope.function_name.take();
912 self.scope.class_name = class.name;
913 walk_stmt(self, stmt)?;
914 self.scope.class_name = prev_class;
915 self.scope.function_name = prev_fn;
916 }
917 StmtKind::Interface(iface) => {
918 let prev_class = self.scope.class_name.replace(iface.name);
919 let prev_fn = self.scope.function_name.take();
920 walk_stmt(self, stmt)?;
921 self.scope.class_name = prev_class;
922 self.scope.function_name = prev_fn;
923 }
924 StmtKind::Trait(trait_decl) => {
925 let prev_class = self.scope.class_name.replace(trait_decl.name);
926 let prev_fn = self.scope.function_name.take();
927 walk_stmt(self, stmt)?;
928 self.scope.class_name = prev_class;
929 self.scope.function_name = prev_fn;
930 }
931 StmtKind::Enum(enum_decl) => {
932 let prev_class = self.scope.class_name.replace(enum_decl.name);
933 let prev_fn = self.scope.function_name.take();
934 walk_stmt(self, stmt)?;
935 self.scope.class_name = prev_class;
936 self.scope.function_name = prev_fn;
937 }
938 StmtKind::Namespace(ns) => {
939 let ns_str = ns.name.as_ref().map(|n| n.to_string_repr());
940 match &ns.body {
941 NamespaceBody::Braced(_) => {
942 let prev_ns = self.scope.namespace.clone();
943 let prev_class = self.scope.class_name.take();
944 let prev_fn = self.scope.function_name.take();
945 self.scope.namespace = ns_str;
946 walk_stmt(self, stmt)?;
947 self.scope.namespace = prev_ns;
948 self.scope.class_name = prev_class;
949 self.scope.function_name = prev_fn;
950 }
951 NamespaceBody::Simple => {
952 self.scope.namespace = ns_str;
955 self.scope.class_name = None;
956 self.scope.function_name = None;
957 }
958 }
959 }
960 _ => {
961 walk_stmt(self, stmt)?;
962 }
963 }
964 ControlFlow::Continue(())
965 }
966
967 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
968 self.inner.visit_expr(expr, &self.scope)?;
969 match &expr.kind {
970 ExprKind::Closure(_) | ExprKind::ArrowFunction(_) => {
971 let prev_fn = self.scope.function_name.take();
972 walk_expr(self, expr)?;
973 self.scope.function_name = prev_fn;
974 }
975 ExprKind::AnonymousClass(_) => {
976 let prev_class = self.scope.class_name.take();
977 let prev_fn = self.scope.function_name.take();
978 walk_expr(self, expr)?;
979 self.scope.class_name = prev_class;
980 self.scope.function_name = prev_fn;
981 }
982 _ => {
983 walk_expr(self, expr)?;
984 }
985 }
986 ControlFlow::Continue(())
987 }
988
989 fn visit_class_member(&mut self, member: &ClassMember<'arena, 'src>) -> ControlFlow<()> {
990 self.inner.visit_class_member(member, &self.scope)?;
991 if let ClassMemberKind::Method(method) = &member.kind {
992 let prev_fn = self.scope.function_name.replace(method.name);
993 walk_class_member(self, member)?;
994 self.scope.function_name = prev_fn;
995 } else {
996 walk_class_member(self, member)?;
997 }
998 ControlFlow::Continue(())
999 }
1000
1001 fn visit_enum_member(&mut self, member: &EnumMember<'arena, 'src>) -> ControlFlow<()> {
1002 self.inner.visit_enum_member(member, &self.scope)?;
1003 if let EnumMemberKind::Method(method) = &member.kind {
1004 let prev_fn = self.scope.function_name.replace(method.name);
1005 walk_enum_member(self, member)?;
1006 self.scope.function_name = prev_fn;
1007 } else {
1008 walk_enum_member(self, member)?;
1009 }
1010 ControlFlow::Continue(())
1011 }
1012
1013 fn visit_param(&mut self, param: &Param<'arena, 'src>) -> ControlFlow<()> {
1014 self.inner.visit_param(param, &self.scope)?;
1015 walk_param(self, param)
1016 }
1017
1018 fn visit_arg(&mut self, arg: &Arg<'arena, 'src>) -> ControlFlow<()> {
1019 self.inner.visit_arg(arg, &self.scope)?;
1020 walk_arg(self, arg)
1021 }
1022
1023 fn visit_property_hook(&mut self, hook: &PropertyHook<'arena, 'src>) -> ControlFlow<()> {
1024 self.inner.visit_property_hook(hook, &self.scope)?;
1025 walk_property_hook(self, hook)
1026 }
1027
1028 fn visit_type_hint(&mut self, type_hint: &TypeHint<'arena, 'src>) -> ControlFlow<()> {
1029 self.inner.visit_type_hint(type_hint, &self.scope)?;
1030 walk_type_hint(self, type_hint)
1031 }
1032
1033 fn visit_attribute(&mut self, attribute: &Attribute<'arena, 'src>) -> ControlFlow<()> {
1034 self.inner.visit_attribute(attribute, &self.scope)?;
1035 walk_attribute(self, attribute)
1036 }
1037
1038 fn visit_catch_clause(&mut self, catch: &CatchClause<'arena, 'src>) -> ControlFlow<()> {
1039 self.inner.visit_catch_clause(catch, &self.scope)?;
1040 walk_catch_clause(self, catch)
1041 }
1042
1043 fn visit_match_arm(&mut self, arm: &MatchArm<'arena, 'src>) -> ControlFlow<()> {
1044 self.inner.visit_match_arm(arm, &self.scope)?;
1045 walk_match_arm(self, arm)
1046 }
1047
1048 fn visit_closure_use_var(&mut self, var: &ClosureUseVar<'src>) -> ControlFlow<()> {
1049 self.inner.visit_closure_use_var(var, &self.scope)
1050 }
1051}
1052
1053#[cfg(test)]
1054mod tests {
1055 use super::*;
1056 use crate::Span;
1057 struct VarCounter {
1062 count: usize,
1063 }
1064
1065 impl<'arena, 'src> Visitor<'arena, 'src> for VarCounter {
1066 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1067 if matches!(&expr.kind, ExprKind::Variable(_)) {
1068 self.count += 1;
1069 }
1070 walk_expr(self, expr)
1071 }
1072 }
1073
1074 #[test]
1075 fn counts_variables() {
1076 let arena = bumpalo::Bump::new();
1077 let var_x = arena.alloc(Expr {
1078 kind: ExprKind::Variable(NameStr::Src("x")),
1079 span: Span::DUMMY,
1080 });
1081 let var_y = arena.alloc(Expr {
1082 kind: ExprKind::Variable(NameStr::Src("y")),
1083 span: Span::DUMMY,
1084 });
1085 let var_z = arena.alloc(Expr {
1086 kind: ExprKind::Variable(NameStr::Src("z")),
1087 span: Span::DUMMY,
1088 });
1089 let binary = arena.alloc(Expr {
1090 kind: ExprKind::Binary(BinaryExpr {
1091 left: var_y,
1092 op: BinaryOp::Add,
1093 right: var_z,
1094 }),
1095 span: Span::DUMMY,
1096 });
1097 let assign = arena.alloc(Expr {
1098 kind: ExprKind::Assign(AssignExpr {
1099 target: var_x,
1100 op: AssignOp::Assign,
1101 value: binary,
1102 by_ref: false,
1103 }),
1104 span: Span::DUMMY,
1105 });
1106 let mut stmts = ArenaVec::new_in(&arena);
1107 stmts.push(Stmt {
1108 kind: StmtKind::Expression(assign),
1109 span: Span::DUMMY,
1110 });
1111 let program = Program {
1112 stmts,
1113 span: Span::DUMMY,
1114 };
1115
1116 let mut v = VarCounter { count: 0 };
1117 let _ = v.visit_program(&program);
1118 assert_eq!(v.count, 3);
1119 }
1120
1121 #[test]
1122 fn early_termination() {
1123 let arena = bumpalo::Bump::new();
1124 let var_a = arena.alloc(Expr {
1125 kind: ExprKind::Variable(NameStr::Src("a")),
1126 span: Span::DUMMY,
1127 });
1128 let var_b = arena.alloc(Expr {
1129 kind: ExprKind::Variable(NameStr::Src("b")),
1130 span: Span::DUMMY,
1131 });
1132 let binary = arena.alloc(Expr {
1133 kind: ExprKind::Binary(BinaryExpr {
1134 left: var_a,
1135 op: BinaryOp::Add,
1136 right: var_b,
1137 }),
1138 span: Span::DUMMY,
1139 });
1140 let mut stmts = ArenaVec::new_in(&arena);
1141 stmts.push(Stmt {
1142 kind: StmtKind::Expression(binary),
1143 span: Span::DUMMY,
1144 });
1145 let program = Program {
1146 stmts,
1147 span: Span::DUMMY,
1148 };
1149
1150 struct FindFirst {
1151 found: Option<String>,
1152 }
1153 impl<'arena, 'src> Visitor<'arena, 'src> for FindFirst {
1154 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1155 if let ExprKind::Variable(name) = &expr.kind {
1156 self.found = Some(name.to_string());
1157 return ControlFlow::Break(());
1158 }
1159 walk_expr(self, expr)
1160 }
1161 }
1162
1163 let mut finder = FindFirst { found: None };
1164 let result = finder.visit_program(&program);
1165 assert!(result.is_break());
1166 assert_eq!(finder.found.as_deref(), Some("a"));
1167 }
1168
1169 #[test]
1170 fn skip_subtree() {
1171 let arena = bumpalo::Bump::new();
1172 let one = arena.alloc(Expr {
1174 kind: ExprKind::Int(1),
1175 span: Span::DUMMY,
1176 });
1177 let two = arena.alloc(Expr {
1178 kind: ExprKind::Int(2),
1179 span: Span::DUMMY,
1180 });
1181 let top = arena.alloc(Expr {
1182 kind: ExprKind::Binary(BinaryExpr {
1183 left: one,
1184 op: BinaryOp::Add,
1185 right: two,
1186 }),
1187 span: Span::DUMMY,
1188 });
1189 let three = arena.alloc(Expr {
1190 kind: ExprKind::Int(3),
1191 span: Span::DUMMY,
1192 });
1193 let four = arena.alloc(Expr {
1194 kind: ExprKind::Int(4),
1195 span: Span::DUMMY,
1196 });
1197 let inner = arena.alloc(Expr {
1198 kind: ExprKind::Binary(BinaryExpr {
1199 left: three,
1200 op: BinaryOp::Add,
1201 right: four,
1202 }),
1203 span: Span::DUMMY,
1204 });
1205 let mut func_body = ArenaVec::new_in(&arena);
1206 func_body.push(Stmt {
1207 kind: StmtKind::Expression(inner),
1208 span: Span::DUMMY,
1209 });
1210 let func = arena.alloc(FunctionDecl {
1211 name: "foo",
1212 params: ArenaVec::new_in(&arena),
1213 body: func_body,
1214 return_type: None,
1215 by_ref: false,
1216 attributes: ArenaVec::new_in(&arena),
1217 doc_comment: None,
1218 });
1219 let mut stmts = ArenaVec::new_in(&arena);
1220 stmts.push(Stmt {
1221 kind: StmtKind::Expression(top),
1222 span: Span::DUMMY,
1223 });
1224 stmts.push(Stmt {
1225 kind: StmtKind::Function(func),
1226 span: Span::DUMMY,
1227 });
1228 let program = Program {
1229 stmts,
1230 span: Span::DUMMY,
1231 };
1232
1233 struct SkipFunctions {
1234 expr_count: usize,
1235 }
1236 impl<'arena, 'src> Visitor<'arena, 'src> for SkipFunctions {
1237 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1238 self.expr_count += 1;
1239 walk_expr(self, expr)
1240 }
1241 fn visit_stmt(&mut self, stmt: &Stmt<'arena, 'src>) -> ControlFlow<()> {
1242 if matches!(&stmt.kind, StmtKind::Function(_)) {
1243 return ControlFlow::Continue(());
1244 }
1245 walk_stmt(self, stmt)
1246 }
1247 }
1248
1249 let mut v = SkipFunctions { expr_count: 0 };
1250 let _ = v.visit_program(&program);
1251 assert_eq!(v.expr_count, 3);
1253 }
1254}