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