1use super::*;
2
3pub trait Visitor<'ast> {
4 fn visit_program(&mut self, program: &'ast Program<'ast>) {
5 walk_program(self, program);
6 }
7
8 fn visit_stmt(&mut self, stmt: StmtId<'ast>) {
9 walk_stmt(self, stmt);
10 }
11
12 fn visit_expr(&mut self, expr: ExprId<'ast>) {
13 walk_expr(self, expr);
14 }
15
16 fn visit_arg(&mut self, arg: &'ast Arg<'ast>) {
17 walk_arg(self, arg);
18 }
19
20 fn visit_array_item(&mut self, item: &'ast ArrayItem<'ast>) {
21 walk_array_item(self, item);
22 }
23
24 fn visit_param(&mut self, param: &'ast Param<'ast>) {
25 walk_param(self, param);
26 }
27
28 fn visit_static_var(&mut self, var: &'ast StaticVar<'ast>) {
29 walk_static_var(self, var);
30 }
31
32 fn visit_match_arm(&mut self, arm: &'ast MatchArm<'ast>) {
33 walk_match_arm(self, arm);
34 }
35
36 fn visit_closure_use(&mut self, closure_use: &'ast ClosureUse<'ast>) {
37 walk_closure_use(self, closure_use);
38 }
39
40 fn visit_name(&mut self, name: &Name<'ast>) {
41 walk_name(self, name);
42 }
43
44 fn visit_type(&mut self, ty: &'ast Type<'ast>) {
45 walk_type(self, ty);
46 }
47
48 fn visit_attribute_group(&mut self, group: &'ast AttributeGroup<'ast>) {
49 walk_attribute_group(self, group);
50 }
51
52 fn visit_attribute(&mut self, attribute: &'ast Attribute<'ast>) {
53 walk_attribute(self, attribute);
54 }
55
56 fn visit_use_item(&mut self, use_item: &'ast UseItem<'ast>) {
57 walk_use_item(self, use_item);
58 }
59
60 fn visit_case(&mut self, case: &'ast Case<'ast>) {
61 walk_case(self, case);
62 }
63
64 fn visit_catch(&mut self, catch: &'ast Catch<'ast>) {
65 walk_catch(self, catch);
66 }
67
68 fn visit_class_member(&mut self, member: &'ast ClassMember<'ast>) {
69 walk_class_member(self, member);
70 }
71
72 fn visit_class_const(&mut self, class_const: &'ast ClassConst<'ast>) {
73 walk_class_const(self, class_const);
74 }
75
76 fn visit_declare_item(&mut self, item: &'ast DeclareItem<'ast>) {
77 walk_declare_item(self, item);
78 }
79
80 fn visit_trait_adaptation(&mut self, adaptation: &'ast TraitAdaptation<'ast>) {
81 walk_trait_adaptation(self, adaptation);
82 }
83
84 fn visit_trait_method_ref(&mut self, method_ref: &'ast TraitMethodRef<'ast>) {
85 walk_trait_method_ref(self, method_ref);
86 }
87
88 fn visit_property_entry(&mut self, entry: &'ast PropertyEntry<'ast>) {
89 walk_property_entry(self, entry);
90 }
91
92 fn visit_property_hook(&mut self, hook: &'ast PropertyHook<'ast>) {
93 walk_property_hook(self, hook);
94 }
95
96 fn visit_property_hook_body(&mut self, body: &'ast PropertyHookBody<'ast>) {
97 walk_property_hook_body(self, body);
98 }
99
100 fn visit_parse_error(&mut self, error: &'ast ParseError) {
101 let _ = error;
102 }
103}
104
105pub fn walk_program<'ast, V: Visitor<'ast> + ?Sized>(
106 visitor: &mut V,
107 program: &'ast Program<'ast>,
108) {
109 walk_statements(visitor, program.statements);
110
111 for error in program.errors {
112 visitor.visit_parse_error(error);
113 }
114}
115
116pub fn walk_stmt<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, stmt: StmtId<'ast>) {
117 match *stmt {
118 Stmt::Echo { exprs, .. } => walk_exprs(visitor, exprs),
119 Stmt::Return { expr, .. } => {
120 if let Some(expr) = expr {
121 visitor.visit_expr(expr);
122 }
123 }
124 Stmt::If {
125 condition,
126 then_block,
127 else_block,
128 ..
129 } => {
130 visitor.visit_expr(condition);
131 walk_statements(visitor, then_block);
132 if let Some(else_block) = else_block {
133 walk_statements(visitor, else_block);
134 }
135 }
136 Stmt::While {
137 condition, body, ..
138 } => {
139 visitor.visit_expr(condition);
140 walk_statements(visitor, body);
141 }
142 Stmt::DoWhile {
143 body, condition, ..
144 } => {
145 walk_statements(visitor, body);
146 visitor.visit_expr(condition);
147 }
148 Stmt::For {
149 init,
150 condition,
151 loop_expr,
152 body,
153 ..
154 } => {
155 walk_exprs(visitor, init);
156 walk_exprs(visitor, condition);
157 walk_exprs(visitor, loop_expr);
158 walk_statements(visitor, body);
159 }
160 Stmt::Foreach {
161 expr,
162 key_var,
163 value_var,
164 body,
165 ..
166 } => {
167 visitor.visit_expr(expr);
168 if let Some(key_var) = key_var {
169 visitor.visit_expr(key_var);
170 }
171 visitor.visit_expr(value_var);
172 walk_statements(visitor, body);
173 }
174 Stmt::Block { statements, .. } => walk_statements(visitor, statements),
175 Stmt::Function {
176 attributes,
177 params,
178 return_type,
179 body,
180 ..
181 } => {
182 walk_attributes(visitor, attributes);
183 walk_params(visitor, params);
184 if let Some(return_type) = return_type {
185 visitor.visit_type(return_type);
186 }
187 walk_statements(visitor, body);
188 }
189 Stmt::Class {
190 attributes,
191 extends,
192 implements,
193 members,
194 ..
195 } => {
196 walk_attributes(visitor, attributes);
197 if let Some(extends) = extends {
198 visitor.visit_name(&extends);
199 }
200 walk_names(visitor, implements);
201 walk_class_members(visitor, members);
202 }
203 Stmt::Interface {
204 attributes,
205 extends,
206 members,
207 ..
208 } => {
209 walk_attributes(visitor, attributes);
210 walk_names(visitor, extends);
211 walk_class_members(visitor, members);
212 }
213 Stmt::Trait {
214 attributes,
215 members,
216 ..
217 } => {
218 walk_attributes(visitor, attributes);
219 walk_class_members(visitor, members);
220 }
221 Stmt::Enum {
222 attributes,
223 backed_type,
224 implements,
225 members,
226 ..
227 } => {
228 walk_attributes(visitor, attributes);
229 if let Some(backed_type) = backed_type {
230 visitor.visit_type(backed_type);
231 }
232 walk_names(visitor, implements);
233 walk_class_members(visitor, members);
234 }
235 Stmt::Namespace { name, body, .. } => {
236 if let Some(name) = name {
237 visitor.visit_name(&name);
238 }
239 if let Some(body) = body {
240 walk_statements(visitor, body);
241 }
242 }
243 Stmt::Use { uses, .. } => {
244 for use_item in uses {
245 visitor.visit_use_item(use_item);
246 }
247 }
248 Stmt::Switch {
249 condition, cases, ..
250 } => {
251 visitor.visit_expr(condition);
252 for case in cases {
253 visitor.visit_case(case);
254 }
255 }
256 Stmt::Try {
257 body,
258 catches,
259 finally,
260 ..
261 } => {
262 walk_statements(visitor, body);
263 for catch in catches {
264 visitor.visit_catch(catch);
265 }
266 if let Some(finally) = finally {
267 walk_statements(visitor, finally);
268 }
269 }
270 Stmt::Throw { expr, .. } => visitor.visit_expr(expr),
271 Stmt::Const {
272 attributes, consts, ..
273 } => {
274 walk_attributes(visitor, attributes);
275 for class_const in consts {
276 visitor.visit_class_const(class_const);
277 }
278 }
279 Stmt::Break { level, .. } | Stmt::Continue { level, .. } => {
280 if let Some(level) = level {
281 visitor.visit_expr(level);
282 }
283 }
284 Stmt::Global { vars, .. } | Stmt::Unset { vars, .. } => {
285 walk_exprs(visitor, vars);
286 }
287 Stmt::Static { vars, .. } => {
288 for var in vars {
289 visitor.visit_static_var(var);
290 }
291 }
292 Stmt::Expression { expr, .. } => visitor.visit_expr(expr),
293 Stmt::InlineHtml { .. }
294 | Stmt::Nop { .. }
295 | Stmt::Label { .. }
296 | Stmt::Goto { .. }
297 | Stmt::Error { .. }
298 | Stmt::HaltCompiler { .. } => {}
299 Stmt::Declare { declares, body, .. } => {
300 for declare in declares {
301 visitor.visit_declare_item(declare);
302 }
303 walk_statements(visitor, body);
304 }
305 }
306}
307
308pub fn walk_expr<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, expr: ExprId<'ast>) {
309 match *expr {
310 Expr::Assign { var, expr, .. } | Expr::AssignRef { var, expr, .. } => {
311 visitor.visit_expr(var);
312 visitor.visit_expr(expr);
313 }
314 Expr::AssignOp { var, expr, .. } => {
315 visitor.visit_expr(var);
316 visitor.visit_expr(expr);
317 }
318 Expr::Binary { left, right, .. } => {
319 visitor.visit_expr(left);
320 visitor.visit_expr(right);
321 }
322 Expr::Unary { expr, .. }
323 | Expr::PostInc { var: expr, .. }
324 | Expr::PostDec { var: expr, .. }
325 | Expr::Print { expr, .. }
326 | Expr::Clone { expr, .. }
327 | Expr::Cast { expr, .. }
328 | Expr::Empty { expr, .. }
329 | Expr::Eval { expr, .. }
330 | Expr::Die {
331 expr: Some(expr), ..
332 }
333 | Expr::Exit {
334 expr: Some(expr), ..
335 } => visitor.visit_expr(expr),
336 Expr::Call { func, args, .. }
337 | Expr::MethodCall {
338 target: func, args, ..
339 }
340 | Expr::StaticCall {
341 class: func, args, ..
342 }
343 | Expr::NullsafeMethodCall {
344 target: func, args, ..
345 } => {
346 visitor.visit_expr(func);
347 for arg in args {
348 visitor.visit_arg(arg);
349 }
350 }
351 Expr::Array { items, .. } => {
352 for item in items {
353 visitor.visit_array_item(item);
354 }
355 }
356 Expr::ArrayDimFetch { array, dim, .. } => {
357 visitor.visit_expr(array);
358 if let Some(dim) = dim {
359 visitor.visit_expr(dim);
360 }
361 }
362 Expr::PropertyFetch {
363 target, property, ..
364 }
365 | Expr::NullsafePropertyFetch {
366 target, property, ..
367 }
368 | Expr::ClassConstFetch {
369 class: target,
370 constant: property,
371 ..
372 } => {
373 visitor.visit_expr(target);
374 visitor.visit_expr(property);
375 }
376 Expr::New { class, args, .. } => {
377 visitor.visit_expr(class);
378 for arg in args {
379 visitor.visit_arg(arg);
380 }
381 }
382 Expr::InterpolatedString { parts, .. } | Expr::ShellExec { parts, .. } => {
383 walk_exprs(visitor, parts);
384 }
385 Expr::Include { expr, .. } => visitor.visit_expr(expr),
386 Expr::Ternary {
387 condition,
388 if_true,
389 if_false,
390 ..
391 } => {
392 visitor.visit_expr(condition);
393 if let Some(if_true) = if_true {
394 visitor.visit_expr(if_true);
395 }
396 visitor.visit_expr(if_false);
397 }
398 Expr::Match {
399 condition, arms, ..
400 } => {
401 visitor.visit_expr(condition);
402 for arm in arms {
403 visitor.visit_match_arm(arm);
404 }
405 }
406 Expr::AnonymousClass {
407 attributes,
408 args,
409 extends,
410 implements,
411 members,
412 ..
413 } => {
414 walk_attributes(visitor, attributes);
415 for arg in args {
416 visitor.visit_arg(arg);
417 }
418 if let Some(extends) = extends {
419 visitor.visit_name(&extends);
420 }
421 walk_names(visitor, implements);
422 walk_class_members(visitor, members);
423 }
424 Expr::Yield {
425 key, value, from, ..
426 } => {
427 if let Some(key) = key {
428 visitor.visit_expr(key);
429 }
430 if let Some(value) = value {
431 visitor.visit_expr(value);
432 }
433 let _ = from;
434 }
435 Expr::Isset { vars, .. } => walk_exprs(visitor, vars),
436 Expr::Closure {
437 attributes,
438 params,
439 uses,
440 return_type,
441 body,
442 ..
443 } => {
444 walk_attributes(visitor, attributes);
445 walk_params(visitor, params);
446 for closure_use in uses {
447 visitor.visit_closure_use(closure_use);
448 }
449 if let Some(return_type) = return_type {
450 visitor.visit_type(return_type);
451 }
452 walk_statements(visitor, body);
453 }
454 Expr::ArrowFunction {
455 attributes,
456 params,
457 return_type,
458 expr,
459 ..
460 } => {
461 walk_attributes(visitor, attributes);
462 walk_params(visitor, params);
463 if let Some(return_type) = return_type {
464 visitor.visit_type(return_type);
465 }
466 visitor.visit_expr(expr);
467 }
468 Expr::Variable { .. }
469 | Expr::Integer { .. }
470 | Expr::Float { .. }
471 | Expr::Boolean { .. }
472 | Expr::Null { .. }
473 | Expr::String { .. }
474 | Expr::MagicConst { .. }
475 | Expr::VariadicPlaceholder { .. }
476 | Expr::Error { .. } => {}
477 Expr::Die { expr: None, .. } | Expr::Exit { expr: None, .. } => {}
478 }
479}
480
481pub fn walk_arg<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, arg: &'ast Arg<'ast>) {
482 visitor.visit_expr(arg.value);
483}
484
485pub fn walk_array_item<'ast, V: Visitor<'ast> + ?Sized>(
486 visitor: &mut V,
487 item: &'ast ArrayItem<'ast>,
488) {
489 if let Some(key) = item.key {
490 visitor.visit_expr(key);
491 }
492 visitor.visit_expr(item.value);
493}
494
495pub fn walk_param<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, param: &'ast Param<'ast>) {
496 walk_attributes(visitor, param.attributes);
497 if let Some(ty) = param.ty {
498 visitor.visit_type(ty);
499 }
500 if let Some(default) = param.default {
501 visitor.visit_expr(default);
502 }
503 if let Some(hooks) = param.hooks {
504 walk_property_hooks(visitor, hooks);
505 }
506}
507
508pub fn walk_static_var<'ast, V: Visitor<'ast> + ?Sized>(
509 visitor: &mut V,
510 var: &'ast StaticVar<'ast>,
511) {
512 visitor.visit_expr(var.var);
513 if let Some(default) = var.default {
514 visitor.visit_expr(default);
515 }
516}
517
518pub fn walk_match_arm<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, arm: &'ast MatchArm<'ast>) {
519 if let Some(conditions) = arm.conditions {
520 walk_exprs(visitor, conditions);
521 }
522 visitor.visit_expr(arm.body);
523}
524
525pub fn walk_closure_use<'ast, V: Visitor<'ast> + ?Sized>(
526 _: &mut V,
527 closure_use: &'ast ClosureUse<'ast>,
528) {
529 let _ = closure_use;
530}
531
532pub fn walk_name<'ast, V: Visitor<'ast> + ?Sized>(_: &mut V, name: &Name<'ast>) {
533 let _ = name;
534}
535
536pub fn walk_type<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, ty: &'ast Type<'ast>) {
537 match ty {
538 Type::Simple(_) => {}
539 Type::Name(name) => visitor.visit_name(name),
540 Type::Union(types) | Type::Intersection(types) => walk_types(visitor, types),
541 Type::Nullable(inner) => visitor.visit_type(inner),
542 }
543}
544
545pub fn walk_attribute_group<'ast, V: Visitor<'ast> + ?Sized>(
546 visitor: &mut V,
547 group: &'ast AttributeGroup<'ast>,
548) {
549 for attribute in group.attributes {
550 visitor.visit_attribute(attribute);
551 }
552}
553
554pub fn walk_attribute<'ast, V: Visitor<'ast> + ?Sized>(
555 visitor: &mut V,
556 attribute: &'ast Attribute<'ast>,
557) {
558 visitor.visit_name(&attribute.name);
559 for arg in attribute.args {
560 visitor.visit_arg(arg);
561 }
562}
563
564pub fn walk_use_item<'ast, V: Visitor<'ast> + ?Sized>(
565 visitor: &mut V,
566 use_item: &'ast UseItem<'ast>,
567) {
568 visitor.visit_name(&use_item.name);
569}
570
571pub fn walk_case<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, case: &'ast Case<'ast>) {
572 if let Some(condition) = case.condition {
573 visitor.visit_expr(condition);
574 }
575 walk_statements(visitor, case.body);
576}
577
578pub fn walk_catch<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, catch: &'ast Catch<'ast>) {
579 walk_names(visitor, catch.types);
580 walk_statements(visitor, catch.body);
581}
582
583pub fn walk_class_member<'ast, V: Visitor<'ast> + ?Sized>(
584 visitor: &mut V,
585 member: &'ast ClassMember<'ast>,
586) {
587 match member {
588 ClassMember::Property {
589 attributes,
590 ty,
591 entries,
592 ..
593 } => {
594 walk_attributes(visitor, attributes);
595 if let Some(ty) = ty {
596 visitor.visit_type(ty);
597 }
598 for entry in entries.iter() {
599 visitor.visit_property_entry(entry);
600 }
601 }
602 ClassMember::PropertyHook {
603 attributes,
604 ty,
605 default,
606 hooks,
607 ..
608 } => {
609 walk_attributes(visitor, attributes);
610 if let Some(ty) = ty {
611 visitor.visit_type(ty);
612 }
613 if let Some(default) = default {
614 visitor.visit_expr(default);
615 }
616 walk_property_hooks(visitor, hooks);
617 }
618 ClassMember::Method {
619 attributes,
620 params,
621 return_type,
622 body,
623 ..
624 } => {
625 walk_attributes(visitor, attributes);
626 walk_params(visitor, params);
627 if let Some(return_type) = return_type {
628 visitor.visit_type(return_type);
629 }
630 walk_statements(visitor, body);
631 }
632 ClassMember::Const {
633 attributes,
634 ty,
635 consts,
636 ..
637 } => {
638 walk_attributes(visitor, attributes);
639 if let Some(ty) = ty {
640 visitor.visit_type(ty);
641 }
642 for class_const in consts.iter() {
643 visitor.visit_class_const(class_const);
644 }
645 }
646 ClassMember::TraitUse {
647 attributes,
648 traits,
649 adaptations,
650 ..
651 } => {
652 walk_attributes(visitor, attributes);
653 walk_names(visitor, traits);
654 for adaptation in adaptations.iter() {
655 visitor.visit_trait_adaptation(adaptation);
656 }
657 }
658 ClassMember::Case {
659 attributes, value, ..
660 } => {
661 walk_attributes(visitor, attributes);
662 if let Some(value) = value {
663 visitor.visit_expr(value);
664 }
665 }
666 }
667}
668
669pub fn walk_class_const<'ast, V: Visitor<'ast> + ?Sized>(
670 visitor: &mut V,
671 class_const: &'ast ClassConst<'ast>,
672) {
673 visitor.visit_expr(class_const.value);
674}
675
676pub fn walk_declare_item<'ast, V: Visitor<'ast> + ?Sized>(
677 visitor: &mut V,
678 item: &'ast DeclareItem<'ast>,
679) {
680 visitor.visit_expr(item.value);
681}
682
683pub fn walk_trait_adaptation<'ast, V: Visitor<'ast> + ?Sized>(
684 visitor: &mut V,
685 adaptation: &'ast TraitAdaptation<'ast>,
686) {
687 match adaptation {
688 TraitAdaptation::Precedence {
689 method, insteadof, ..
690 } => {
691 visitor.visit_trait_method_ref(method);
692 walk_names(visitor, insteadof);
693 }
694 TraitAdaptation::Alias { method, .. } => {
695 visitor.visit_trait_method_ref(method);
696 }
697 }
698}
699
700pub fn walk_trait_method_ref<'ast, V: Visitor<'ast> + ?Sized>(
701 visitor: &mut V,
702 method_ref: &'ast TraitMethodRef<'ast>,
703) {
704 if let Some(trait_name) = method_ref.trait_name {
705 visitor.visit_name(&trait_name);
706 }
707}
708
709pub fn walk_property_entry<'ast, V: Visitor<'ast> + ?Sized>(
710 visitor: &mut V,
711 entry: &'ast PropertyEntry<'ast>,
712) {
713 if let Some(default) = entry.default {
714 visitor.visit_expr(default);
715 }
716}
717
718pub fn walk_property_hook<'ast, V: Visitor<'ast> + ?Sized>(
719 visitor: &mut V,
720 hook: &'ast PropertyHook<'ast>,
721) {
722 walk_attributes(visitor, hook.attributes);
723 walk_params(visitor, hook.params);
724 visitor.visit_property_hook_body(&hook.body);
725}
726
727pub fn walk_property_hook_body<'ast, V: Visitor<'ast> + ?Sized>(
728 visitor: &mut V,
729 body: &'ast PropertyHookBody<'ast>,
730) {
731 match body {
732 PropertyHookBody::None => {}
733 PropertyHookBody::Statements(statements) => walk_statements(visitor, statements),
734 PropertyHookBody::Expr(expr) => visitor.visit_expr(expr),
735 }
736}
737
738fn walk_statements<'ast, V: Visitor<'ast> + ?Sized>(
739 visitor: &mut V,
740 statements: &'ast [StmtId<'ast>],
741) {
742 for stmt in statements.iter().copied() {
743 visitor.visit_stmt(stmt);
744 }
745}
746
747fn walk_exprs<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, exprs: &'ast [ExprId<'ast>]) {
748 for expr in exprs.iter().copied() {
749 visitor.visit_expr(expr);
750 }
751}
752
753fn walk_attributes<'ast, V: Visitor<'ast> + ?Sized>(
754 visitor: &mut V,
755 attributes: &'ast [AttributeGroup<'ast>],
756) {
757 for group in attributes {
758 visitor.visit_attribute_group(group);
759 }
760}
761
762fn walk_params<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, params: &'ast [Param<'ast>]) {
763 for param in params {
764 visitor.visit_param(param);
765 }
766}
767
768fn walk_types<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, types: &'ast [Type<'ast>]) {
769 for ty in types {
770 visitor.visit_type(ty);
771 }
772}
773
774fn walk_names<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, names: &'ast [Name<'ast>]) {
775 for name in names {
776 visitor.visit_name(name);
777 }
778}
779
780fn walk_property_hooks<'ast, V: Visitor<'ast> + ?Sized>(
781 visitor: &mut V,
782 hooks: &'ast [PropertyHook<'ast>],
783) {
784 for hook in hooks {
785 visitor.visit_property_hook(hook);
786 }
787}
788
789fn walk_class_members<'ast, V: Visitor<'ast> + ?Sized>(
790 visitor: &mut V,
791 members: &'ast [ClassMember<'ast>],
792) {
793 for member in members {
794 visitor.visit_class_member(member);
795 }
796}