mago_syntax/utils/
control_flow.rs

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