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::IndirectVariable { name, .. } => {
478 visitor.visit_expr(name);
479 }
480 Expr::Die { expr: None, .. } | Expr::Exit { expr: None, .. } => {}
481 }
482}
483
484pub fn walk_arg<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, arg: &'ast Arg<'ast>) {
485 visitor.visit_expr(arg.value);
486}
487
488pub fn walk_array_item<'ast, V: Visitor<'ast> + ?Sized>(
489 visitor: &mut V,
490 item: &'ast ArrayItem<'ast>,
491) {
492 if let Some(key) = item.key {
493 visitor.visit_expr(key);
494 }
495 visitor.visit_expr(item.value);
496}
497
498pub fn walk_param<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, param: &'ast Param<'ast>) {
499 walk_attributes(visitor, param.attributes);
500 if let Some(ty) = param.ty {
501 visitor.visit_type(ty);
502 }
503 if let Some(default) = param.default {
504 visitor.visit_expr(default);
505 }
506 if let Some(hooks) = param.hooks {
507 walk_property_hooks(visitor, hooks);
508 }
509}
510
511pub fn walk_static_var<'ast, V: Visitor<'ast> + ?Sized>(
512 visitor: &mut V,
513 var: &'ast StaticVar<'ast>,
514) {
515 visitor.visit_expr(var.var);
516 if let Some(default) = var.default {
517 visitor.visit_expr(default);
518 }
519}
520
521pub fn walk_match_arm<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, arm: &'ast MatchArm<'ast>) {
522 if let Some(conditions) = arm.conditions {
523 walk_exprs(visitor, conditions);
524 }
525 visitor.visit_expr(arm.body);
526}
527
528pub fn walk_closure_use<'ast, V: Visitor<'ast> + ?Sized>(
529 _: &mut V,
530 closure_use: &'ast ClosureUse<'ast>,
531) {
532 let _ = closure_use;
533}
534
535pub fn walk_name<'ast, V: Visitor<'ast> + ?Sized>(_: &mut V, name: &Name<'ast>) {
536 let _ = name;
537}
538
539pub fn walk_type<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, ty: &'ast Type<'ast>) {
540 match ty {
541 Type::Simple(_) => {}
542 Type::Name(name) => visitor.visit_name(name),
543 Type::Union(types) | Type::Intersection(types) => walk_types(visitor, types),
544 Type::Nullable(inner) => visitor.visit_type(inner),
545 }
546}
547
548pub fn walk_attribute_group<'ast, V: Visitor<'ast> + ?Sized>(
549 visitor: &mut V,
550 group: &'ast AttributeGroup<'ast>,
551) {
552 for attribute in group.attributes {
553 visitor.visit_attribute(attribute);
554 }
555}
556
557pub fn walk_attribute<'ast, V: Visitor<'ast> + ?Sized>(
558 visitor: &mut V,
559 attribute: &'ast Attribute<'ast>,
560) {
561 visitor.visit_name(&attribute.name);
562 for arg in attribute.args {
563 visitor.visit_arg(arg);
564 }
565}
566
567pub fn walk_use_item<'ast, V: Visitor<'ast> + ?Sized>(
568 visitor: &mut V,
569 use_item: &'ast UseItem<'ast>,
570) {
571 visitor.visit_name(&use_item.name);
572}
573
574pub fn walk_case<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, case: &'ast Case<'ast>) {
575 if let Some(condition) = case.condition {
576 visitor.visit_expr(condition);
577 }
578 walk_statements(visitor, case.body);
579}
580
581pub fn walk_catch<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, catch: &'ast Catch<'ast>) {
582 walk_names(visitor, catch.types);
583 walk_statements(visitor, catch.body);
584}
585
586pub fn walk_class_member<'ast, V: Visitor<'ast> + ?Sized>(
587 visitor: &mut V,
588 member: &'ast ClassMember<'ast>,
589) {
590 match member {
591 ClassMember::Property {
592 attributes,
593 ty,
594 entries,
595 ..
596 } => {
597 walk_attributes(visitor, attributes);
598 if let Some(ty) = ty {
599 visitor.visit_type(ty);
600 }
601 for entry in entries.iter() {
602 visitor.visit_property_entry(entry);
603 }
604 }
605 ClassMember::PropertyHook {
606 attributes,
607 ty,
608 default,
609 hooks,
610 ..
611 } => {
612 walk_attributes(visitor, attributes);
613 if let Some(ty) = ty {
614 visitor.visit_type(ty);
615 }
616 if let Some(default) = default {
617 visitor.visit_expr(default);
618 }
619 walk_property_hooks(visitor, hooks);
620 }
621 ClassMember::Method {
622 attributes,
623 params,
624 return_type,
625 body,
626 ..
627 } => {
628 walk_attributes(visitor, attributes);
629 walk_params(visitor, params);
630 if let Some(return_type) = return_type {
631 visitor.visit_type(return_type);
632 }
633 walk_statements(visitor, body);
634 }
635 ClassMember::Const {
636 attributes,
637 ty,
638 consts,
639 ..
640 } => {
641 walk_attributes(visitor, attributes);
642 if let Some(ty) = ty {
643 visitor.visit_type(ty);
644 }
645 for class_const in consts.iter() {
646 visitor.visit_class_const(class_const);
647 }
648 }
649 ClassMember::TraitUse {
650 attributes,
651 traits,
652 adaptations,
653 ..
654 } => {
655 walk_attributes(visitor, attributes);
656 walk_names(visitor, traits);
657 for adaptation in adaptations.iter() {
658 visitor.visit_trait_adaptation(adaptation);
659 }
660 }
661 ClassMember::Case {
662 attributes, value, ..
663 } => {
664 walk_attributes(visitor, attributes);
665 if let Some(value) = value {
666 visitor.visit_expr(value);
667 }
668 }
669 }
670}
671
672pub fn walk_class_const<'ast, V: Visitor<'ast> + ?Sized>(
673 visitor: &mut V,
674 class_const: &'ast ClassConst<'ast>,
675) {
676 visitor.visit_expr(class_const.value);
677}
678
679pub fn walk_declare_item<'ast, V: Visitor<'ast> + ?Sized>(
680 visitor: &mut V,
681 item: &'ast DeclareItem<'ast>,
682) {
683 visitor.visit_expr(item.value);
684}
685
686pub fn walk_trait_adaptation<'ast, V: Visitor<'ast> + ?Sized>(
687 visitor: &mut V,
688 adaptation: &'ast TraitAdaptation<'ast>,
689) {
690 match adaptation {
691 TraitAdaptation::Precedence {
692 method, insteadof, ..
693 } => {
694 visitor.visit_trait_method_ref(method);
695 walk_names(visitor, insteadof);
696 }
697 TraitAdaptation::Alias { method, .. } => {
698 visitor.visit_trait_method_ref(method);
699 }
700 }
701}
702
703pub fn walk_trait_method_ref<'ast, V: Visitor<'ast> + ?Sized>(
704 visitor: &mut V,
705 method_ref: &'ast TraitMethodRef<'ast>,
706) {
707 if let Some(trait_name) = method_ref.trait_name {
708 visitor.visit_name(&trait_name);
709 }
710}
711
712pub fn walk_property_entry<'ast, V: Visitor<'ast> + ?Sized>(
713 visitor: &mut V,
714 entry: &'ast PropertyEntry<'ast>,
715) {
716 if let Some(default) = entry.default {
717 visitor.visit_expr(default);
718 }
719}
720
721pub fn walk_property_hook<'ast, V: Visitor<'ast> + ?Sized>(
722 visitor: &mut V,
723 hook: &'ast PropertyHook<'ast>,
724) {
725 walk_attributes(visitor, hook.attributes);
726 walk_params(visitor, hook.params);
727 visitor.visit_property_hook_body(&hook.body);
728}
729
730pub fn walk_property_hook_body<'ast, V: Visitor<'ast> + ?Sized>(
731 visitor: &mut V,
732 body: &'ast PropertyHookBody<'ast>,
733) {
734 match body {
735 PropertyHookBody::None => {}
736 PropertyHookBody::Statements(statements) => walk_statements(visitor, statements),
737 PropertyHookBody::Expr(expr) => visitor.visit_expr(expr),
738 }
739}
740
741fn walk_statements<'ast, V: Visitor<'ast> + ?Sized>(
742 visitor: &mut V,
743 statements: &'ast [StmtId<'ast>],
744) {
745 for stmt in statements.iter().copied() {
746 visitor.visit_stmt(stmt);
747 }
748}
749
750fn walk_exprs<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, exprs: &'ast [ExprId<'ast>]) {
751 for expr in exprs.iter().copied() {
752 visitor.visit_expr(expr);
753 }
754}
755
756fn walk_attributes<'ast, V: Visitor<'ast> + ?Sized>(
757 visitor: &mut V,
758 attributes: &'ast [AttributeGroup<'ast>],
759) {
760 for group in attributes {
761 visitor.visit_attribute_group(group);
762 }
763}
764
765fn walk_params<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, params: &'ast [Param<'ast>]) {
766 for param in params {
767 visitor.visit_param(param);
768 }
769}
770
771fn walk_types<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, types: &'ast [Type<'ast>]) {
772 for ty in types {
773 visitor.visit_type(ty);
774 }
775}
776
777fn walk_names<'ast, V: Visitor<'ast> + ?Sized>(visitor: &mut V, names: &'ast [Name<'ast>]) {
778 for name in names {
779 visitor.visit_name(name);
780 }
781}
782
783fn walk_property_hooks<'ast, V: Visitor<'ast> + ?Sized>(
784 visitor: &mut V,
785 hooks: &'ast [PropertyHook<'ast>],
786) {
787 for hook in hooks {
788 visitor.visit_property_hook(hook);
789 }
790}
791
792fn walk_class_members<'ast, V: Visitor<'ast> + ?Sized>(
793 visitor: &mut V,
794 members: &'ast [ClassMember<'ast>],
795) {
796 for member in members {
797 visitor.visit_class_member(member);
798 }
799}