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