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 = std::mem::replace(&mut self.scope.function_name, func.name.as_str());
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.and_then(|n| n.as_str());
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 = std::mem::replace(&mut self.scope.class_name, iface.name.as_str());
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 =
1063 std::mem::replace(&mut self.scope.class_name, trait_decl.name.as_str());
1064 let prev_fn = self.scope.function_name.take();
1065 walk_stmt(self, stmt)?;
1066 self.scope.class_name = prev_class;
1067 self.scope.function_name = prev_fn;
1068 }
1069 StmtKind::Enum(enum_decl) => {
1070 let prev_class =
1071 std::mem::replace(&mut self.scope.class_name, enum_decl.name.as_str());
1072 let prev_fn = self.scope.function_name.take();
1073 walk_stmt(self, stmt)?;
1074 self.scope.class_name = prev_class;
1075 self.scope.function_name = prev_fn;
1076 }
1077 StmtKind::Namespace(ns) => {
1078 let ns_str = ns.name.as_ref().map(|n| n.src_repr(self.src));
1079 match &ns.body {
1080 NamespaceBody::Braced(_) => {
1081 let prev_ns = self.scope.namespace;
1082 let prev_class = self.scope.class_name.take();
1083 let prev_fn = self.scope.function_name.take();
1084 self.scope.namespace = ns_str;
1085 walk_stmt(self, stmt)?;
1086 self.scope.namespace = prev_ns;
1087 self.scope.class_name = prev_class;
1088 self.scope.function_name = prev_fn;
1089 }
1090 NamespaceBody::Simple => {
1091 self.scope.namespace = ns_str;
1094 self.scope.class_name = None;
1095 self.scope.function_name = None;
1096 }
1097 }
1098 }
1099 _ => {
1100 walk_stmt(self, stmt)?;
1101 }
1102 }
1103 ControlFlow::Continue(())
1104 }
1105
1106 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1107 self.inner.visit_expr(expr, &self.scope)?;
1108 match &expr.kind {
1109 ExprKind::Closure(_) | ExprKind::ArrowFunction(_) => {
1110 let prev_fn = self.scope.function_name.take();
1111 walk_expr(self, expr)?;
1112 self.scope.function_name = prev_fn;
1113 }
1114 ExprKind::AnonymousClass(_) => {
1115 let prev_class = self.scope.class_name.take();
1116 let prev_fn = self.scope.function_name.take();
1117 walk_expr(self, expr)?;
1118 self.scope.class_name = prev_class;
1119 self.scope.function_name = prev_fn;
1120 }
1121 _ => {
1122 walk_expr(self, expr)?;
1123 }
1124 }
1125 ControlFlow::Continue(())
1126 }
1127
1128 fn visit_class_member(&mut self, member: &ClassMember<'arena, 'src>) -> ControlFlow<()> {
1129 self.inner.visit_class_member(member, &self.scope)?;
1130 if let ClassMemberKind::Method(method) = &member.kind {
1131 let prev_fn = std::mem::replace(&mut self.scope.function_name, method.name.as_str());
1132 walk_class_member(self, member)?;
1133 self.scope.function_name = prev_fn;
1134 } else {
1135 walk_class_member(self, member)?;
1136 }
1137 ControlFlow::Continue(())
1138 }
1139
1140 fn visit_enum_member(&mut self, member: &EnumMember<'arena, 'src>) -> ControlFlow<()> {
1141 self.inner.visit_enum_member(member, &self.scope)?;
1142 if let EnumMemberKind::Method(method) = &member.kind {
1143 let prev_fn = std::mem::replace(&mut self.scope.function_name, method.name.as_str());
1144 walk_enum_member(self, member)?;
1145 self.scope.function_name = prev_fn;
1146 } else {
1147 walk_enum_member(self, member)?;
1148 }
1149 ControlFlow::Continue(())
1150 }
1151
1152 fn visit_param(&mut self, param: &Param<'arena, 'src>) -> ControlFlow<()> {
1153 self.inner.visit_param(param, &self.scope)?;
1154 walk_param(self, param)
1155 }
1156
1157 fn visit_arg(&mut self, arg: &Arg<'arena, 'src>) -> ControlFlow<()> {
1158 self.inner.visit_arg(arg, &self.scope)?;
1159 walk_arg(self, arg)
1160 }
1161
1162 fn visit_property_hook(&mut self, hook: &PropertyHook<'arena, 'src>) -> ControlFlow<()> {
1163 self.inner.visit_property_hook(hook, &self.scope)?;
1164 walk_property_hook(self, hook)
1165 }
1166
1167 fn visit_type_hint(&mut self, type_hint: &TypeHint<'arena, 'src>) -> ControlFlow<()> {
1168 self.inner.visit_type_hint(type_hint, &self.scope)?;
1169 walk_type_hint(self, type_hint)
1170 }
1171
1172 fn visit_attribute(&mut self, attribute: &Attribute<'arena, 'src>) -> ControlFlow<()> {
1173 self.inner.visit_attribute(attribute, &self.scope)?;
1174 walk_attribute(self, attribute)
1175 }
1176
1177 fn visit_catch_clause(&mut self, catch: &CatchClause<'arena, 'src>) -> ControlFlow<()> {
1178 self.inner.visit_catch_clause(catch, &self.scope)?;
1179 walk_catch_clause(self, catch)
1180 }
1181
1182 fn visit_match_arm(&mut self, arm: &MatchArm<'arena, 'src>) -> ControlFlow<()> {
1183 self.inner.visit_match_arm(arm, &self.scope)?;
1184 walk_match_arm(self, arm)
1185 }
1186
1187 fn visit_closure_use_var(&mut self, var: &ClosureUseVar<'src>) -> ControlFlow<()> {
1188 self.inner.visit_closure_use_var(var, &self.scope)
1189 }
1190
1191 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl<'arena, 'src>) -> ControlFlow<()> {
1192 self.inner.visit_trait_use(trait_use, &self.scope)?;
1193 walk_trait_use(self, trait_use)
1194 }
1195
1196 fn visit_trait_adaptation(
1197 &mut self,
1198 adaptation: &TraitAdaptation<'arena, 'src>,
1199 ) -> ControlFlow<()> {
1200 self.inner.visit_trait_adaptation(adaptation, &self.scope)
1201 }
1202}
1203
1204#[cfg(test)]
1205mod tests {
1206 use super::*;
1207 use crate::Span;
1208 struct VarCounter {
1213 count: usize,
1214 }
1215
1216 impl<'arena, 'src> Visitor<'arena, 'src> for VarCounter {
1217 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1218 if matches!(&expr.kind, ExprKind::Variable(_)) {
1219 self.count += 1;
1220 }
1221 walk_expr(self, expr)
1222 }
1223 }
1224
1225 #[test]
1226 fn counts_variables() {
1227 let arena = bumpalo::Bump::new();
1228 let var_x = arena.alloc(Expr {
1229 kind: ExprKind::Variable(NameStr::Src("x")),
1230 span: Span::DUMMY,
1231 });
1232 let var_y = arena.alloc(Expr {
1233 kind: ExprKind::Variable(NameStr::Src("y")),
1234 span: Span::DUMMY,
1235 });
1236 let var_z = arena.alloc(Expr {
1237 kind: ExprKind::Variable(NameStr::Src("z")),
1238 span: Span::DUMMY,
1239 });
1240 let binary = arena.alloc(Expr {
1241 kind: ExprKind::Binary(BinaryExpr {
1242 left: var_y,
1243 op: BinaryOp::Add,
1244 right: var_z,
1245 }),
1246 span: Span::DUMMY,
1247 });
1248 let assign = arena.alloc(Expr {
1249 kind: ExprKind::Assign(AssignExpr {
1250 target: var_x,
1251 op: AssignOp::Assign,
1252 value: binary,
1253 by_ref: false,
1254 }),
1255 span: Span::DUMMY,
1256 });
1257 let mut stmts = ArenaVec::new_in(&arena);
1258 stmts.push(Stmt {
1259 kind: StmtKind::Expression(assign),
1260 span: Span::DUMMY,
1261 });
1262 let program = Program {
1263 stmts,
1264 span: Span::DUMMY,
1265 };
1266
1267 let mut v = VarCounter { count: 0 };
1268 let _ = v.visit_program(&program);
1269 assert_eq!(v.count, 3);
1270 }
1271
1272 #[test]
1273 fn early_termination() {
1274 let arena = bumpalo::Bump::new();
1275 let var_a = arena.alloc(Expr {
1276 kind: ExprKind::Variable(NameStr::Src("a")),
1277 span: Span::DUMMY,
1278 });
1279 let var_b = arena.alloc(Expr {
1280 kind: ExprKind::Variable(NameStr::Src("b")),
1281 span: Span::DUMMY,
1282 });
1283 let binary = arena.alloc(Expr {
1284 kind: ExprKind::Binary(BinaryExpr {
1285 left: var_a,
1286 op: BinaryOp::Add,
1287 right: var_b,
1288 }),
1289 span: Span::DUMMY,
1290 });
1291 let mut stmts = ArenaVec::new_in(&arena);
1292 stmts.push(Stmt {
1293 kind: StmtKind::Expression(binary),
1294 span: Span::DUMMY,
1295 });
1296 let program = Program {
1297 stmts,
1298 span: Span::DUMMY,
1299 };
1300
1301 struct FindFirst {
1302 found: Option<String>,
1303 }
1304 impl<'arena, 'src> Visitor<'arena, 'src> for FindFirst {
1305 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1306 if let ExprKind::Variable(name) = &expr.kind {
1307 self.found = Some(name.to_string());
1308 return ControlFlow::Break(());
1309 }
1310 walk_expr(self, expr)
1311 }
1312 }
1313
1314 let mut finder = FindFirst { found: None };
1315 let result = finder.visit_program(&program);
1316 assert!(result.is_break());
1317 assert_eq!(finder.found.as_deref(), Some("a"));
1318 }
1319
1320 #[test]
1321 fn skip_subtree() {
1322 let arena = bumpalo::Bump::new();
1323 let one = arena.alloc(Expr {
1325 kind: ExprKind::Int(1),
1326 span: Span::DUMMY,
1327 });
1328 let two = arena.alloc(Expr {
1329 kind: ExprKind::Int(2),
1330 span: Span::DUMMY,
1331 });
1332 let top = arena.alloc(Expr {
1333 kind: ExprKind::Binary(BinaryExpr {
1334 left: one,
1335 op: BinaryOp::Add,
1336 right: two,
1337 }),
1338 span: Span::DUMMY,
1339 });
1340 let three = arena.alloc(Expr {
1341 kind: ExprKind::Int(3),
1342 span: Span::DUMMY,
1343 });
1344 let four = arena.alloc(Expr {
1345 kind: ExprKind::Int(4),
1346 span: Span::DUMMY,
1347 });
1348 let inner = arena.alloc(Expr {
1349 kind: ExprKind::Binary(BinaryExpr {
1350 left: three,
1351 op: BinaryOp::Add,
1352 right: four,
1353 }),
1354 span: Span::DUMMY,
1355 });
1356 let mut func_body = ArenaVec::new_in(&arena);
1357 func_body.push(Stmt {
1358 kind: StmtKind::Expression(inner),
1359 span: Span::DUMMY,
1360 });
1361 let func = arena.alloc(FunctionDecl {
1362 name: Ident::name("foo"),
1363 params: ArenaVec::new_in(&arena),
1364 body: func_body,
1365 return_type: None,
1366 by_ref: false,
1367 attributes: ArenaVec::new_in(&arena),
1368 doc_comment: None,
1369 });
1370 let mut stmts = ArenaVec::new_in(&arena);
1371 stmts.push(Stmt {
1372 kind: StmtKind::Expression(top),
1373 span: Span::DUMMY,
1374 });
1375 stmts.push(Stmt {
1376 kind: StmtKind::Function(func),
1377 span: Span::DUMMY,
1378 });
1379 let program = Program {
1380 stmts,
1381 span: Span::DUMMY,
1382 };
1383
1384 struct SkipFunctions {
1385 expr_count: usize,
1386 }
1387 impl<'arena, 'src> Visitor<'arena, 'src> for SkipFunctions {
1388 fn visit_expr(&mut self, expr: &Expr<'arena, 'src>) -> ControlFlow<()> {
1389 self.expr_count += 1;
1390 walk_expr(self, expr)
1391 }
1392 fn visit_stmt(&mut self, stmt: &Stmt<'arena, 'src>) -> ControlFlow<()> {
1393 if matches!(&stmt.kind, StmtKind::Function(_)) {
1394 return ControlFlow::Continue(());
1395 }
1396 walk_stmt(self, stmt)
1397 }
1398 }
1399
1400 let mut v = SkipFunctions { expr_count: 0 };
1401 let _ = v.visit_program(&program);
1402 assert_eq!(v.expr_count, 3);
1404 }
1405}