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::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}