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