mago_syntax/utils/
control_flow.rs

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