1use std::ops::ControlFlow;
28
29use super::*;
30
31pub trait OwnedVisitor {
48 type Break;
51
52 fn visit_program(&mut self, program: &Program) -> ControlFlow<Self::Break> {
54 walk_owned_program(self, program)
55 }
56
57 fn visit_stmt(&mut self, stmt: &Stmt) -> ControlFlow<Self::Break> {
59 walk_owned_stmt(self, stmt)
60 }
61
62 fn visit_block(&mut self, block: &Block) -> ControlFlow<Self::Break> {
64 walk_owned_block(self, block)
65 }
66
67 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<Self::Break> {
69 walk_owned_expr(self, expr)
70 }
71
72 fn visit_param(&mut self, param: &Param) -> ControlFlow<Self::Break> {
74 walk_owned_param(self, param)
75 }
76
77 fn visit_arg(&mut self, arg: &Arg) -> ControlFlow<Self::Break> {
79 walk_owned_arg(self, arg)
80 }
81
82 fn visit_class_member(&mut self, member: &ClassMember) -> ControlFlow<Self::Break> {
84 walk_owned_class_member(self, member)
85 }
86
87 fn visit_enum_member(&mut self, member: &EnumMember) -> ControlFlow<Self::Break> {
89 walk_owned_enum_member(self, member)
90 }
91
92 fn visit_property_hook(&mut self, hook: &PropertyHook) -> ControlFlow<Self::Break> {
94 walk_owned_property_hook(self, hook)
95 }
96
97 fn visit_type_hint(&mut self, type_hint: &TypeHint) -> ControlFlow<Self::Break> {
99 walk_owned_type_hint(self, type_hint)
100 }
101
102 fn visit_attribute(&mut self, attribute: &Attribute) -> ControlFlow<Self::Break> {
104 walk_owned_attribute(self, attribute)
105 }
106
107 fn visit_catch_clause(&mut self, catch: &CatchClause) -> ControlFlow<Self::Break> {
109 walk_owned_catch_clause(self, catch)
110 }
111
112 fn visit_match_arm(&mut self, arm: &MatchArm) -> ControlFlow<Self::Break> {
114 walk_owned_match_arm(self, arm)
115 }
116
117 fn visit_closure_use_var(&mut self, _var: &ClosureUseVar) -> ControlFlow<Self::Break> {
119 ControlFlow::Continue(())
120 }
121
122 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl) -> ControlFlow<Self::Break> {
124 walk_owned_trait_use(self, trait_use)
125 }
126
127 fn visit_trait_adaptation(&mut self, adaptation: &TraitAdaptation) -> ControlFlow<Self::Break> {
129 walk_owned_trait_adaptation(self, adaptation)
130 }
131
132 fn visit_name(&mut self, _name: &Name) -> ControlFlow<Self::Break> {
134 ControlFlow::Continue(())
135 }
136
137 fn visit_comment(&mut self, _comment: &Comment) -> ControlFlow<Self::Break> {
139 ControlFlow::Continue(())
140 }
141}
142
143pub fn walk_owned_name<V: OwnedVisitor + ?Sized>(
154 visitor: &mut V,
155 name: &Name,
156) -> ControlFlow<V::Break> {
157 visitor.visit_name(name)
158}
159
160pub fn walk_owned_comments<V: OwnedVisitor + ?Sized>(
162 visitor: &mut V,
163 comments: &[Comment],
164) -> ControlFlow<V::Break> {
165 for comment in comments {
166 visitor.visit_comment(comment)?;
167 }
168 ControlFlow::Continue(())
169}
170
171pub fn walk_owned_program<V: OwnedVisitor + ?Sized>(
173 visitor: &mut V,
174 program: &Program,
175) -> ControlFlow<V::Break> {
176 for stmt in program.stmts.iter() {
177 visitor.visit_stmt(stmt)?;
178 }
179 ControlFlow::Continue(())
180}
181
182pub fn walk_owned_block<V: OwnedVisitor + ?Sized>(
184 visitor: &mut V,
185 block: &Block,
186) -> ControlFlow<V::Break> {
187 for stmt in block.stmts.iter() {
188 visitor.visit_stmt(stmt)?;
189 }
190 ControlFlow::Continue(())
191}
192
193pub fn walk_owned_stmt<V: OwnedVisitor + ?Sized>(
195 visitor: &mut V,
196 stmt: &Stmt,
197) -> ControlFlow<V::Break> {
198 match &stmt.kind {
199 StmtKind::Expression(expr) => {
200 visitor.visit_expr(expr)?;
201 }
202 StmtKind::Echo(exprs) => {
203 for expr in exprs.iter() {
204 visitor.visit_expr(expr)?;
205 }
206 }
207 StmtKind::Return(expr) => {
208 if let Some(expr) = expr {
209 visitor.visit_expr(expr)?;
210 }
211 }
212 StmtKind::Block(block) => {
213 visitor.visit_block(block)?;
214 }
215 StmtKind::If(if_stmt) => {
216 visitor.visit_expr(&if_stmt.condition)?;
217 visitor.visit_stmt(&if_stmt.then_branch)?;
218 for elseif in if_stmt.elseif_branches.iter() {
219 visitor.visit_expr(&elseif.condition)?;
220 visitor.visit_stmt(&elseif.body)?;
221 }
222 if let Some(else_branch) = &if_stmt.else_branch {
223 visitor.visit_stmt(else_branch)?;
224 }
225 }
226 StmtKind::While(while_stmt) => {
227 visitor.visit_expr(&while_stmt.condition)?;
228 visitor.visit_stmt(&while_stmt.body)?;
229 }
230 StmtKind::For(for_stmt) => {
231 for expr in for_stmt.init.iter() {
232 visitor.visit_expr(expr)?;
233 }
234 for expr in for_stmt.condition.iter() {
235 visitor.visit_expr(expr)?;
236 }
237 for expr in for_stmt.update.iter() {
238 visitor.visit_expr(expr)?;
239 }
240 visitor.visit_stmt(&for_stmt.body)?;
241 }
242 StmtKind::Foreach(foreach_stmt) => {
243 visitor.visit_expr(&foreach_stmt.expr)?;
244 if let Some(key) = &foreach_stmt.key {
245 visitor.visit_expr(key)?;
246 }
247 visitor.visit_expr(&foreach_stmt.value)?;
248 visitor.visit_stmt(&foreach_stmt.body)?;
249 }
250 StmtKind::DoWhile(do_while) => {
251 visitor.visit_stmt(&do_while.body)?;
252 visitor.visit_expr(&do_while.condition)?;
253 }
254 StmtKind::Function(func) => {
255 walk_owned_function_like(visitor, &func.attributes, &func.params, &func.return_type)?;
256 visitor.visit_block(&func.body)?;
257 }
258 StmtKind::Break(expr) | StmtKind::Continue(expr) => {
259 if let Some(expr) = expr {
260 visitor.visit_expr(expr)?;
261 }
262 }
263 StmtKind::Switch(switch_stmt) => {
264 visitor.visit_expr(&switch_stmt.expr)?;
265 for case in switch_stmt.body.cases.iter() {
266 if let Some(value) = &case.value {
267 visitor.visit_expr(value)?;
268 }
269 for stmt in case.body.iter() {
270 visitor.visit_stmt(stmt)?;
271 }
272 }
273 }
274 StmtKind::Throw(expr) => {
275 visitor.visit_expr(expr)?;
276 }
277 StmtKind::TryCatch(tc) => {
278 visitor.visit_block(&tc.body)?;
279 for catch in tc.catches.iter() {
280 visitor.visit_catch_clause(catch)?;
281 }
282 if let Some(finally) = &tc.finally {
283 visitor.visit_block(finally)?;
284 }
285 }
286 StmtKind::Declare(decl) => {
287 for (_, expr) in decl.directives.iter() {
288 visitor.visit_expr(expr)?;
289 }
290 if let Some(body) = &decl.body {
291 visitor.visit_stmt(body)?;
292 }
293 }
294 StmtKind::Unset(exprs) | StmtKind::Global(exprs) => {
295 for expr in exprs.iter() {
296 visitor.visit_expr(expr)?;
297 }
298 }
299 StmtKind::Class(class) => {
300 walk_owned_attributes(visitor, &class.attributes)?;
301 if let Some(extends) = &class.extends {
302 visitor.visit_name(extends)?;
303 }
304 for name in class.implements.iter() {
305 visitor.visit_name(name)?;
306 }
307 for member in class.body.members.iter() {
308 visitor.visit_class_member(member)?;
309 }
310 }
311 StmtKind::Interface(iface) => {
312 walk_owned_attributes(visitor, &iface.attributes)?;
313 for name in iface.extends.iter() {
314 visitor.visit_name(name)?;
315 }
316 for member in iface.body.members.iter() {
317 visitor.visit_class_member(member)?;
318 }
319 }
320 StmtKind::Trait(trait_decl) => {
321 walk_owned_attributes(visitor, &trait_decl.attributes)?;
322 for member in trait_decl.body.members.iter() {
323 visitor.visit_class_member(member)?;
324 }
325 }
326 StmtKind::Enum(enum_decl) => {
327 walk_owned_attributes(visitor, &enum_decl.attributes)?;
328 if let Some(scalar_type) = &enum_decl.scalar_type {
329 visitor.visit_name(scalar_type)?;
330 }
331 for name in enum_decl.implements.iter() {
332 visitor.visit_name(name)?;
333 }
334 for member in enum_decl.body.members.iter() {
335 visitor.visit_enum_member(member)?;
336 }
337 }
338 StmtKind::Namespace(ns) => {
339 if let Some(name) = &ns.name {
340 visitor.visit_name(name)?;
341 }
342 if let NamespaceBody::Braced(block) = &ns.body {
343 visitor.visit_block(block)?;
344 }
345 }
346 StmtKind::Const(items) => {
347 for item in items.iter() {
348 walk_owned_attributes(visitor, &item.attributes)?;
349 visitor.visit_expr(&item.value)?;
350 }
351 }
352 StmtKind::StaticVar(vars) => {
353 for var in vars.iter() {
354 if let Some(default) = &var.default {
355 visitor.visit_expr(default)?;
356 }
357 }
358 }
359 StmtKind::Use(decl) => {
360 for item in decl.uses.iter() {
361 visitor.visit_name(&item.name)?;
362 }
363 }
364 StmtKind::Goto(_)
365 | StmtKind::Label(_)
366 | StmtKind::Nop
367 | StmtKind::InlineHtml(_)
368 | StmtKind::HaltCompiler(_)
369 | StmtKind::Error => {}
370 }
371 ControlFlow::Continue(())
372}
373
374pub fn walk_owned_expr<V: OwnedVisitor + ?Sized>(
376 visitor: &mut V,
377 expr: &Expr,
378) -> ControlFlow<V::Break> {
379 match &expr.kind {
380 ExprKind::Name(name) => visitor.visit_name(name)?,
381 ExprKind::Assign(assign) => {
382 visitor.visit_expr(&assign.target)?;
383 visitor.visit_expr(&assign.value)?;
384 }
385 ExprKind::Binary(binary) => {
386 visitor.visit_expr(&binary.left)?;
387 visitor.visit_expr(&binary.right)?;
388 }
389 ExprKind::UnaryPrefix(unary) => {
390 visitor.visit_expr(&unary.operand)?;
391 }
392 ExprKind::UnaryPostfix(unary) => {
393 visitor.visit_expr(&unary.operand)?;
394 }
395 ExprKind::Ternary(ternary) => {
396 visitor.visit_expr(&ternary.condition)?;
397 if let Some(then_expr) = &ternary.then_expr {
398 visitor.visit_expr(then_expr)?;
399 }
400 visitor.visit_expr(&ternary.else_expr)?;
401 }
402 ExprKind::NullCoalesce(nc) => {
403 visitor.visit_expr(&nc.left)?;
404 visitor.visit_expr(&nc.right)?;
405 }
406 ExprKind::FunctionCall(call) => {
407 visitor.visit_expr(&call.name)?;
408 for arg in call.args.iter() {
409 visitor.visit_arg(arg)?;
410 }
411 }
412 ExprKind::Array(elements) => {
413 for elem in elements.iter() {
414 if let Some(key) = &elem.key {
415 visitor.visit_expr(key)?;
416 }
417 visitor.visit_expr(elem.value())?;
418 }
419 }
420 ExprKind::ArrayAccess(access) => {
421 visitor.visit_expr(&access.array)?;
422 if let Some(index) = &access.index {
423 visitor.visit_expr(index)?;
424 }
425 }
426 ExprKind::Print(expr) => {
427 visitor.visit_expr(expr)?;
428 }
429 ExprKind::Parenthesized(expr) => {
430 visitor.visit_expr(expr)?;
431 }
432 ExprKind::Cast(_, expr) => {
433 visitor.visit_expr(expr)?;
434 }
435 ExprKind::ErrorSuppress(expr) => {
436 visitor.visit_expr(expr)?;
437 }
438 ExprKind::Isset(exprs) => {
439 for expr in exprs.iter() {
440 visitor.visit_expr(expr)?;
441 }
442 }
443 ExprKind::Empty(expr) => {
444 visitor.visit_expr(expr)?;
445 }
446 ExprKind::Include(_, expr) => {
447 visitor.visit_expr(expr)?;
448 }
449 ExprKind::Eval(expr) => {
450 visitor.visit_expr(expr)?;
451 }
452 ExprKind::Exit(expr) => {
453 if let Some(expr) = expr {
454 visitor.visit_expr(expr)?;
455 }
456 }
457 ExprKind::Clone(expr) => {
458 visitor.visit_expr(expr)?;
459 }
460 ExprKind::CloneWith(object, overrides) => {
461 visitor.visit_expr(object)?;
462 visitor.visit_expr(overrides)?;
463 }
464 ExprKind::New(new_expr) => {
465 visitor.visit_expr(&new_expr.class)?;
466 for arg in new_expr.args.iter() {
467 visitor.visit_arg(arg)?;
468 }
469 }
470 ExprKind::PropertyAccess(access) | ExprKind::NullsafePropertyAccess(access) => {
471 visitor.visit_expr(&access.object)?;
472 visitor.visit_expr(&access.property)?;
473 }
474 ExprKind::MethodCall(call) | ExprKind::NullsafeMethodCall(call) => {
475 visitor.visit_expr(&call.object)?;
476 visitor.visit_expr(&call.method)?;
477 for arg in call.args.iter() {
478 visitor.visit_arg(arg)?;
479 }
480 }
481 ExprKind::StaticPropertyAccess(access) | ExprKind::ClassConstAccess(access) => {
482 visitor.visit_expr(&access.class)?;
483 visitor.visit_expr(&access.member)?;
484 }
485 ExprKind::ClassConstAccessDynamic { class, member }
486 | ExprKind::StaticPropertyAccessDynamic { class, member } => {
487 visitor.visit_expr(class)?;
488 visitor.visit_expr(member)?;
489 }
490 ExprKind::StaticMethodCall(call) => {
491 visitor.visit_expr(&call.class)?;
492 visitor.visit_expr(&call.method)?;
493 for arg in call.args.iter() {
494 visitor.visit_arg(arg)?;
495 }
496 }
497 ExprKind::StaticMethodCallDynamic(call) => {
498 visitor.visit_expr(&call.class)?;
499 visitor.visit_expr(&call.method)?;
500 for arg in call.args.iter() {
501 visitor.visit_arg(arg)?;
502 }
503 }
504 ExprKind::Closure(closure) => {
505 walk_owned_function_like(
506 visitor,
507 &closure.attributes,
508 &closure.params,
509 &closure.return_type,
510 )?;
511 for use_var in closure.use_vars.iter() {
512 visitor.visit_closure_use_var(use_var)?;
513 }
514 visitor.visit_block(&closure.body)?;
515 }
516 ExprKind::ArrowFunction(arrow) => {
517 walk_owned_function_like(
518 visitor,
519 &arrow.attributes,
520 &arrow.params,
521 &arrow.return_type,
522 )?;
523 visitor.visit_expr(&arrow.body)?;
524 }
525 ExprKind::Match(match_expr) => {
526 visitor.visit_expr(&match_expr.subject)?;
527 for arm in match_expr.arms.iter() {
528 visitor.visit_match_arm(arm)?;
529 }
530 }
531 ExprKind::Throw(expr) => {
532 visitor.visit_expr(expr)?;
533 }
534 ExprKind::Yield(yield_expr) => {
535 if let Some(key) = &yield_expr.key {
536 visitor.visit_expr(key)?;
537 }
538 if let Some(value) = &yield_expr.value {
539 visitor.visit_expr(value)?;
540 }
541 }
542 ExprKind::AnonymousClass(class) => {
543 walk_owned_attributes(visitor, &class.attributes)?;
544 for member in class.body.members.iter() {
545 visitor.visit_class_member(member)?;
546 }
547 }
548 ExprKind::InterpolatedString(parts) | ExprKind::ShellExec(parts) => {
549 for part in parts.iter() {
550 if let StringPart::Expr(e) = part {
551 visitor.visit_expr(e)?;
552 }
553 }
554 }
555 ExprKind::Heredoc { parts, .. } => {
556 for part in parts.iter() {
557 if let StringPart::Expr(e) = part {
558 visitor.visit_expr(e)?;
559 }
560 }
561 }
562 ExprKind::VariableVariable(inner) => {
563 visitor.visit_expr(inner)?;
564 }
565 ExprKind::CallableCreate(cc) => match &cc.kind {
566 CallableCreateKind::Function(name) => visitor.visit_expr(name)?,
567 CallableCreateKind::Method { object, method }
568 | CallableCreateKind::NullsafeMethod { object, method } => {
569 visitor.visit_expr(object)?;
570 visitor.visit_expr(method)?;
571 }
572 CallableCreateKind::StaticMethod { class, method } => {
573 visitor.visit_expr(class)?;
574 visitor.visit_expr(method)?;
575 }
576 },
577 ExprKind::Int(_)
578 | ExprKind::Float(_)
579 | ExprKind::String(_)
580 | ExprKind::Bool(_)
581 | ExprKind::Null
582 | ExprKind::Omit
583 | ExprKind::Variable(_)
584 | ExprKind::Identifier(_)
585 | ExprKind::MagicConst(_)
586 | ExprKind::Nowdoc { .. }
587 | ExprKind::Error => {}
588 }
589 ControlFlow::Continue(())
590}
591
592pub fn walk_owned_param<V: OwnedVisitor + ?Sized>(
594 visitor: &mut V,
595 param: &Param,
596) -> ControlFlow<V::Break> {
597 walk_owned_attributes(visitor, ¶m.attributes)?;
598 if let Some(type_hint) = ¶m.type_hint {
599 visitor.visit_type_hint(type_hint)?;
600 }
601 if let Some(default) = ¶m.default {
602 visitor.visit_expr(default)?;
603 }
604 for hook in param.hooks.iter() {
605 visitor.visit_property_hook(hook)?;
606 }
607 ControlFlow::Continue(())
608}
609
610pub fn walk_owned_arg<V: OwnedVisitor + ?Sized>(
612 visitor: &mut V,
613 arg: &Arg,
614) -> ControlFlow<V::Break> {
615 if let Some(name) = &arg.name {
616 visitor.visit_name(name)?;
617 }
618 match arg.value() {
619 Some(value) => visitor.visit_expr(value),
620 None => ControlFlow::Continue(()),
621 }
622}
623
624pub fn walk_owned_class_member<V: OwnedVisitor + ?Sized>(
626 visitor: &mut V,
627 member: &ClassMember,
628) -> ControlFlow<V::Break> {
629 match &member.kind {
630 ClassMemberKind::Property(prop) => {
631 walk_owned_property_decl(visitor, prop)?;
632 }
633 ClassMemberKind::Method(method) => {
634 walk_owned_method_decl(visitor, method)?;
635 }
636 ClassMemberKind::ClassConst(cc) => {
637 walk_owned_class_const_decl(visitor, cc)?;
638 }
639 ClassMemberKind::TraitUse(trait_use) => {
640 visitor.visit_trait_use(trait_use)?;
641 }
642 }
643 ControlFlow::Continue(())
644}
645
646pub fn walk_owned_property_hook<V: OwnedVisitor + ?Sized>(
648 visitor: &mut V,
649 hook: &PropertyHook,
650) -> ControlFlow<V::Break> {
651 walk_owned_attributes(visitor, &hook.attributes)?;
652 for param in hook.params.iter() {
653 visitor.visit_param(param)?;
654 }
655 match &hook.body {
656 PropertyHookBody::Block(block) => {
657 visitor.visit_block(block)?;
658 }
659 PropertyHookBody::Expression(expr) => {
660 visitor.visit_expr(expr)?;
661 }
662 PropertyHookBody::Abstract => {}
663 }
664 ControlFlow::Continue(())
665}
666
667pub fn walk_owned_enum_member<V: OwnedVisitor + ?Sized>(
669 visitor: &mut V,
670 member: &EnumMember,
671) -> ControlFlow<V::Break> {
672 match &member.kind {
673 EnumMemberKind::Case(case) => {
674 walk_owned_attributes(visitor, &case.attributes)?;
675 if let Some(value) = &case.value {
676 visitor.visit_expr(value)?;
677 }
678 }
679 EnumMemberKind::Method(method) => {
680 walk_owned_method_decl(visitor, method)?;
681 }
682 EnumMemberKind::ClassConst(cc) => {
683 walk_owned_class_const_decl(visitor, cc)?;
684 }
685 EnumMemberKind::TraitUse(trait_use) => {
686 visitor.visit_trait_use(trait_use)?;
687 }
688 EnumMemberKind::Property(prop) => {
689 walk_owned_property_decl(visitor, prop)?;
690 }
691 }
692 ControlFlow::Continue(())
693}
694
695pub fn walk_owned_type_hint<V: OwnedVisitor + ?Sized>(
697 visitor: &mut V,
698 type_hint: &TypeHint,
699) -> ControlFlow<V::Break> {
700 match &type_hint.kind {
701 TypeHintKind::Nullable(inner) => {
702 visitor.visit_type_hint(inner)?;
703 }
704 TypeHintKind::Union(types) | TypeHintKind::Intersection(types) => {
705 for ty in types.iter() {
706 visitor.visit_type_hint(ty)?;
707 }
708 }
709 TypeHintKind::Named(name) => {
710 visitor.visit_name(name)?;
711 }
712 TypeHintKind::Keyword(_, _) => {}
713 }
714 ControlFlow::Continue(())
715}
716
717pub fn walk_owned_attribute<V: OwnedVisitor + ?Sized>(
719 visitor: &mut V,
720 attribute: &Attribute,
721) -> ControlFlow<V::Break> {
722 visitor.visit_name(&attribute.name)?;
723 for arg in attribute.args.iter() {
724 visitor.visit_arg(arg)?;
725 }
726 ControlFlow::Continue(())
727}
728
729pub fn walk_owned_catch_clause<V: OwnedVisitor + ?Sized>(
731 visitor: &mut V,
732 catch: &CatchClause,
733) -> ControlFlow<V::Break> {
734 for ty in catch.types.iter() {
735 visitor.visit_name(ty)?;
736 }
737 visitor.visit_block(&catch.body)?;
738 ControlFlow::Continue(())
739}
740
741pub fn walk_owned_match_arm<V: OwnedVisitor + ?Sized>(
743 visitor: &mut V,
744 arm: &MatchArm,
745) -> ControlFlow<V::Break> {
746 if let Some(conditions) = &arm.conditions {
747 for cond in conditions.iter() {
748 visitor.visit_expr(cond)?;
749 }
750 }
751 visitor.visit_expr(&arm.body)
752}
753
754pub fn walk_owned_trait_use<V: OwnedVisitor + ?Sized>(
756 visitor: &mut V,
757 trait_use: &TraitUseDecl,
758) -> ControlFlow<V::Break> {
759 for name in trait_use.traits.iter() {
760 visitor.visit_name(name)?;
761 }
762 for adaptation in trait_use.adaptations.iter() {
763 visitor.visit_trait_adaptation(adaptation)?;
764 }
765 ControlFlow::Continue(())
766}
767
768pub fn walk_owned_trait_adaptation<V: OwnedVisitor + ?Sized>(
770 visitor: &mut V,
771 adaptation: &TraitAdaptation,
772) -> ControlFlow<V::Break> {
773 match &adaptation.kind {
774 TraitAdaptationKind::Precedence {
775 trait_name,
776 method,
777 insteadof,
778 } => {
779 visitor.visit_name(trait_name)?;
780 visitor.visit_name(method)?;
781 for name in insteadof.iter() {
782 visitor.visit_name(name)?;
783 }
784 }
785 TraitAdaptationKind::Alias {
786 trait_name,
787 method,
788 new_name,
789 ..
790 } => {
791 if let Some(name) = trait_name {
792 visitor.visit_name(name)?;
793 }
794 visitor.visit_name(method)?;
795 if let Some(name) = new_name {
796 visitor.visit_name(name)?;
797 }
798 }
799 }
800 ControlFlow::Continue(())
801}
802
803fn walk_owned_function_like<V: OwnedVisitor + ?Sized>(
808 visitor: &mut V,
809 attributes: &[Attribute],
810 params: &[Param],
811 return_type: &Option<TypeHint>,
812) -> ControlFlow<V::Break> {
813 walk_owned_attributes(visitor, attributes)?;
814 for param in params.iter() {
815 visitor.visit_param(param)?;
816 }
817 if let Some(ret) = return_type {
818 visitor.visit_type_hint(ret)?;
819 }
820 ControlFlow::Continue(())
821}
822
823fn walk_owned_method_decl<V: OwnedVisitor + ?Sized>(
824 visitor: &mut V,
825 method: &MethodDecl,
826) -> ControlFlow<V::Break> {
827 walk_owned_function_like(
828 visitor,
829 &method.attributes,
830 &method.params,
831 &method.return_type,
832 )?;
833 if let Some(body) = &method.body {
834 visitor.visit_block(body)?;
835 }
836 ControlFlow::Continue(())
837}
838
839fn walk_owned_class_const_decl<V: OwnedVisitor + ?Sized>(
840 visitor: &mut V,
841 cc: &ClassConstDecl,
842) -> ControlFlow<V::Break> {
843 walk_owned_attributes(visitor, &cc.attributes)?;
844 if let Some(type_hint) = &cc.type_hint {
845 visitor.visit_type_hint(type_hint)?;
846 }
847 visitor.visit_expr(&cc.value)
848}
849
850fn walk_owned_property_decl<V: OwnedVisitor + ?Sized>(
851 visitor: &mut V,
852 prop: &PropertyDecl,
853) -> ControlFlow<V::Break> {
854 walk_owned_attributes(visitor, &prop.attributes)?;
855 if let Some(type_hint) = &prop.type_hint {
856 visitor.visit_type_hint(type_hint)?;
857 }
858 if let Some(default) = &prop.default {
859 visitor.visit_expr(default)?;
860 }
861 for hook in prop.hooks.iter() {
862 visitor.visit_property_hook(hook)?;
863 }
864 ControlFlow::Continue(())
865}
866
867fn walk_owned_attributes<V: OwnedVisitor + ?Sized>(
868 visitor: &mut V,
869 attributes: &[Attribute],
870) -> ControlFlow<V::Break> {
871 for attr in attributes.iter() {
872 visitor.visit_attribute(attr)?;
873 }
874 ControlFlow::Continue(())
875}
876
877#[derive(Debug, Clone, Default)]
886#[non_exhaustive]
887pub struct OwnedScope {
888 pub namespace: Option<String>,
890 pub class_name: Option<String>,
892 pub function_name: Option<String>,
894}
895
896pub trait OwnedScopeVisitor {
901 type Break;
904
905 fn visit_program(
907 &mut self,
908 _program: &Program,
909 _scope: &OwnedScope,
910 ) -> ControlFlow<Self::Break> {
911 ControlFlow::Continue(())
912 }
913 fn visit_stmt(&mut self, _stmt: &Stmt, _scope: &OwnedScope) -> ControlFlow<Self::Break> {
915 ControlFlow::Continue(())
916 }
917 fn visit_expr(&mut self, _expr: &Expr, _scope: &OwnedScope) -> ControlFlow<Self::Break> {
919 ControlFlow::Continue(())
920 }
921 fn visit_param(&mut self, _param: &Param, _scope: &OwnedScope) -> ControlFlow<Self::Break> {
923 ControlFlow::Continue(())
924 }
925 fn visit_arg(&mut self, _arg: &Arg, _scope: &OwnedScope) -> ControlFlow<Self::Break> {
927 ControlFlow::Continue(())
928 }
929 fn visit_class_member(
931 &mut self,
932 _member: &ClassMember,
933 _scope: &OwnedScope,
934 ) -> ControlFlow<Self::Break> {
935 ControlFlow::Continue(())
936 }
937 fn visit_enum_member(
939 &mut self,
940 _member: &EnumMember,
941 _scope: &OwnedScope,
942 ) -> ControlFlow<Self::Break> {
943 ControlFlow::Continue(())
944 }
945 fn visit_property_hook(
947 &mut self,
948 _hook: &PropertyHook,
949 _scope: &OwnedScope,
950 ) -> ControlFlow<Self::Break> {
951 ControlFlow::Continue(())
952 }
953 fn visit_type_hint(
955 &mut self,
956 _type_hint: &TypeHint,
957 _scope: &OwnedScope,
958 ) -> ControlFlow<Self::Break> {
959 ControlFlow::Continue(())
960 }
961 fn visit_attribute(
963 &mut self,
964 _attribute: &Attribute,
965 _scope: &OwnedScope,
966 ) -> ControlFlow<Self::Break> {
967 ControlFlow::Continue(())
968 }
969 fn visit_catch_clause(
971 &mut self,
972 _catch: &CatchClause,
973 _scope: &OwnedScope,
974 ) -> ControlFlow<Self::Break> {
975 ControlFlow::Continue(())
976 }
977 fn visit_match_arm(
979 &mut self,
980 _arm: &MatchArm,
981 _scope: &OwnedScope,
982 ) -> ControlFlow<Self::Break> {
983 ControlFlow::Continue(())
984 }
985 fn visit_closure_use_var(
987 &mut self,
988 _var: &ClosureUseVar,
989 _scope: &OwnedScope,
990 ) -> ControlFlow<Self::Break> {
991 ControlFlow::Continue(())
992 }
993 fn visit_trait_use(
995 &mut self,
996 _trait_use: &TraitUseDecl,
997 _scope: &OwnedScope,
998 ) -> ControlFlow<Self::Break> {
999 ControlFlow::Continue(())
1000 }
1001 fn visit_trait_adaptation(
1003 &mut self,
1004 _adaptation: &TraitAdaptation,
1005 _scope: &OwnedScope,
1006 ) -> ControlFlow<Self::Break> {
1007 ControlFlow::Continue(())
1008 }
1009 fn visit_block(&mut self, _block: &Block, _scope: &OwnedScope) -> ControlFlow<Self::Break> {
1011 ControlFlow::Continue(())
1012 }
1013 fn visit_name(&mut self, _name: &Name, _scope: &OwnedScope) -> ControlFlow<Self::Break> {
1015 ControlFlow::Continue(())
1016 }
1017 fn visit_comment(
1019 &mut self,
1020 _comment: &Comment,
1021 _scope: &OwnedScope,
1022 ) -> ControlFlow<Self::Break> {
1023 ControlFlow::Continue(())
1024 }
1025}
1026
1027pub struct OwnedScopeWalker<V> {
1029 inner: V,
1030 scope: OwnedScope,
1031}
1032
1033impl<V> OwnedScopeWalker<V> {
1034 pub fn new(inner: V) -> Self {
1036 Self {
1037 inner,
1038 scope: OwnedScope::default(),
1039 }
1040 }
1041
1042 pub fn into_inner(self) -> V {
1044 self.inner
1045 }
1046
1047 pub fn inner(&self) -> &V {
1049 &self.inner
1050 }
1051
1052 pub fn inner_mut(&mut self) -> &mut V {
1054 &mut self.inner
1055 }
1056}
1057
1058impl<V: OwnedScopeVisitor> OwnedScopeWalker<V> {
1059 pub fn walk(&mut self, program: &Program) -> ControlFlow<V::Break> {
1061 self.visit_program(program)
1062 }
1063}
1064
1065fn owned_name_repr(name: &Name) -> Option<String> {
1066 if name.parts.is_empty() {
1067 return None;
1068 }
1069 Some(
1070 name.parts
1071 .iter()
1072 .map(|s| s.as_ref())
1073 .collect::<Vec<_>>()
1074 .join("\\"),
1075 )
1076}
1077
1078impl<V: OwnedScopeVisitor> OwnedVisitor for OwnedScopeWalker<V> {
1079 type Break = V::Break;
1080
1081 fn visit_program(&mut self, program: &Program) -> ControlFlow<V::Break> {
1082 self.scope = OwnedScope::default();
1084 self.inner.visit_program(program, &self.scope)?;
1085 walk_owned_program(self, program)
1086 }
1087
1088 fn visit_stmt(&mut self, stmt: &Stmt) -> ControlFlow<V::Break> {
1089 self.inner.visit_stmt(stmt, &self.scope)?;
1090 match &stmt.kind {
1091 StmtKind::Function(func) => {
1092 let fn_name = func.name.as_str().map(str::to_owned);
1093 let prev_fn = std::mem::replace(&mut self.scope.function_name, fn_name);
1094 walk_owned_stmt(self, stmt)?;
1095 self.scope.function_name = prev_fn;
1096 }
1097 StmtKind::Class(class) => {
1098 let class_name = class
1099 .name
1100 .as_ref()
1101 .and_then(|ident| ident.as_str())
1102 .map(|s| s.to_string());
1103 let prev_class = std::mem::replace(&mut self.scope.class_name, class_name);
1104 let prev_fn = self.scope.function_name.take();
1105 walk_owned_stmt(self, stmt)?;
1106 self.scope.class_name = prev_class;
1107 self.scope.function_name = prev_fn;
1108 }
1109 StmtKind::Interface(iface) => {
1110 let iface_name = iface.name.as_str().map(str::to_owned);
1111 let prev_class = std::mem::replace(&mut self.scope.class_name, iface_name);
1112 let prev_fn = self.scope.function_name.take();
1113 walk_owned_stmt(self, stmt)?;
1114 self.scope.class_name = prev_class;
1115 self.scope.function_name = prev_fn;
1116 }
1117 StmtKind::Trait(trait_decl) => {
1118 let trait_name = trait_decl.name.as_str().map(str::to_owned);
1119 let prev_class = std::mem::replace(&mut self.scope.class_name, trait_name);
1120 let prev_fn = self.scope.function_name.take();
1121 walk_owned_stmt(self, stmt)?;
1122 self.scope.class_name = prev_class;
1123 self.scope.function_name = prev_fn;
1124 }
1125 StmtKind::Enum(enum_decl) => {
1126 let enum_name = enum_decl.name.as_str().map(str::to_owned);
1127 let prev_class = std::mem::replace(&mut self.scope.class_name, enum_name);
1128 let prev_fn = self.scope.function_name.take();
1129 walk_owned_stmt(self, stmt)?;
1130 self.scope.class_name = prev_class;
1131 self.scope.function_name = prev_fn;
1132 }
1133 StmtKind::Namespace(ns) => {
1134 let ns_str = ns.name.as_ref().and_then(owned_name_repr);
1135 match &ns.body {
1136 NamespaceBody::Braced(_) => {
1137 let prev_ns = std::mem::replace(&mut self.scope.namespace, ns_str);
1138 let prev_class = self.scope.class_name.take();
1139 let prev_fn = self.scope.function_name.take();
1140 walk_owned_stmt(self, stmt)?;
1141 self.scope.namespace = prev_ns;
1142 self.scope.class_name = prev_class;
1143 self.scope.function_name = prev_fn;
1144 }
1145 NamespaceBody::Simple => {
1146 self.scope.namespace = ns_str;
1147 self.scope.class_name = None;
1148 self.scope.function_name = None;
1149 }
1150 }
1151 }
1152 _ => {
1153 walk_owned_stmt(self, stmt)?;
1154 }
1155 }
1156 ControlFlow::Continue(())
1157 }
1158
1159 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<V::Break> {
1160 self.inner.visit_expr(expr, &self.scope)?;
1161 match &expr.kind {
1162 ExprKind::Closure(_) | ExprKind::ArrowFunction(_) => {
1163 let prev_fn = self.scope.function_name.take();
1164 walk_owned_expr(self, expr)?;
1165 self.scope.function_name = prev_fn;
1166 }
1167 ExprKind::AnonymousClass(_) => {
1168 let prev_class = self.scope.class_name.take();
1169 let prev_fn = self.scope.function_name.take();
1170 walk_owned_expr(self, expr)?;
1171 self.scope.class_name = prev_class;
1172 self.scope.function_name = prev_fn;
1173 }
1174 _ => {
1175 walk_owned_expr(self, expr)?;
1176 }
1177 }
1178 ControlFlow::Continue(())
1179 }
1180
1181 fn visit_class_member(&mut self, member: &ClassMember) -> ControlFlow<V::Break> {
1182 self.inner.visit_class_member(member, &self.scope)?;
1183 if let ClassMemberKind::Method(method) = &member.kind {
1184 let fn_name = method.name.as_str().map(str::to_owned);
1185 let prev_fn = std::mem::replace(&mut self.scope.function_name, fn_name);
1186 walk_owned_class_member(self, member)?;
1187 self.scope.function_name = prev_fn;
1188 } else {
1189 walk_owned_class_member(self, member)?;
1190 }
1191 ControlFlow::Continue(())
1192 }
1193
1194 fn visit_enum_member(&mut self, member: &EnumMember) -> ControlFlow<V::Break> {
1195 self.inner.visit_enum_member(member, &self.scope)?;
1196 if let EnumMemberKind::Method(method) = &member.kind {
1197 let fn_name = method.name.as_str().map(str::to_owned);
1198 let prev_fn = std::mem::replace(&mut self.scope.function_name, fn_name);
1199 walk_owned_enum_member(self, member)?;
1200 self.scope.function_name = prev_fn;
1201 } else {
1202 walk_owned_enum_member(self, member)?;
1203 }
1204 ControlFlow::Continue(())
1205 }
1206
1207 fn visit_param(&mut self, param: &Param) -> ControlFlow<V::Break> {
1208 self.inner.visit_param(param, &self.scope)?;
1209 walk_owned_param(self, param)
1210 }
1211
1212 fn visit_arg(&mut self, arg: &Arg) -> ControlFlow<V::Break> {
1213 self.inner.visit_arg(arg, &self.scope)?;
1214 walk_owned_arg(self, arg)
1215 }
1216
1217 fn visit_property_hook(&mut self, hook: &PropertyHook) -> ControlFlow<V::Break> {
1218 self.inner.visit_property_hook(hook, &self.scope)?;
1219 walk_owned_property_hook(self, hook)
1220 }
1221
1222 fn visit_type_hint(&mut self, type_hint: &TypeHint) -> ControlFlow<V::Break> {
1223 self.inner.visit_type_hint(type_hint, &self.scope)?;
1224 walk_owned_type_hint(self, type_hint)
1225 }
1226
1227 fn visit_attribute(&mut self, attribute: &Attribute) -> ControlFlow<V::Break> {
1228 self.inner.visit_attribute(attribute, &self.scope)?;
1229 walk_owned_attribute(self, attribute)
1230 }
1231
1232 fn visit_catch_clause(&mut self, catch: &CatchClause) -> ControlFlow<V::Break> {
1233 self.inner.visit_catch_clause(catch, &self.scope)?;
1234 walk_owned_catch_clause(self, catch)
1235 }
1236
1237 fn visit_match_arm(&mut self, arm: &MatchArm) -> ControlFlow<V::Break> {
1238 self.inner.visit_match_arm(arm, &self.scope)?;
1239 walk_owned_match_arm(self, arm)
1240 }
1241
1242 fn visit_closure_use_var(&mut self, var: &ClosureUseVar) -> ControlFlow<V::Break> {
1243 self.inner.visit_closure_use_var(var, &self.scope)
1244 }
1245
1246 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl) -> ControlFlow<V::Break> {
1247 self.inner.visit_trait_use(trait_use, &self.scope)?;
1248 walk_owned_trait_use(self, trait_use)
1249 }
1250
1251 fn visit_trait_adaptation(&mut self, adaptation: &TraitAdaptation) -> ControlFlow<V::Break> {
1252 self.inner.visit_trait_adaptation(adaptation, &self.scope)?;
1253 walk_owned_trait_adaptation(self, adaptation)
1254 }
1255
1256 fn visit_block(&mut self, block: &Block) -> ControlFlow<V::Break> {
1257 self.inner.visit_block(block, &self.scope)?;
1258 walk_owned_block(self, block)
1259 }
1260
1261 fn visit_name(&mut self, name: &Name) -> ControlFlow<V::Break> {
1262 self.inner.visit_name(name, &self.scope)
1263 }
1264
1265 fn visit_comment(&mut self, comment: &Comment) -> ControlFlow<V::Break> {
1266 self.inner.visit_comment(comment, &self.scope)
1267 }
1268}
1269
1270#[cfg(test)]
1275mod tests {
1276 use super::*;
1277 use crate::ast::{AssignOp, BinaryOp};
1278 use crate::Span;
1279
1280 fn dummy_var(name: &str) -> Expr {
1281 Expr {
1282 kind: ExprKind::Variable(Box::from(name)),
1283 span: Span::DUMMY,
1284 }
1285 }
1286
1287 fn dummy_int(n: i64) -> Expr {
1288 Expr {
1289 kind: ExprKind::Int(n),
1290 span: Span::DUMMY,
1291 }
1292 }
1293
1294 fn binary(left: Expr, op: BinaryOp, right: Expr) -> Expr {
1295 Expr {
1296 kind: ExprKind::Binary(BinaryExpr {
1297 left: Box::new(left),
1298 op,
1299 right: Box::new(right),
1300 }),
1301 span: Span::DUMMY,
1302 }
1303 }
1304
1305 fn expr_stmt(e: Expr) -> Stmt {
1306 Stmt {
1307 kind: StmtKind::Expression(Box::new(e)),
1308 span: Span::DUMMY,
1309 doc_comment: None,
1310 }
1311 }
1312
1313 fn block_of(stmts: impl IntoIterator<Item = Stmt>) -> Box<Block> {
1314 Box::new(Block {
1315 stmts: stmts.into_iter().collect(),
1316 span: Span::DUMMY,
1317 })
1318 }
1319
1320 fn program(stmts: impl IntoIterator<Item = Stmt>) -> Program {
1321 Program {
1322 stmts: stmts.into_iter().collect(),
1323 span: Span::DUMMY,
1324 }
1325 }
1326
1327 struct VarCounter {
1328 count: usize,
1329 }
1330
1331 impl OwnedVisitor for VarCounter {
1332 type Break = ();
1333 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
1334 if matches!(&expr.kind, ExprKind::Variable(_)) {
1335 self.count += 1;
1336 }
1337 walk_owned_expr(self, expr)
1338 }
1339 }
1340
1341 #[test]
1342 fn counts_variables() {
1343 let p = program([expr_stmt(Expr {
1345 kind: ExprKind::Assign(AssignExpr {
1346 target: Box::new(dummy_var("x")),
1347 op: AssignOp::Assign,
1348 value: Box::new(binary(dummy_var("y"), BinaryOp::Add, dummy_var("z"))),
1349 by_ref: false,
1350 }),
1351 span: Span::DUMMY,
1352 })]);
1353 let mut v = VarCounter { count: 0 };
1354 let _ = v.visit_program(&p);
1355 assert_eq!(v.count, 3);
1356 }
1357
1358 #[test]
1359 fn early_termination() {
1360 let p = program([expr_stmt(binary(
1362 dummy_var("a"),
1363 BinaryOp::Add,
1364 dummy_var("b"),
1365 ))]);
1366
1367 struct FindFirst {
1368 found: Option<String>,
1369 }
1370 impl OwnedVisitor for FindFirst {
1371 type Break = ();
1372 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
1373 if let ExprKind::Variable(name) = &expr.kind {
1374 self.found = Some(name.to_string());
1375 return ControlFlow::Break(());
1376 }
1377 walk_owned_expr(self, expr)
1378 }
1379 }
1380
1381 let mut finder = FindFirst { found: None };
1382 let r = finder.visit_program(&p);
1383 assert!(r.is_break());
1384 assert_eq!(finder.found.as_deref(), Some("a"));
1385 }
1386
1387 #[test]
1388 fn skip_subtree() {
1389 let top = binary(dummy_int(1), BinaryOp::Add, dummy_int(2));
1391 let inner = binary(dummy_int(3), BinaryOp::Add, dummy_int(4));
1392 let func = Stmt {
1393 kind: StmtKind::Function(Box::new(FunctionDecl {
1394 name: crate::owned::Ident::new("foo", Span::DUMMY),
1395 params: Box::from([]),
1396 missing_param_list: false,
1397 body: block_of([expr_stmt(inner)]),
1398 return_type: None,
1399 by_ref: false,
1400 attributes: Box::from([]),
1401 doc_comment: None,
1402 })),
1403 span: Span::DUMMY,
1404 doc_comment: None,
1405 };
1406 let p = program([expr_stmt(top), func]);
1407
1408 struct SkipFunctions {
1409 expr_count: usize,
1410 }
1411 impl OwnedVisitor for SkipFunctions {
1412 type Break = ();
1413 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
1414 self.expr_count += 1;
1415 walk_owned_expr(self, expr)
1416 }
1417 fn visit_stmt(&mut self, stmt: &Stmt) -> ControlFlow<()> {
1418 if matches!(&stmt.kind, StmtKind::Function(_)) {
1419 return ControlFlow::Continue(());
1420 }
1421 walk_owned_stmt(self, stmt)
1422 }
1423 }
1424
1425 let mut v = SkipFunctions { expr_count: 0 };
1426 let _ = v.visit_program(&p);
1427 assert_eq!(v.expr_count, 3);
1429 }
1430
1431 #[test]
1432 fn scope_walker_tracks_class_and_method() {
1433 let method = ClassMember {
1435 kind: ClassMemberKind::Method(MethodDecl {
1436 name: crate::owned::Ident::new("bar", Span::DUMMY),
1437 modifiers: {
1438 let mut modifiers = crate::ast::Modifiers::new();
1439 modifiers.visibility = Some(crate::ast::Visibility::Public);
1440 modifiers
1441 },
1442 by_ref: false,
1443 params: Box::from([]),
1444 missing_param_list: false,
1445 return_type: None,
1446 body: Some(block_of([expr_stmt(dummy_var("x"))])),
1447 attributes: Box::from([]),
1448 doc_comment: None,
1449 }),
1450 span: Span::DUMMY,
1451 };
1452 let class_stmt = Stmt {
1453 kind: StmtKind::Class(Box::new(ClassDecl {
1454 name: Some(crate::owned::Ident::new("Foo", Span::DUMMY)),
1455 modifiers: crate::ast::ClassModifiers::default(),
1456 extends: None,
1457 implements: Box::from([]),
1458 body: ClassBody {
1459 members: [method].into_iter().collect(),
1460 span: Span::DUMMY,
1461 },
1462 attributes: Box::from([]),
1463 doc_comment: None,
1464 })),
1465 span: Span::DUMMY,
1466 doc_comment: None,
1467 };
1468 let p = program([class_stmt]);
1469
1470 struct MethodCollector {
1471 methods: Vec<String>,
1472 }
1473 impl OwnedScopeVisitor for MethodCollector {
1474 type Break = ();
1475 fn visit_class_member(
1476 &mut self,
1477 member: &ClassMember,
1478 scope: &OwnedScope,
1479 ) -> ControlFlow<()> {
1480 if let ClassMemberKind::Method(m) = &member.kind {
1481 self.methods.push(format!(
1482 "{}::{}",
1483 scope.class_name.as_deref().unwrap_or("<anon>"),
1484 m.name.or_error(),
1485 ));
1486 }
1487 ControlFlow::Continue(())
1488 }
1489 }
1490
1491 let mut walker = OwnedScopeWalker::new(MethodCollector { methods: vec![] });
1492 let _ = walker.walk(&p);
1493 assert_eq!(walker.into_inner().methods, ["Foo::bar"]);
1494 }
1495}