1use std::ops::ControlFlow;
27
28use super::*;
29
30pub trait OwnedVisitor {
46 fn visit_program(&mut self, program: &Program) -> ControlFlow<()> {
47 walk_owned_program(self, program)
48 }
49
50 fn visit_stmt(&mut self, stmt: &Stmt) -> ControlFlow<()> {
51 walk_owned_stmt(self, stmt)
52 }
53
54 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
55 walk_owned_expr(self, expr)
56 }
57
58 fn visit_param(&mut self, param: &Param) -> ControlFlow<()> {
59 walk_owned_param(self, param)
60 }
61
62 fn visit_arg(&mut self, arg: &Arg) -> ControlFlow<()> {
63 walk_owned_arg(self, arg)
64 }
65
66 fn visit_class_member(&mut self, member: &ClassMember) -> ControlFlow<()> {
67 walk_owned_class_member(self, member)
68 }
69
70 fn visit_enum_member(&mut self, member: &EnumMember) -> ControlFlow<()> {
71 walk_owned_enum_member(self, member)
72 }
73
74 fn visit_property_hook(&mut self, hook: &PropertyHook) -> ControlFlow<()> {
75 walk_owned_property_hook(self, hook)
76 }
77
78 fn visit_type_hint(&mut self, type_hint: &TypeHint) -> ControlFlow<()> {
79 walk_owned_type_hint(self, type_hint)
80 }
81
82 fn visit_attribute(&mut self, attribute: &Attribute) -> ControlFlow<()> {
83 walk_owned_attribute(self, attribute)
84 }
85
86 fn visit_catch_clause(&mut self, catch: &CatchClause) -> ControlFlow<()> {
87 walk_owned_catch_clause(self, catch)
88 }
89
90 fn visit_match_arm(&mut self, arm: &MatchArm) -> ControlFlow<()> {
91 walk_owned_match_arm(self, arm)
92 }
93
94 fn visit_closure_use_var(&mut self, _var: &ClosureUseVar) -> ControlFlow<()> {
95 ControlFlow::Continue(())
96 }
97
98 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl) -> ControlFlow<()> {
99 walk_owned_trait_use(self, trait_use)
100 }
101
102 fn visit_trait_adaptation(&mut self, _adaptation: &TraitAdaptation) -> ControlFlow<()> {
103 ControlFlow::Continue(())
104 }
105
106 fn visit_name(&mut self, _name: &Name) -> ControlFlow<()> {
107 ControlFlow::Continue(())
108 }
109
110 fn visit_comment(&mut self, _comment: &Comment) -> ControlFlow<()> {
111 ControlFlow::Continue(())
112 }
113}
114
115pub fn walk_owned_name<V: OwnedVisitor + ?Sized>(visitor: &mut V, name: &Name) -> ControlFlow<()> {
126 visitor.visit_name(name)
127}
128
129pub fn walk_owned_comments<V: OwnedVisitor + ?Sized>(
131 visitor: &mut V,
132 comments: &[Comment],
133) -> ControlFlow<()> {
134 for comment in comments {
135 visitor.visit_comment(comment)?;
136 }
137 ControlFlow::Continue(())
138}
139
140pub fn walk_owned_program<V: OwnedVisitor + ?Sized>(
142 visitor: &mut V,
143 program: &Program,
144) -> ControlFlow<()> {
145 for stmt in program.stmts.iter() {
146 visitor.visit_stmt(stmt)?;
147 }
148 ControlFlow::Continue(())
149}
150
151pub fn walk_owned_stmt<V: OwnedVisitor + ?Sized>(visitor: &mut V, stmt: &Stmt) -> ControlFlow<()> {
153 match &stmt.kind {
154 StmtKind::Expression(expr) => {
155 visitor.visit_expr(expr)?;
156 }
157 StmtKind::Echo(exprs) => {
158 for expr in exprs.iter() {
159 visitor.visit_expr(expr)?;
160 }
161 }
162 StmtKind::Return(expr) => {
163 if let Some(expr) = expr {
164 visitor.visit_expr(expr)?;
165 }
166 }
167 StmtKind::Block(stmts) => {
168 for stmt in stmts.iter() {
169 visitor.visit_stmt(stmt)?;
170 }
171 }
172 StmtKind::If(if_stmt) => {
173 visitor.visit_expr(&if_stmt.condition)?;
174 visitor.visit_stmt(&if_stmt.then_branch)?;
175 for elseif in if_stmt.elseif_branches.iter() {
176 visitor.visit_expr(&elseif.condition)?;
177 visitor.visit_stmt(&elseif.body)?;
178 }
179 if let Some(else_branch) = &if_stmt.else_branch {
180 visitor.visit_stmt(else_branch)?;
181 }
182 }
183 StmtKind::While(while_stmt) => {
184 visitor.visit_expr(&while_stmt.condition)?;
185 visitor.visit_stmt(&while_stmt.body)?;
186 }
187 StmtKind::For(for_stmt) => {
188 for expr in for_stmt.init.iter() {
189 visitor.visit_expr(expr)?;
190 }
191 for expr in for_stmt.condition.iter() {
192 visitor.visit_expr(expr)?;
193 }
194 for expr in for_stmt.update.iter() {
195 visitor.visit_expr(expr)?;
196 }
197 visitor.visit_stmt(&for_stmt.body)?;
198 }
199 StmtKind::Foreach(foreach_stmt) => {
200 visitor.visit_expr(&foreach_stmt.expr)?;
201 if let Some(key) = &foreach_stmt.key {
202 visitor.visit_expr(key)?;
203 }
204 visitor.visit_expr(&foreach_stmt.value)?;
205 visitor.visit_stmt(&foreach_stmt.body)?;
206 }
207 StmtKind::DoWhile(do_while) => {
208 visitor.visit_stmt(&do_while.body)?;
209 visitor.visit_expr(&do_while.condition)?;
210 }
211 StmtKind::Function(func) => {
212 walk_owned_function_like(visitor, &func.attributes, &func.params, &func.return_type)?;
213 for stmt in func.body.iter() {
214 visitor.visit_stmt(stmt)?;
215 }
216 }
217 StmtKind::Break(expr) | StmtKind::Continue(expr) => {
218 if let Some(expr) = expr {
219 visitor.visit_expr(expr)?;
220 }
221 }
222 StmtKind::Switch(switch_stmt) => {
223 visitor.visit_expr(&switch_stmt.expr)?;
224 for case in switch_stmt.cases.iter() {
225 if let Some(value) = &case.value {
226 visitor.visit_expr(value)?;
227 }
228 for stmt in case.body.iter() {
229 visitor.visit_stmt(stmt)?;
230 }
231 }
232 }
233 StmtKind::Throw(expr) => {
234 visitor.visit_expr(expr)?;
235 }
236 StmtKind::TryCatch(tc) => {
237 for stmt in tc.body.iter() {
238 visitor.visit_stmt(stmt)?;
239 }
240 for catch in tc.catches.iter() {
241 visitor.visit_catch_clause(catch)?;
242 }
243 if let Some(finally) = &tc.finally {
244 for stmt in finally.iter() {
245 visitor.visit_stmt(stmt)?;
246 }
247 }
248 }
249 StmtKind::Declare(decl) => {
250 for (_, expr) in decl.directives.iter() {
251 visitor.visit_expr(expr)?;
252 }
253 if let Some(body) = &decl.body {
254 visitor.visit_stmt(body)?;
255 }
256 }
257 StmtKind::Unset(exprs) | StmtKind::Global(exprs) => {
258 for expr in exprs.iter() {
259 visitor.visit_expr(expr)?;
260 }
261 }
262 StmtKind::Class(class) => {
263 walk_owned_attributes(visitor, &class.attributes)?;
264 if let Some(extends) = &class.extends {
265 visitor.visit_name(extends)?;
266 }
267 for name in class.implements.iter() {
268 visitor.visit_name(name)?;
269 }
270 for member in class.members.iter() {
271 visitor.visit_class_member(member)?;
272 }
273 }
274 StmtKind::Interface(iface) => {
275 walk_owned_attributes(visitor, &iface.attributes)?;
276 for name in iface.extends.iter() {
277 visitor.visit_name(name)?;
278 }
279 for member in iface.members.iter() {
280 visitor.visit_class_member(member)?;
281 }
282 }
283 StmtKind::Trait(trait_decl) => {
284 walk_owned_attributes(visitor, &trait_decl.attributes)?;
285 for member in trait_decl.members.iter() {
286 visitor.visit_class_member(member)?;
287 }
288 }
289 StmtKind::Enum(enum_decl) => {
290 walk_owned_attributes(visitor, &enum_decl.attributes)?;
291 if let Some(scalar_type) = &enum_decl.scalar_type {
292 visitor.visit_name(scalar_type)?;
293 }
294 for name in enum_decl.implements.iter() {
295 visitor.visit_name(name)?;
296 }
297 for member in enum_decl.members.iter() {
298 visitor.visit_enum_member(member)?;
299 }
300 }
301 StmtKind::Namespace(ns) => {
302 if let NamespaceBody::Braced(stmts) = &ns.body {
303 for stmt in stmts.iter() {
304 visitor.visit_stmt(stmt)?;
305 }
306 }
307 }
308 StmtKind::Const(items) => {
309 for item in items.iter() {
310 walk_owned_attributes(visitor, &item.attributes)?;
311 visitor.visit_expr(&item.value)?;
312 }
313 }
314 StmtKind::StaticVar(vars) => {
315 for var in vars.iter() {
316 if let Some(default) = &var.default {
317 visitor.visit_expr(default)?;
318 }
319 }
320 }
321 StmtKind::Use(decl) => {
322 for item in decl.uses.iter() {
323 visitor.visit_name(&item.name)?;
324 }
325 }
326 StmtKind::Goto(_)
327 | StmtKind::Label(_)
328 | StmtKind::Nop
329 | StmtKind::InlineHtml(_)
330 | StmtKind::HaltCompiler(_)
331 | StmtKind::Error => {}
332 }
333 ControlFlow::Continue(())
334}
335
336pub fn walk_owned_expr<V: OwnedVisitor + ?Sized>(visitor: &mut V, expr: &Expr) -> ControlFlow<()> {
338 match &expr.kind {
339 ExprKind::Assign(assign) => {
340 visitor.visit_expr(&assign.target)?;
341 visitor.visit_expr(&assign.value)?;
342 }
343 ExprKind::Binary(binary) => {
344 visitor.visit_expr(&binary.left)?;
345 visitor.visit_expr(&binary.right)?;
346 }
347 ExprKind::UnaryPrefix(unary) => {
348 visitor.visit_expr(&unary.operand)?;
349 }
350 ExprKind::UnaryPostfix(unary) => {
351 visitor.visit_expr(&unary.operand)?;
352 }
353 ExprKind::Ternary(ternary) => {
354 visitor.visit_expr(&ternary.condition)?;
355 if let Some(then_expr) = &ternary.then_expr {
356 visitor.visit_expr(then_expr)?;
357 }
358 visitor.visit_expr(&ternary.else_expr)?;
359 }
360 ExprKind::NullCoalesce(nc) => {
361 visitor.visit_expr(&nc.left)?;
362 visitor.visit_expr(&nc.right)?;
363 }
364 ExprKind::FunctionCall(call) => {
365 visitor.visit_expr(&call.name)?;
366 for arg in call.args.iter() {
367 visitor.visit_arg(arg)?;
368 }
369 }
370 ExprKind::Array(elements) => {
371 for elem in elements.iter() {
372 if let Some(key) = &elem.key {
373 visitor.visit_expr(key)?;
374 }
375 visitor.visit_expr(&elem.value)?;
376 }
377 }
378 ExprKind::ArrayAccess(access) => {
379 visitor.visit_expr(&access.array)?;
380 if let Some(index) = &access.index {
381 visitor.visit_expr(index)?;
382 }
383 }
384 ExprKind::Print(expr) => {
385 visitor.visit_expr(expr)?;
386 }
387 ExprKind::Parenthesized(expr) => {
388 visitor.visit_expr(expr)?;
389 }
390 ExprKind::Cast(_, expr) => {
391 visitor.visit_expr(expr)?;
392 }
393 ExprKind::ErrorSuppress(expr) => {
394 visitor.visit_expr(expr)?;
395 }
396 ExprKind::Isset(exprs) => {
397 for expr in exprs.iter() {
398 visitor.visit_expr(expr)?;
399 }
400 }
401 ExprKind::Empty(expr) => {
402 visitor.visit_expr(expr)?;
403 }
404 ExprKind::Include(_, expr) => {
405 visitor.visit_expr(expr)?;
406 }
407 ExprKind::Eval(expr) => {
408 visitor.visit_expr(expr)?;
409 }
410 ExprKind::Exit(expr) => {
411 if let Some(expr) = expr {
412 visitor.visit_expr(expr)?;
413 }
414 }
415 ExprKind::Clone(expr) => {
416 visitor.visit_expr(expr)?;
417 }
418 ExprKind::CloneWith(object, overrides) => {
419 visitor.visit_expr(object)?;
420 visitor.visit_expr(overrides)?;
421 }
422 ExprKind::New(new_expr) => {
423 visitor.visit_expr(&new_expr.class)?;
424 for arg in new_expr.args.iter() {
425 visitor.visit_arg(arg)?;
426 }
427 }
428 ExprKind::PropertyAccess(access) | ExprKind::NullsafePropertyAccess(access) => {
429 visitor.visit_expr(&access.object)?;
430 visitor.visit_expr(&access.property)?;
431 }
432 ExprKind::MethodCall(call) | ExprKind::NullsafeMethodCall(call) => {
433 visitor.visit_expr(&call.object)?;
434 visitor.visit_expr(&call.method)?;
435 for arg in call.args.iter() {
436 visitor.visit_arg(arg)?;
437 }
438 }
439 ExprKind::StaticPropertyAccess(access) | ExprKind::ClassConstAccess(access) => {
440 visitor.visit_expr(&access.class)?;
441 visitor.visit_expr(&access.member)?;
442 }
443 ExprKind::ClassConstAccessDynamic { class, member }
444 | ExprKind::StaticPropertyAccessDynamic { class, member } => {
445 visitor.visit_expr(class)?;
446 visitor.visit_expr(member)?;
447 }
448 ExprKind::StaticMethodCall(call) => {
449 visitor.visit_expr(&call.class)?;
450 visitor.visit_expr(&call.method)?;
451 for arg in call.args.iter() {
452 visitor.visit_arg(arg)?;
453 }
454 }
455 ExprKind::StaticDynMethodCall(call) => {
456 visitor.visit_expr(&call.class)?;
457 visitor.visit_expr(&call.method)?;
458 for arg in call.args.iter() {
459 visitor.visit_arg(arg)?;
460 }
461 }
462 ExprKind::Closure(closure) => {
463 walk_owned_function_like(
464 visitor,
465 &closure.attributes,
466 &closure.params,
467 &closure.return_type,
468 )?;
469 for use_var in closure.use_vars.iter() {
470 visitor.visit_closure_use_var(use_var)?;
471 }
472 for stmt in closure.body.iter() {
473 visitor.visit_stmt(stmt)?;
474 }
475 }
476 ExprKind::ArrowFunction(arrow) => {
477 walk_owned_function_like(
478 visitor,
479 &arrow.attributes,
480 &arrow.params,
481 &arrow.return_type,
482 )?;
483 visitor.visit_expr(&arrow.body)?;
484 }
485 ExprKind::Match(match_expr) => {
486 visitor.visit_expr(&match_expr.subject)?;
487 for arm in match_expr.arms.iter() {
488 visitor.visit_match_arm(arm)?;
489 }
490 }
491 ExprKind::ThrowExpr(expr) => {
492 visitor.visit_expr(expr)?;
493 }
494 ExprKind::Yield(yield_expr) => {
495 if let Some(key) = &yield_expr.key {
496 visitor.visit_expr(key)?;
497 }
498 if let Some(value) = &yield_expr.value {
499 visitor.visit_expr(value)?;
500 }
501 }
502 ExprKind::AnonymousClass(class) => {
503 walk_owned_attributes(visitor, &class.attributes)?;
504 for member in class.members.iter() {
505 visitor.visit_class_member(member)?;
506 }
507 }
508 ExprKind::InterpolatedString(parts) | ExprKind::ShellExec(parts) => {
509 for part in parts.iter() {
510 if let StringPart::Expr(e) = part {
511 visitor.visit_expr(e)?;
512 }
513 }
514 }
515 ExprKind::Heredoc { 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_owned_param<V: OwnedVisitor + ?Sized>(
554 visitor: &mut V,
555 param: &Param,
556) -> ControlFlow<()> {
557 walk_owned_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_owned_arg<V: OwnedVisitor + ?Sized>(visitor: &mut V, arg: &Arg) -> ControlFlow<()> {
572 visitor.visit_expr(&arg.value)
573}
574
575pub fn walk_owned_class_member<V: OwnedVisitor + ?Sized>(
577 visitor: &mut V,
578 member: &ClassMember,
579) -> ControlFlow<()> {
580 match &member.kind {
581 ClassMemberKind::Property(prop) => {
582 walk_owned_property_decl(visitor, prop)?;
583 }
584 ClassMemberKind::Method(method) => {
585 walk_owned_method_decl(visitor, method)?;
586 }
587 ClassMemberKind::ClassConst(cc) => {
588 walk_owned_class_const_decl(visitor, cc)?;
589 }
590 ClassMemberKind::TraitUse(trait_use) => {
591 visitor.visit_trait_use(trait_use)?;
592 }
593 }
594 ControlFlow::Continue(())
595}
596
597pub fn walk_owned_property_hook<V: OwnedVisitor + ?Sized>(
599 visitor: &mut V,
600 hook: &PropertyHook,
601) -> ControlFlow<()> {
602 walk_owned_attributes(visitor, &hook.attributes)?;
603 for param in hook.params.iter() {
604 visitor.visit_param(param)?;
605 }
606 match &hook.body {
607 PropertyHookBody::Block(stmts) => {
608 for stmt in stmts.iter() {
609 visitor.visit_stmt(stmt)?;
610 }
611 }
612 PropertyHookBody::Expression(expr) => {
613 visitor.visit_expr(expr)?;
614 }
615 PropertyHookBody::Abstract => {}
616 }
617 ControlFlow::Continue(())
618}
619
620pub fn walk_owned_enum_member<V: OwnedVisitor + ?Sized>(
622 visitor: &mut V,
623 member: &EnumMember,
624) -> ControlFlow<()> {
625 match &member.kind {
626 EnumMemberKind::Case(case) => {
627 walk_owned_attributes(visitor, &case.attributes)?;
628 if let Some(value) = &case.value {
629 visitor.visit_expr(value)?;
630 }
631 }
632 EnumMemberKind::Method(method) => {
633 walk_owned_method_decl(visitor, method)?;
634 }
635 EnumMemberKind::ClassConst(cc) => {
636 walk_owned_class_const_decl(visitor, cc)?;
637 }
638 EnumMemberKind::TraitUse(trait_use) => {
639 visitor.visit_trait_use(trait_use)?;
640 }
641 }
642 ControlFlow::Continue(())
643}
644
645pub fn walk_owned_type_hint<V: OwnedVisitor + ?Sized>(
647 visitor: &mut V,
648 type_hint: &TypeHint,
649) -> ControlFlow<()> {
650 match &type_hint.kind {
651 TypeHintKind::Nullable(inner) => {
652 visitor.visit_type_hint(inner)?;
653 }
654 TypeHintKind::Union(types) | TypeHintKind::Intersection(types) => {
655 for ty in types.iter() {
656 visitor.visit_type_hint(ty)?;
657 }
658 }
659 TypeHintKind::Named(name) => {
660 visitor.visit_name(name)?;
661 }
662 TypeHintKind::Keyword(_, _) => {}
663 }
664 ControlFlow::Continue(())
665}
666
667pub fn walk_owned_attribute<V: OwnedVisitor + ?Sized>(
669 visitor: &mut V,
670 attribute: &Attribute,
671) -> ControlFlow<()> {
672 visitor.visit_name(&attribute.name)?;
673 for arg in attribute.args.iter() {
674 visitor.visit_arg(arg)?;
675 }
676 ControlFlow::Continue(())
677}
678
679pub fn walk_owned_catch_clause<V: OwnedVisitor + ?Sized>(
681 visitor: &mut V,
682 catch: &CatchClause,
683) -> ControlFlow<()> {
684 for ty in catch.types.iter() {
685 visitor.visit_name(ty)?;
686 }
687 for stmt in catch.body.iter() {
688 visitor.visit_stmt(stmt)?;
689 }
690 ControlFlow::Continue(())
691}
692
693pub fn walk_owned_match_arm<V: OwnedVisitor + ?Sized>(
695 visitor: &mut V,
696 arm: &MatchArm,
697) -> ControlFlow<()> {
698 if let Some(conditions) = &arm.conditions {
699 for cond in conditions.iter() {
700 visitor.visit_expr(cond)?;
701 }
702 }
703 visitor.visit_expr(&arm.body)
704}
705
706pub fn walk_owned_trait_use<V: OwnedVisitor + ?Sized>(
708 visitor: &mut V,
709 trait_use: &TraitUseDecl,
710) -> ControlFlow<()> {
711 for name in trait_use.traits.iter() {
712 visitor.visit_name(name)?;
713 }
714 for adaptation in trait_use.adaptations.iter() {
715 visitor.visit_trait_adaptation(adaptation)?;
716 }
717 ControlFlow::Continue(())
718}
719
720fn walk_owned_function_like<V: OwnedVisitor + ?Sized>(
725 visitor: &mut V,
726 attributes: &[Attribute],
727 params: &[Param],
728 return_type: &Option<TypeHint>,
729) -> ControlFlow<()> {
730 walk_owned_attributes(visitor, attributes)?;
731 for param in params.iter() {
732 visitor.visit_param(param)?;
733 }
734 if let Some(ret) = return_type {
735 visitor.visit_type_hint(ret)?;
736 }
737 ControlFlow::Continue(())
738}
739
740fn walk_owned_method_decl<V: OwnedVisitor + ?Sized>(
741 visitor: &mut V,
742 method: &MethodDecl,
743) -> ControlFlow<()> {
744 walk_owned_function_like(
745 visitor,
746 &method.attributes,
747 &method.params,
748 &method.return_type,
749 )?;
750 if let Some(body) = &method.body {
751 for stmt in body.iter() {
752 visitor.visit_stmt(stmt)?;
753 }
754 }
755 ControlFlow::Continue(())
756}
757
758fn walk_owned_class_const_decl<V: OwnedVisitor + ?Sized>(
759 visitor: &mut V,
760 cc: &ClassConstDecl,
761) -> ControlFlow<()> {
762 walk_owned_attributes(visitor, &cc.attributes)?;
763 if let Some(type_hint) = &cc.type_hint {
764 visitor.visit_type_hint(type_hint)?;
765 }
766 visitor.visit_expr(&cc.value)
767}
768
769fn walk_owned_property_decl<V: OwnedVisitor + ?Sized>(
770 visitor: &mut V,
771 prop: &PropertyDecl,
772) -> ControlFlow<()> {
773 walk_owned_attributes(visitor, &prop.attributes)?;
774 if let Some(type_hint) = &prop.type_hint {
775 visitor.visit_type_hint(type_hint)?;
776 }
777 if let Some(default) = &prop.default {
778 visitor.visit_expr(default)?;
779 }
780 for hook in prop.hooks.iter() {
781 visitor.visit_property_hook(hook)?;
782 }
783 ControlFlow::Continue(())
784}
785
786fn walk_owned_attributes<V: OwnedVisitor + ?Sized>(
787 visitor: &mut V,
788 attributes: &[Attribute],
789) -> ControlFlow<()> {
790 for attr in attributes.iter() {
791 visitor.visit_attribute(attr)?;
792 }
793 ControlFlow::Continue(())
794}
795
796#[derive(Debug, Clone, Default)]
805pub struct OwnedScope {
806 pub namespace: Option<String>,
808 pub class_name: Option<String>,
810 pub function_name: Option<String>,
812}
813
814pub trait OwnedScopeVisitor {
819 fn visit_program(&mut self, _program: &Program, _scope: &OwnedScope) -> ControlFlow<()> {
820 ControlFlow::Continue(())
821 }
822 fn visit_stmt(&mut self, _stmt: &Stmt, _scope: &OwnedScope) -> ControlFlow<()> {
823 ControlFlow::Continue(())
824 }
825 fn visit_expr(&mut self, _expr: &Expr, _scope: &OwnedScope) -> ControlFlow<()> {
826 ControlFlow::Continue(())
827 }
828 fn visit_param(&mut self, _param: &Param, _scope: &OwnedScope) -> ControlFlow<()> {
829 ControlFlow::Continue(())
830 }
831 fn visit_arg(&mut self, _arg: &Arg, _scope: &OwnedScope) -> ControlFlow<()> {
832 ControlFlow::Continue(())
833 }
834 fn visit_class_member(
835 &mut self,
836 _member: &ClassMember,
837 _scope: &OwnedScope,
838 ) -> ControlFlow<()> {
839 ControlFlow::Continue(())
840 }
841 fn visit_enum_member(&mut self, _member: &EnumMember, _scope: &OwnedScope) -> ControlFlow<()> {
842 ControlFlow::Continue(())
843 }
844 fn visit_property_hook(
845 &mut self,
846 _hook: &PropertyHook,
847 _scope: &OwnedScope,
848 ) -> ControlFlow<()> {
849 ControlFlow::Continue(())
850 }
851 fn visit_type_hint(&mut self, _type_hint: &TypeHint, _scope: &OwnedScope) -> ControlFlow<()> {
852 ControlFlow::Continue(())
853 }
854 fn visit_attribute(&mut self, _attribute: &Attribute, _scope: &OwnedScope) -> ControlFlow<()> {
855 ControlFlow::Continue(())
856 }
857 fn visit_catch_clause(&mut self, _catch: &CatchClause, _scope: &OwnedScope) -> ControlFlow<()> {
858 ControlFlow::Continue(())
859 }
860 fn visit_match_arm(&mut self, _arm: &MatchArm, _scope: &OwnedScope) -> ControlFlow<()> {
861 ControlFlow::Continue(())
862 }
863 fn visit_closure_use_var(
864 &mut self,
865 _var: &ClosureUseVar,
866 _scope: &OwnedScope,
867 ) -> ControlFlow<()> {
868 ControlFlow::Continue(())
869 }
870 fn visit_trait_use(
871 &mut self,
872 _trait_use: &TraitUseDecl,
873 _scope: &OwnedScope,
874 ) -> ControlFlow<()> {
875 ControlFlow::Continue(())
876 }
877 fn visit_trait_adaptation(
878 &mut self,
879 _adaptation: &TraitAdaptation,
880 _scope: &OwnedScope,
881 ) -> ControlFlow<()> {
882 ControlFlow::Continue(())
883 }
884 fn visit_comment(&mut self, _comment: &Comment, _scope: &OwnedScope) -> ControlFlow<()> {
885 ControlFlow::Continue(())
886 }
887}
888
889pub struct OwnedScopeWalker<V> {
891 inner: V,
892 scope: OwnedScope,
893}
894
895impl<V> OwnedScopeWalker<V> {
896 pub fn new(inner: V) -> Self {
898 Self {
899 inner,
900 scope: OwnedScope::default(),
901 }
902 }
903
904 pub fn into_inner(self) -> V {
906 self.inner
907 }
908
909 pub fn inner(&self) -> &V {
911 &self.inner
912 }
913
914 pub fn inner_mut(&mut self) -> &mut V {
916 &mut self.inner
917 }
918}
919
920impl<V: OwnedScopeVisitor> OwnedScopeWalker<V> {
921 pub fn walk(&mut self, program: &Program) -> ControlFlow<()> {
923 self.visit_program(program)
924 }
925}
926
927fn owned_name_repr(name: &Name) -> Option<String> {
928 if name.parts.is_empty() {
929 return None;
930 }
931 Some(
932 name.parts
933 .iter()
934 .map(|s| s.as_ref())
935 .collect::<Vec<_>>()
936 .join("\\"),
937 )
938}
939
940impl<V: OwnedScopeVisitor> OwnedVisitor for OwnedScopeWalker<V> {
941 fn visit_program(&mut self, program: &Program) -> ControlFlow<()> {
942 self.inner.visit_program(program, &self.scope)?;
943 walk_owned_program(self, program)
944 }
945
946 fn visit_stmt(&mut self, stmt: &Stmt) -> ControlFlow<()> {
947 self.inner.visit_stmt(stmt, &self.scope)?;
948 match &stmt.kind {
949 StmtKind::Function(func) => {
950 let fn_name = func.name.as_ref().map(|n| n.to_string());
951 let prev_fn = std::mem::replace(&mut self.scope.function_name, fn_name);
952 walk_owned_stmt(self, stmt)?;
953 self.scope.function_name = prev_fn;
954 }
955 StmtKind::Class(class) => {
956 let class_name = class
957 .name
958 .as_ref()
959 .and_then(|ident| ident.as_ref())
960 .map(|s| s.to_string());
961 let prev_class = std::mem::replace(&mut self.scope.class_name, class_name);
962 let prev_fn = self.scope.function_name.take();
963 walk_owned_stmt(self, stmt)?;
964 self.scope.class_name = prev_class;
965 self.scope.function_name = prev_fn;
966 }
967 StmtKind::Interface(iface) => {
968 let iface_name = iface.name.as_ref().map(|n| n.to_string());
969 let prev_class = std::mem::replace(&mut self.scope.class_name, iface_name);
970 let prev_fn = self.scope.function_name.take();
971 walk_owned_stmt(self, stmt)?;
972 self.scope.class_name = prev_class;
973 self.scope.function_name = prev_fn;
974 }
975 StmtKind::Trait(trait_decl) => {
976 let trait_name = trait_decl.name.as_ref().map(|n| n.to_string());
977 let prev_class = std::mem::replace(&mut self.scope.class_name, trait_name);
978 let prev_fn = self.scope.function_name.take();
979 walk_owned_stmt(self, stmt)?;
980 self.scope.class_name = prev_class;
981 self.scope.function_name = prev_fn;
982 }
983 StmtKind::Enum(enum_decl) => {
984 let enum_name = enum_decl.name.as_ref().map(|n| n.to_string());
985 let prev_class = std::mem::replace(&mut self.scope.class_name, enum_name);
986 let prev_fn = self.scope.function_name.take();
987 walk_owned_stmt(self, stmt)?;
988 self.scope.class_name = prev_class;
989 self.scope.function_name = prev_fn;
990 }
991 StmtKind::Namespace(ns) => {
992 let ns_str = ns.name.as_ref().and_then(owned_name_repr);
993 match &ns.body {
994 NamespaceBody::Braced(_) => {
995 let prev_ns = std::mem::replace(&mut self.scope.namespace, ns_str);
996 let prev_class = self.scope.class_name.take();
997 let prev_fn = self.scope.function_name.take();
998 walk_owned_stmt(self, stmt)?;
999 self.scope.namespace = prev_ns;
1000 self.scope.class_name = prev_class;
1001 self.scope.function_name = prev_fn;
1002 }
1003 NamespaceBody::Simple => {
1004 self.scope.namespace = ns_str;
1005 self.scope.class_name = None;
1006 self.scope.function_name = None;
1007 }
1008 }
1009 }
1010 _ => {
1011 walk_owned_stmt(self, stmt)?;
1012 }
1013 }
1014 ControlFlow::Continue(())
1015 }
1016
1017 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
1018 self.inner.visit_expr(expr, &self.scope)?;
1019 match &expr.kind {
1020 ExprKind::Closure(_) | ExprKind::ArrowFunction(_) => {
1021 let prev_fn = self.scope.function_name.take();
1022 walk_owned_expr(self, expr)?;
1023 self.scope.function_name = prev_fn;
1024 }
1025 ExprKind::AnonymousClass(_) => {
1026 let prev_class = self.scope.class_name.take();
1027 let prev_fn = self.scope.function_name.take();
1028 walk_owned_expr(self, expr)?;
1029 self.scope.class_name = prev_class;
1030 self.scope.function_name = prev_fn;
1031 }
1032 _ => {
1033 walk_owned_expr(self, expr)?;
1034 }
1035 }
1036 ControlFlow::Continue(())
1037 }
1038
1039 fn visit_class_member(&mut self, member: &ClassMember) -> ControlFlow<()> {
1040 self.inner.visit_class_member(member, &self.scope)?;
1041 if let ClassMemberKind::Method(method) = &member.kind {
1042 let fn_name = method.name.as_ref().map(|n| n.to_string());
1043 let prev_fn = std::mem::replace(&mut self.scope.function_name, fn_name);
1044 walk_owned_class_member(self, member)?;
1045 self.scope.function_name = prev_fn;
1046 } else {
1047 walk_owned_class_member(self, member)?;
1048 }
1049 ControlFlow::Continue(())
1050 }
1051
1052 fn visit_enum_member(&mut self, member: &EnumMember) -> ControlFlow<()> {
1053 self.inner.visit_enum_member(member, &self.scope)?;
1054 if let EnumMemberKind::Method(method) = &member.kind {
1055 let fn_name = method.name.as_ref().map(|n| n.to_string());
1056 let prev_fn = std::mem::replace(&mut self.scope.function_name, fn_name);
1057 walk_owned_enum_member(self, member)?;
1058 self.scope.function_name = prev_fn;
1059 } else {
1060 walk_owned_enum_member(self, member)?;
1061 }
1062 ControlFlow::Continue(())
1063 }
1064
1065 fn visit_param(&mut self, param: &Param) -> ControlFlow<()> {
1066 self.inner.visit_param(param, &self.scope)?;
1067 walk_owned_param(self, param)
1068 }
1069
1070 fn visit_arg(&mut self, arg: &Arg) -> ControlFlow<()> {
1071 self.inner.visit_arg(arg, &self.scope)?;
1072 walk_owned_arg(self, arg)
1073 }
1074
1075 fn visit_property_hook(&mut self, hook: &PropertyHook) -> ControlFlow<()> {
1076 self.inner.visit_property_hook(hook, &self.scope)?;
1077 walk_owned_property_hook(self, hook)
1078 }
1079
1080 fn visit_type_hint(&mut self, type_hint: &TypeHint) -> ControlFlow<()> {
1081 self.inner.visit_type_hint(type_hint, &self.scope)?;
1082 walk_owned_type_hint(self, type_hint)
1083 }
1084
1085 fn visit_attribute(&mut self, attribute: &Attribute) -> ControlFlow<()> {
1086 self.inner.visit_attribute(attribute, &self.scope)?;
1087 walk_owned_attribute(self, attribute)
1088 }
1089
1090 fn visit_catch_clause(&mut self, catch: &CatchClause) -> ControlFlow<()> {
1091 self.inner.visit_catch_clause(catch, &self.scope)?;
1092 walk_owned_catch_clause(self, catch)
1093 }
1094
1095 fn visit_match_arm(&mut self, arm: &MatchArm) -> ControlFlow<()> {
1096 self.inner.visit_match_arm(arm, &self.scope)?;
1097 walk_owned_match_arm(self, arm)
1098 }
1099
1100 fn visit_closure_use_var(&mut self, var: &ClosureUseVar) -> ControlFlow<()> {
1101 self.inner.visit_closure_use_var(var, &self.scope)
1102 }
1103
1104 fn visit_trait_use(&mut self, trait_use: &TraitUseDecl) -> ControlFlow<()> {
1105 self.inner.visit_trait_use(trait_use, &self.scope)?;
1106 walk_owned_trait_use(self, trait_use)
1107 }
1108
1109 fn visit_trait_adaptation(&mut self, adaptation: &TraitAdaptation) -> ControlFlow<()> {
1110 self.inner.visit_trait_adaptation(adaptation, &self.scope)
1111 }
1112
1113 fn visit_comment(&mut self, comment: &Comment) -> ControlFlow<()> {
1114 self.inner.visit_comment(comment, &self.scope)
1115 }
1116}
1117
1118#[cfg(test)]
1123mod tests {
1124 use super::*;
1125 use crate::ast::{AssignOp, BinaryOp};
1126 use crate::Span;
1127
1128 fn dummy_var(name: &str) -> Expr {
1129 Expr {
1130 kind: ExprKind::Variable(Box::from(name)),
1131 span: Span::DUMMY,
1132 }
1133 }
1134
1135 fn dummy_int(n: i64) -> Expr {
1136 Expr {
1137 kind: ExprKind::Int(n),
1138 span: Span::DUMMY,
1139 }
1140 }
1141
1142 fn binary(left: Expr, op: BinaryOp, right: Expr) -> Expr {
1143 Expr {
1144 kind: ExprKind::Binary(BinaryExpr {
1145 left: Box::new(left),
1146 op,
1147 right: Box::new(right),
1148 }),
1149 span: Span::DUMMY,
1150 }
1151 }
1152
1153 fn expr_stmt(e: Expr) -> Stmt {
1154 Stmt {
1155 kind: StmtKind::Expression(Box::new(e)),
1156 span: Span::DUMMY,
1157 }
1158 }
1159
1160 fn program(stmts: impl IntoIterator<Item = Stmt>) -> Program {
1161 Program {
1162 stmts: stmts.into_iter().collect(),
1163 span: Span::DUMMY,
1164 }
1165 }
1166
1167 struct VarCounter {
1168 count: usize,
1169 }
1170
1171 impl OwnedVisitor for VarCounter {
1172 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
1173 if matches!(&expr.kind, ExprKind::Variable(_)) {
1174 self.count += 1;
1175 }
1176 walk_owned_expr(self, expr)
1177 }
1178 }
1179
1180 #[test]
1181 fn counts_variables() {
1182 let p = program([expr_stmt(Expr {
1184 kind: ExprKind::Assign(AssignExpr {
1185 target: Box::new(dummy_var("x")),
1186 op: AssignOp::Assign,
1187 value: Box::new(binary(dummy_var("y"), BinaryOp::Add, dummy_var("z"))),
1188 by_ref: false,
1189 }),
1190 span: Span::DUMMY,
1191 })]);
1192 let mut v = VarCounter { count: 0 };
1193 let _ = v.visit_program(&p);
1194 assert_eq!(v.count, 3);
1195 }
1196
1197 #[test]
1198 fn early_termination() {
1199 let p = program([expr_stmt(binary(
1201 dummy_var("a"),
1202 BinaryOp::Add,
1203 dummy_var("b"),
1204 ))]);
1205
1206 struct FindFirst {
1207 found: Option<String>,
1208 }
1209 impl OwnedVisitor for FindFirst {
1210 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
1211 if let ExprKind::Variable(name) = &expr.kind {
1212 self.found = Some(name.to_string());
1213 return ControlFlow::Break(());
1214 }
1215 walk_owned_expr(self, expr)
1216 }
1217 }
1218
1219 let mut finder = FindFirst { found: None };
1220 let r = finder.visit_program(&p);
1221 assert!(r.is_break());
1222 assert_eq!(finder.found.as_deref(), Some("a"));
1223 }
1224
1225 #[test]
1226 fn skip_subtree() {
1227 let top = binary(dummy_int(1), BinaryOp::Add, dummy_int(2));
1229 let inner = binary(dummy_int(3), BinaryOp::Add, dummy_int(4));
1230 let func = Stmt {
1231 kind: StmtKind::Function(Box::new(FunctionDecl {
1232 name: Some(Box::from("foo")),
1233 params: Box::from([]),
1234 body: [expr_stmt(inner)].into_iter().collect(),
1235 return_type: None,
1236 by_ref: false,
1237 attributes: Box::from([]),
1238 doc_comment: None,
1239 })),
1240 span: Span::DUMMY,
1241 };
1242 let p = program([expr_stmt(top), func]);
1243
1244 struct SkipFunctions {
1245 expr_count: usize,
1246 }
1247 impl OwnedVisitor for SkipFunctions {
1248 fn visit_expr(&mut self, expr: &Expr) -> ControlFlow<()> {
1249 self.expr_count += 1;
1250 walk_owned_expr(self, expr)
1251 }
1252 fn visit_stmt(&mut self, stmt: &Stmt) -> ControlFlow<()> {
1253 if matches!(&stmt.kind, StmtKind::Function(_)) {
1254 return ControlFlow::Continue(());
1255 }
1256 walk_owned_stmt(self, stmt)
1257 }
1258 }
1259
1260 let mut v = SkipFunctions { expr_count: 0 };
1261 let _ = v.visit_program(&p);
1262 assert_eq!(v.expr_count, 3);
1264 }
1265
1266 #[test]
1267 fn scope_walker_tracks_class_and_method() {
1268 let method = ClassMember {
1270 kind: ClassMemberKind::Method(MethodDecl {
1271 name: Some(Box::from("bar")),
1272 visibility: Some(crate::ast::Visibility::Public),
1273 is_static: false,
1274 is_abstract: false,
1275 is_final: false,
1276 by_ref: false,
1277 params: Box::from([]),
1278 return_type: None,
1279 body: Some([expr_stmt(dummy_var("x"))].into_iter().collect()),
1280 attributes: Box::from([]),
1281 doc_comment: None,
1282 }),
1283 span: Span::DUMMY,
1284 };
1285 let class_stmt = Stmt {
1286 kind: StmtKind::Class(Box::new(ClassDecl {
1287 name: Some(Some(Box::from("Foo"))),
1288 modifiers: crate::ast::ClassModifiers::default(),
1289 extends: None,
1290 implements: Box::from([]),
1291 members: [method].into_iter().collect(),
1292 attributes: Box::from([]),
1293 doc_comment: None,
1294 })),
1295 span: Span::DUMMY,
1296 };
1297 let p = program([class_stmt]);
1298
1299 struct MethodCollector {
1300 methods: Vec<String>,
1301 }
1302 impl OwnedScopeVisitor for MethodCollector {
1303 fn visit_class_member(
1304 &mut self,
1305 member: &ClassMember,
1306 scope: &OwnedScope,
1307 ) -> ControlFlow<()> {
1308 if let ClassMemberKind::Method(m) = &member.kind {
1309 self.methods.push(format!(
1310 "{}::{}",
1311 scope.class_name.as_deref().unwrap_or("<anon>"),
1312 m.name.as_deref().unwrap_or("<error>"),
1313 ));
1314 }
1315 ControlFlow::Continue(())
1316 }
1317 }
1318
1319 let mut walker = OwnedScopeWalker::new(MethodCollector { methods: vec![] });
1320 let _ = walker.walk(&p);
1321 assert_eq!(walker.into_inner().methods, ["Foo::bar"]);
1322 }
1323}