php_parser/ast/
visitor.rs

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}