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