mago_ast_utils/
control_flow.rs

1use mago_ast::*;
2use mago_span::HasSpan;
3use mago_span::Span;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ControlFlow<'a> {
7    Return(&'a Return),
8    Throw(&'a Throw),
9    Continue(&'a Continue),
10    Break(&'a Break),
11}
12
13impl HasSpan for ControlFlow<'_> {
14    fn span(&self) -> Span {
15        match self {
16            ControlFlow::Return(r#return) => r#return.span(),
17            ControlFlow::Throw(throw) => throw.span(),
18            ControlFlow::Continue(r#continue) => r#continue.span(),
19            ControlFlow::Break(r#break) => r#break.span(),
20        }
21    }
22}
23
24#[inline]
25pub fn find_control_flows_in_block(block: &Block) -> Vec<ControlFlow<'_>> {
26    let mut controls = vec![];
27
28    for statement in block.statements.iter() {
29        controls.extend(find_control_flows_in_statement(statement));
30    }
31
32    controls
33}
34
35#[inline]
36pub fn find_control_flows_in_statement(statement: &Statement) -> Vec<ControlFlow<'_>> {
37    let mut controls = vec![];
38
39    match statement {
40        Statement::Namespace(namespace) => {
41            for statement in namespace.statements().iter() {
42                controls.extend(find_control_flows_in_statement(statement));
43            }
44        }
45        Statement::Block(block) => {
46            controls.extend(find_control_flows_in_block(block));
47        }
48        Statement::Try(r#try) => {
49            controls.extend(find_control_flows_in_block(&r#try.block));
50
51            for catch in r#try.catch_clauses.iter() {
52                controls.extend(find_control_flows_in_block(&catch.block));
53            }
54
55            if let Some(finally) = &r#try.finally_clause {
56                controls.extend(find_control_flows_in_block(&finally.block));
57            }
58        }
59        Statement::Foreach(foreach) => {
60            controls.extend(find_control_flows_in_expression(&foreach.expression));
61            match &foreach.target {
62                ForeachTarget::Value(foreach_value_target) => {
63                    controls.extend(find_control_flows_in_expression(&foreach_value_target.value));
64                }
65                ForeachTarget::KeyValue(foreach_key_value_target) => {
66                    controls.extend(find_control_flows_in_expression(&foreach_key_value_target.key));
67                    controls.extend(find_control_flows_in_expression(&foreach_key_value_target.value));
68                }
69            }
70
71            match &foreach.body {
72                ForeachBody::Statement(statement) => {
73                    controls.extend(find_control_flows_in_statement(statement));
74                }
75                ForeachBody::ColonDelimited(foreach_colon_delimited_body) => {
76                    for statement in foreach_colon_delimited_body.statements.iter() {
77                        controls.extend(find_control_flows_in_statement(statement));
78                    }
79                }
80            }
81        }
82        Statement::For(r#for) => {
83            for initialization in r#for.initializations.iter() {
84                controls.extend(find_control_flows_in_expression(initialization));
85            }
86
87            for condition in r#for.conditions.iter() {
88                controls.extend(find_control_flows_in_expression(condition));
89            }
90
91            for increment in r#for.increments.iter() {
92                controls.extend(find_control_flows_in_expression(increment));
93            }
94
95            match &r#for.body {
96                ForBody::Statement(statement) => {
97                    controls.extend(find_control_flows_in_statement(statement));
98                }
99                ForBody::ColonDelimited(foreach_colon_delimited_body) => {
100                    for statement in foreach_colon_delimited_body.statements.iter() {
101                        controls.extend(find_control_flows_in_statement(statement));
102                    }
103                }
104            }
105        }
106        Statement::While(r#while) => {
107            controls.extend(find_control_flows_in_expression(&r#while.condition));
108
109            match &r#while.body {
110                WhileBody::Statement(statement) => {
111                    controls.extend(find_control_flows_in_statement(statement));
112                }
113                WhileBody::ColonDelimited(foreach_colon_delimited_body) => {
114                    for statement in foreach_colon_delimited_body.statements.iter() {
115                        controls.extend(find_control_flows_in_statement(statement));
116                    }
117                }
118            }
119        }
120        Statement::DoWhile(do_while) => {
121            controls.extend(find_control_flows_in_expression(&do_while.condition));
122            controls.extend(find_control_flows_in_statement(&do_while.statement));
123        }
124        Statement::Switch(switch) => {
125            controls.extend(find_control_flows_in_expression(&switch.expression));
126
127            let cases = match &switch.body {
128                SwitchBody::BraceDelimited(switch_brace_delimited_body) => &switch_brace_delimited_body.cases,
129                SwitchBody::ColonDelimited(switch_colon_delimited_body) => &switch_colon_delimited_body.cases,
130            };
131
132            let mut switch_controls = vec![];
133            for case in cases.iter() {
134                match &case {
135                    SwitchCase::Expression(switch_expression_case) => {
136                        switch_controls.extend(find_control_flows_in_expression(&switch_expression_case.expression));
137
138                        for statement in switch_expression_case.statements.iter() {
139                            switch_controls.extend(find_control_flows_in_statement(statement));
140                        }
141                    }
142                    SwitchCase::Default(switch_default_case) => {
143                        for statement in switch_default_case.statements.iter() {
144                            switch_controls.extend(find_control_flows_in_statement(statement));
145                        }
146                    }
147                }
148            }
149
150            for control in switch_controls {
151                match control {
152                    ControlFlow::Break(r#break) => {
153                        if !matches!(
154                            r#break.level,
155                            Some(Expression::Literal(Literal::Integer(LiteralInteger { value: Some(1), .. }))) | None
156                        ) {
157                            controls.push(control)
158                        }
159                    }
160                    _ => controls.push(control),
161                }
162            }
163        }
164        Statement::If(r#if) => {
165            controls.extend(find_control_flows_in_expression(&r#if.condition));
166
167            match &r#if.body {
168                IfBody::Statement(if_statement_body) => {
169                    controls.extend(find_control_flows_in_statement(&if_statement_body.statement));
170
171                    for else_if in if_statement_body.else_if_clauses.iter() {
172                        controls.extend(find_control_flows_in_expression(&else_if.condition));
173                        controls.extend(find_control_flows_in_statement(&else_if.statement));
174                    }
175
176                    if let Some(else_clause) = &if_statement_body.else_clause {
177                        controls.extend(find_control_flows_in_statement(&else_clause.statement));
178                    }
179                }
180                IfBody::ColonDelimited(if_colon_delimited_body) => {
181                    for statement in if_colon_delimited_body.statements.iter() {
182                        controls.extend(find_control_flows_in_statement(statement));
183                    }
184
185                    for else_if in if_colon_delimited_body.else_if_clauses.iter() {
186                        controls.extend(find_control_flows_in_expression(&else_if.condition));
187                        for statement in else_if.statements.iter() {
188                            controls.extend(find_control_flows_in_statement(statement));
189                        }
190                    }
191
192                    if let Some(else_clause) = &if_colon_delimited_body.else_clause {
193                        for statement in else_clause.statements.iter() {
194                            controls.extend(find_control_flows_in_statement(statement));
195                        }
196                    }
197                }
198            }
199        }
200        Statement::Return(r#return) => {
201            controls.push(ControlFlow::Return(r#return));
202            if let Some(value) = &r#return.value {
203                controls.extend(find_control_flows_in_expression(value));
204            }
205        }
206        Statement::Continue(r#continue) => {
207            controls.push(ControlFlow::Continue(r#continue));
208            if let Some(level) = &r#continue.level {
209                controls.extend(find_control_flows_in_expression(level));
210            }
211        }
212        Statement::Break(r#break) => {
213            controls.push(ControlFlow::Break(r#break));
214            if let Some(level) = &r#break.level {
215                controls.extend(find_control_flows_in_expression(level));
216            }
217        }
218        Statement::Expression(expression_statement) => {
219            controls.extend(find_control_flows_in_expression(&expression_statement.expression));
220        }
221        Statement::Echo(echo) => {
222            for expression in echo.values.iter() {
223                controls.extend(find_control_flows_in_expression(expression));
224            }
225        }
226        Statement::Unset(unset) => {
227            for value in unset.values.iter() {
228                controls.extend(find_control_flows_in_expression(value));
229            }
230        }
231        _ => {}
232    }
233
234    controls
235}
236
237#[inline]
238pub fn find_control_flows_in_expression(expression: &Expression) -> Vec<ControlFlow<'_>> {
239    let mut controls = vec![];
240
241    match expression {
242        Expression::Binary(binary) => {
243            controls.extend(find_control_flows_in_expression(&binary.lhs));
244            controls.extend(find_control_flows_in_expression(&binary.rhs));
245        }
246        Expression::UnaryPrefix(unary_prefix) => {
247            controls.extend(find_control_flows_in_expression(&unary_prefix.operand));
248        }
249        Expression::UnaryPostfix(unary_postfix) => {
250            controls.extend(find_control_flows_in_expression(&unary_postfix.operand));
251        }
252        Expression::Parenthesized(parenthesized) => {
253            controls.extend(find_control_flows_in_expression(&parenthesized.expression));
254        }
255        Expression::CompositeString(composite_string) => {
256            for part in composite_string.parts().iter() {
257                match part {
258                    StringPart::Expression(expression) => {
259                        controls.extend(find_control_flows_in_expression(expression));
260                    }
261                    StringPart::BracedExpression(braced_expression_string_part) => {
262                        controls.extend(find_control_flows_in_expression(&braced_expression_string_part.expression));
263                    }
264                    _ => {}
265                }
266            }
267        }
268        Expression::Assignment(assignment) => {
269            controls.extend(find_control_flows_in_expression(&assignment.lhs));
270            controls.extend(find_control_flows_in_expression(&assignment.rhs));
271        }
272        Expression::Conditional(conditional) => {
273            controls.extend(find_control_flows_in_expression(&conditional.condition));
274            if let Some(then) = &conditional.then {
275                controls.extend(find_control_flows_in_expression(then));
276            }
277
278            controls.extend(find_control_flows_in_expression(&conditional.r#else));
279        }
280        Expression::Array(Array { elements, .. })
281        | Expression::LegacyArray(LegacyArray { elements, .. })
282        | Expression::List(List { elements, .. }) => {
283            for element in elements.iter() {
284                match element {
285                    ArrayElement::KeyValue(key_value_array_element) => {
286                        controls.extend(find_control_flows_in_expression(&key_value_array_element.key));
287                        controls.extend(find_control_flows_in_expression(&key_value_array_element.value));
288                    }
289                    ArrayElement::Value(value_array_element) => {
290                        controls.extend(find_control_flows_in_expression(&value_array_element.value));
291                    }
292                    ArrayElement::Variadic(variadic_array_element) => {
293                        controls.extend(find_control_flows_in_expression(&variadic_array_element.value));
294                    }
295                    _ => {}
296                }
297            }
298        }
299        Expression::ArrayAccess(array_access) => {
300            controls.extend(find_control_flows_in_expression(&array_access.array));
301            controls.extend(find_control_flows_in_expression(&array_access.index));
302        }
303        Expression::ArrayAppend(array_append) => {
304            controls.extend(find_control_flows_in_expression(&array_append.array));
305        }
306        Expression::AnonymousClass(anonymous_class) => {
307            if let Some(arguments) = &anonymous_class.arguments {
308                for argument in arguments.arguments.iter() {
309                    controls.extend(find_control_flows_in_expression(argument.value()));
310                }
311            }
312        }
313        Expression::Match(r#match) => {
314            controls.extend(find_control_flows_in_expression(&r#match.expression));
315            for arm in r#match.arms.iter() {
316                match arm {
317                    MatchArm::Expression(match_expression_arm) => {
318                        for condition in match_expression_arm.conditions.iter() {
319                            controls.extend(find_control_flows_in_expression(condition));
320                        }
321
322                        controls.extend(find_control_flows_in_expression(&match_expression_arm.expression));
323                    }
324                    MatchArm::Default(match_default_arm) => {
325                        controls.extend(find_control_flows_in_expression(&match_default_arm.expression));
326                    }
327                }
328            }
329        }
330        Expression::Yield(r#yield) => match r#yield {
331            Yield::Value(yield_value) => {
332                if let Some(value) = &yield_value.value {
333                    controls.extend(find_control_flows_in_expression(value));
334                }
335            }
336            Yield::Pair(yield_pair) => {
337                controls.extend(find_control_flows_in_expression(&yield_pair.key));
338                controls.extend(find_control_flows_in_expression(&yield_pair.value));
339            }
340            Yield::From(yield_from) => {
341                controls.extend(find_control_flows_in_expression(&yield_from.iterator));
342            }
343        },
344        Expression::Construct(construct) => match construct {
345            Construct::Isset(isset_construct) => {
346                for expression in isset_construct.values.iter() {
347                    controls.extend(find_control_flows_in_expression(expression));
348                }
349            }
350            Construct::Empty(empty_construct) => {
351                controls.extend(find_control_flows_in_expression(&empty_construct.value));
352            }
353            Construct::Eval(eval_construct) => {
354                controls.extend(find_control_flows_in_expression(&eval_construct.value));
355            }
356            Construct::Include(include_construct) => {
357                controls.extend(find_control_flows_in_expression(&include_construct.value));
358            }
359            Construct::IncludeOnce(include_once_construct) => {
360                controls.extend(find_control_flows_in_expression(&include_once_construct.value));
361            }
362            Construct::Require(require_construct) => {
363                controls.extend(find_control_flows_in_expression(&require_construct.value));
364            }
365            Construct::RequireOnce(require_once_construct) => {
366                controls.extend(find_control_flows_in_expression(&require_once_construct.value));
367            }
368            Construct::Print(print_construct) => {
369                controls.extend(find_control_flows_in_expression(&print_construct.value));
370            }
371            Construct::Exit(exit_construct) => {
372                if let Some(arguments) = &exit_construct.arguments {
373                    for argument in arguments.arguments.iter() {
374                        controls.extend(find_control_flows_in_expression(argument.value()));
375                    }
376                }
377            }
378            Construct::Die(die_construct) => {
379                if let Some(arguments) = &die_construct.arguments {
380                    for argument in arguments.arguments.iter() {
381                        controls.extend(find_control_flows_in_expression(argument.value()));
382                    }
383                }
384            }
385        },
386        Expression::Throw(throw) => {
387            controls.push(ControlFlow::Throw(throw));
388        }
389        Expression::Clone(clone) => {
390            controls.extend(find_control_flows_in_expression(&clone.object));
391        }
392        Expression::Call(call) => match call {
393            Call::Function(function_call) => {
394                controls.extend(find_control_flows_in_expression(&function_call.function));
395                for argument in function_call.argument_list.arguments.iter() {
396                    controls.extend(find_control_flows_in_expression(argument.value()));
397                }
398            }
399            Call::Method(method_call) => {
400                controls.extend(find_control_flows_in_expression(&method_call.object));
401                match &method_call.method {
402                    ClassLikeMemberSelector::Variable(variable) => {
403                        controls.extend(find_control_flows_in_variable(variable));
404                    }
405                    ClassLikeMemberSelector::Expression(class_like_member_expression_selector) => {
406                        controls.extend(find_control_flows_in_expression(
407                            &class_like_member_expression_selector.expression,
408                        ));
409                    }
410                    _ => {}
411                }
412
413                for argument in method_call.argument_list.arguments.iter() {
414                    controls.extend(find_control_flows_in_expression(argument.value()));
415                }
416            }
417            Call::NullSafeMethod(null_safe_method_call) => {
418                controls.extend(find_control_flows_in_expression(&null_safe_method_call.object));
419                match &null_safe_method_call.method {
420                    ClassLikeMemberSelector::Variable(variable) => {
421                        controls.extend(find_control_flows_in_variable(variable));
422                    }
423                    ClassLikeMemberSelector::Expression(class_like_member_expression_selector) => {
424                        controls.extend(find_control_flows_in_expression(
425                            &class_like_member_expression_selector.expression,
426                        ));
427                    }
428                    _ => {}
429                }
430
431                for argument in null_safe_method_call.argument_list.arguments.iter() {
432                    controls.extend(find_control_flows_in_expression(argument.value()));
433                }
434            }
435            Call::StaticMethod(static_method_call) => {
436                controls.extend(find_control_flows_in_expression(&static_method_call.class));
437                match &static_method_call.method {
438                    ClassLikeMemberSelector::Variable(variable) => {
439                        controls.extend(find_control_flows_in_variable(variable));
440                    }
441                    ClassLikeMemberSelector::Expression(class_like_member_expression_selector) => {
442                        controls.extend(find_control_flows_in_expression(
443                            &class_like_member_expression_selector.expression,
444                        ));
445                    }
446                    _ => {}
447                }
448
449                for argument in static_method_call.argument_list.arguments.iter() {
450                    controls.extend(find_control_flows_in_expression(argument.value()));
451                }
452            }
453        },
454        Expression::Access(access) => match access {
455            Access::Property(property_access) => {
456                controls.extend(find_control_flows_in_expression(&property_access.object));
457                match &property_access.property {
458                    ClassLikeMemberSelector::Variable(variable) => {
459                        controls.extend(find_control_flows_in_variable(variable));
460                    }
461                    ClassLikeMemberSelector::Expression(class_like_member_expression_selector) => {
462                        controls.extend(find_control_flows_in_expression(
463                            &class_like_member_expression_selector.expression,
464                        ));
465                    }
466                    _ => {}
467                }
468            }
469            Access::NullSafeProperty(null_safe_property_access) => {
470                controls.extend(find_control_flows_in_expression(&null_safe_property_access.object));
471                match &null_safe_property_access.property {
472                    ClassLikeMemberSelector::Variable(variable) => {
473                        controls.extend(find_control_flows_in_variable(variable));
474                    }
475                    ClassLikeMemberSelector::Expression(class_like_member_expression_selector) => {
476                        controls.extend(find_control_flows_in_expression(
477                            &class_like_member_expression_selector.expression,
478                        ));
479                    }
480                    _ => {}
481                }
482            }
483            Access::StaticProperty(static_property_access) => {
484                controls.extend(find_control_flows_in_expression(&static_property_access.class));
485                controls.extend(find_control_flows_in_variable(&static_property_access.property));
486            }
487            Access::ClassConstant(class_constant_access) => {
488                controls.extend(find_control_flows_in_expression(&class_constant_access.class));
489                if let ClassLikeConstantSelector::Expression(class_like_member_expression_selector) =
490                    &class_constant_access.constant
491                {
492                    controls
493                        .extend(find_control_flows_in_expression(&class_like_member_expression_selector.expression));
494                }
495            }
496        },
497        Expression::Variable(variable) => {
498            controls.extend(find_control_flows_in_variable(variable));
499        }
500        Expression::ClosureCreation(closure_creation) => match closure_creation {
501            ClosureCreation::Function(function_closure_creation) => {
502                controls.extend(find_control_flows_in_expression(&function_closure_creation.function));
503            }
504            ClosureCreation::Method(method_closure_creation) => {
505                controls.extend(find_control_flows_in_expression(&method_closure_creation.object));
506                match &method_closure_creation.method {
507                    ClassLikeMemberSelector::Variable(variable) => {
508                        controls.extend(find_control_flows_in_variable(variable));
509                    }
510                    ClassLikeMemberSelector::Expression(class_like_member_expression_selector) => {
511                        controls.extend(find_control_flows_in_expression(
512                            &class_like_member_expression_selector.expression,
513                        ));
514                    }
515                    _ => {}
516                }
517            }
518            ClosureCreation::StaticMethod(static_method_closure_creation) => {
519                controls.extend(find_control_flows_in_expression(&static_method_closure_creation.class));
520                match &static_method_closure_creation.method {
521                    ClassLikeMemberSelector::Variable(variable) => {
522                        controls.extend(find_control_flows_in_variable(variable));
523                    }
524                    ClassLikeMemberSelector::Expression(class_like_member_expression_selector) => {
525                        controls.extend(find_control_flows_in_expression(
526                            &class_like_member_expression_selector.expression,
527                        ));
528                    }
529                    _ => {}
530                }
531            }
532        },
533        Expression::Instantiation(instantiation) => {
534            controls.extend(find_control_flows_in_expression(&instantiation.class));
535            if let Some(arguments) = &instantiation.arguments {
536                for argument in arguments.arguments.iter() {
537                    controls.extend(find_control_flows_in_expression(argument.value()));
538                }
539            }
540        }
541        _ => {}
542    }
543
544    controls
545}
546
547fn find_control_flows_in_variable(variable: &Variable) -> Vec<ControlFlow<'_>> {
548    match variable {
549        Variable::Indirect(indirect_variable) => {
550            find_control_flows_in_expression(indirect_variable.expression.as_ref())
551        }
552        Variable::Nested(nested_variable) => find_control_flows_in_variable(nested_variable.variable.as_ref()),
553        Variable::Direct(_) => {
554            vec![]
555        }
556    }
557}