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