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