Skip to main content

mago_syntax/utils/
control_flow.rs

1use mago_span::HasSpan;
2use mago_span::Span;
3
4use crate::cst::Access;
5use crate::cst::Array;
6use crate::cst::ArrayElement;
7use crate::cst::Block;
8use crate::cst::Break;
9use crate::cst::Call;
10use crate::cst::ClassLikeConstantSelector;
11use crate::cst::ClassLikeMemberSelector;
12use crate::cst::Construct;
13use crate::cst::Continue;
14use crate::cst::Expression;
15use crate::cst::ForBody;
16use crate::cst::ForeachBody;
17use crate::cst::ForeachTarget;
18use crate::cst::IfBody;
19use crate::cst::LegacyArray;
20use crate::cst::List;
21use crate::cst::Literal;
22use crate::cst::LiteralInteger;
23use crate::cst::MatchArm;
24use crate::cst::PartialApplication;
25use crate::cst::Return;
26use crate::cst::Statement;
27use crate::cst::StringPart;
28use crate::cst::SwitchBody;
29use crate::cst::SwitchCase;
30use crate::cst::Throw;
31use crate::cst::Variable;
32use crate::cst::WhileBody;
33use crate::cst::Yield;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum ControlFlow<'arena> {
37    Return(&'arena Return<'arena>),
38    Throw(&'arena Throw<'arena>),
39    Continue(&'arena Continue<'arena>),
40    Break(&'arena 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<'arena>(block: &'arena Block<'arena>) -> Vec<ControlFlow<'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<'arena>(statement: &'arena Statement<'arena>) -> Vec<ControlFlow<'arena>> {
69    let mut controls = vec![];
70
71    match statement {
72        Statement::Namespace(namespace) => {
73            for statement in namespace.statements() {
74                controls.extend(find_control_flows_in_statement(statement));
75            }
76        }
77        Statement::Block(block) => {
78            controls.extend(find_control_flows_in_block(block));
79        }
80        Statement::Try(r#try) => {
81            controls.extend(find_control_flows_in_block(&r#try.block));
82
83            for catch in &r#try.catch_clauses {
84                controls.extend(find_control_flows_in_block(&catch.block));
85            }
86
87            if let Some(finally) = &r#try.finally_clause {
88                controls.extend(find_control_flows_in_block(&finally.block));
89            }
90        }
91        Statement::Foreach(foreach) => {
92            controls.extend(find_control_flows_in_expression(foreach.expression));
93            match &foreach.target {
94                ForeachTarget::Value(foreach_value_target) => {
95                    controls.extend(find_control_flows_in_expression(foreach_value_target.value));
96                }
97                ForeachTarget::KeyValue(foreach_key_value_target) => {
98                    controls.extend(find_control_flows_in_expression(foreach_key_value_target.key));
99                    controls.extend(find_control_flows_in_expression(foreach_key_value_target.value));
100                }
101            }
102
103            match &foreach.body {
104                ForeachBody::Statement(statement) => {
105                    controls.extend(find_control_flows_in_statement(statement));
106                }
107                ForeachBody::ColonDelimited(foreach_colon_delimited_body) => {
108                    for statement in &foreach_colon_delimited_body.statements {
109                        controls.extend(find_control_flows_in_statement(statement));
110                    }
111                }
112            }
113        }
114        Statement::For(r#for) => {
115            for initialization in &r#for.initializations {
116                controls.extend(find_control_flows_in_expression(initialization));
117            }
118
119            for condition in &r#for.conditions {
120                controls.extend(find_control_flows_in_expression(condition));
121            }
122
123            for increment in &r#for.increments {
124                controls.extend(find_control_flows_in_expression(increment));
125            }
126
127            match &r#for.body {
128                ForBody::Statement(statement) => {
129                    controls.extend(find_control_flows_in_statement(statement));
130                }
131                ForBody::ColonDelimited(foreach_colon_delimited_body) => {
132                    for statement in &foreach_colon_delimited_body.statements {
133                        controls.extend(find_control_flows_in_statement(statement));
134                    }
135                }
136            }
137        }
138        Statement::While(r#while) => {
139            controls.extend(find_control_flows_in_expression(r#while.condition));
140
141            match &r#while.body {
142                WhileBody::Statement(statement) => {
143                    controls.extend(find_control_flows_in_statement(statement));
144                }
145                WhileBody::ColonDelimited(foreach_colon_delimited_body) => {
146                    for statement in &foreach_colon_delimited_body.statements {
147                        controls.extend(find_control_flows_in_statement(statement));
148                    }
149                }
150            }
151        }
152        Statement::DoWhile(do_while) => {
153            controls.extend(find_control_flows_in_expression(do_while.condition));
154            controls.extend(find_control_flows_in_statement(do_while.statement));
155        }
156        Statement::Switch(switch) => {
157            controls.extend(find_control_flows_in_expression(switch.expression));
158
159            let cases = match &switch.body {
160                SwitchBody::BraceDelimited(switch_brace_delimited_body) => &switch_brace_delimited_body.cases,
161                SwitchBody::ColonDelimited(switch_colon_delimited_body) => &switch_colon_delimited_body.cases,
162            };
163
164            let mut switch_controls = vec![];
165            for case in cases {
166                match &case {
167                    SwitchCase::Expression(switch_expression_case) => {
168                        switch_controls.extend(find_control_flows_in_expression(switch_expression_case.expression));
169
170                        for statement in &switch_expression_case.statements {
171                            switch_controls.extend(find_control_flows_in_statement(statement));
172                        }
173                    }
174                    SwitchCase::Default(switch_default_case) => {
175                        for statement in &switch_default_case.statements {
176                            switch_controls.extend(find_control_flows_in_statement(statement));
177                        }
178                    }
179                }
180            }
181
182            for control in switch_controls {
183                match control {
184                    ControlFlow::Break(r#break) => {
185                        if !matches!(
186                            r#break.level,
187                            Some(Expression::Literal(Literal::Integer(LiteralInteger { value: Some(1), .. }))) | None
188                        ) {
189                            controls.push(control);
190                        }
191                    }
192                    _ => controls.push(control),
193                }
194            }
195        }
196        Statement::If(r#if) => {
197            controls.extend(find_control_flows_in_expression(r#if.condition));
198
199            match &r#if.body {
200                IfBody::Statement(if_statement_body) => {
201                    controls.extend(find_control_flows_in_statement(if_statement_body.statement));
202
203                    for else_if in &if_statement_body.else_if_clauses {
204                        controls.extend(find_control_flows_in_expression(else_if.condition));
205                        controls.extend(find_control_flows_in_statement(else_if.statement));
206                    }
207
208                    if let Some(else_clause) = &if_statement_body.else_clause {
209                        controls.extend(find_control_flows_in_statement(else_clause.statement));
210                    }
211                }
212                IfBody::ColonDelimited(if_colon_delimited_body) => {
213                    for statement in &if_colon_delimited_body.statements {
214                        controls.extend(find_control_flows_in_statement(statement));
215                    }
216
217                    for else_if in &if_colon_delimited_body.else_if_clauses {
218                        controls.extend(find_control_flows_in_expression(else_if.condition));
219                        for statement in &else_if.statements {
220                            controls.extend(find_control_flows_in_statement(statement));
221                        }
222                    }
223
224                    if let Some(else_clause) = &if_colon_delimited_body.else_clause {
225                        for statement in &else_clause.statements {
226                            controls.extend(find_control_flows_in_statement(statement));
227                        }
228                    }
229                }
230            }
231        }
232        Statement::Return(r#return) => {
233            controls.push(ControlFlow::Return(r#return));
234            if let Some(value) = &r#return.value {
235                controls.extend(find_control_flows_in_expression(value));
236            }
237        }
238        Statement::Continue(r#continue) => {
239            controls.push(ControlFlow::Continue(r#continue));
240            if let Some(level) = &r#continue.level {
241                controls.extend(find_control_flows_in_expression(level));
242            }
243        }
244        Statement::Break(r#break) => {
245            controls.push(ControlFlow::Break(r#break));
246            if let Some(level) = &r#break.level {
247                controls.extend(find_control_flows_in_expression(level));
248            }
249        }
250        Statement::Expression(expression_statement) => {
251            controls.extend(find_control_flows_in_expression(expression_statement.expression));
252        }
253        Statement::Echo(echo) => {
254            for expression in &echo.values {
255                controls.extend(find_control_flows_in_expression(expression));
256            }
257        }
258        Statement::Unset(unset) => {
259            for value in &unset.values {
260                controls.extend(find_control_flows_in_expression(value));
261            }
262        }
263        _ => {}
264    }
265
266    controls
267}
268
269#[inline]
270#[must_use]
271pub fn find_control_flows_in_expression<'arena>(expression: &'arena Expression<'arena>) -> Vec<ControlFlow<'arena>> {
272    let mut controls = vec![];
273
274    match expression {
275        Expression::Binary(binary) => {
276            controls.extend(find_control_flows_in_expression(binary.lhs));
277            controls.extend(find_control_flows_in_expression(binary.rhs));
278        }
279        Expression::UnaryPrefix(unary_prefix) => {
280            controls.extend(find_control_flows_in_expression(unary_prefix.operand));
281        }
282        Expression::UnaryPostfix(unary_postfix) => {
283            controls.extend(find_control_flows_in_expression(unary_postfix.operand));
284        }
285        Expression::Parenthesized(parenthesized) => {
286            controls.extend(find_control_flows_in_expression(parenthesized.expression));
287        }
288        Expression::CompositeString(composite_string) => {
289            for part in composite_string.parts() {
290                match part {
291                    StringPart::Expression(expression) => {
292                        controls.extend(find_control_flows_in_expression(expression));
293                    }
294                    StringPart::BracedExpression(braced_expression_string_part) => {
295                        controls.extend(find_control_flows_in_expression(braced_expression_string_part.expression));
296                    }
297                    StringPart::Literal(_) => {}
298                }
299            }
300        }
301        Expression::Assignment(assignment) => {
302            controls.extend(find_control_flows_in_expression(assignment.lhs));
303            controls.extend(find_control_flows_in_expression(assignment.rhs));
304        }
305        Expression::Conditional(conditional) => {
306            controls.extend(find_control_flows_in_expression(conditional.condition));
307            if let Some(then) = &conditional.then {
308                controls.extend(find_control_flows_in_expression(then));
309            }
310
311            controls.extend(find_control_flows_in_expression(conditional.r#else));
312        }
313        Expression::Array(Array { elements, .. })
314        | Expression::LegacyArray(LegacyArray { elements, .. })
315        | Expression::List(List { elements, .. }) => {
316            for element in elements {
317                match element {
318                    ArrayElement::KeyValue(key_value_array_element) => {
319                        controls.extend(find_control_flows_in_expression(key_value_array_element.key));
320                        controls.extend(find_control_flows_in_expression(key_value_array_element.value));
321                    }
322                    ArrayElement::Value(value_array_element) => {
323                        controls.extend(find_control_flows_in_expression(value_array_element.value));
324                    }
325                    ArrayElement::Variadic(variadic_array_element) => {
326                        controls.extend(find_control_flows_in_expression(variadic_array_element.value));
327                    }
328                    ArrayElement::Missing(_) => {}
329                }
330            }
331        }
332        Expression::ArrayAccess(array_access) => {
333            controls.extend(find_control_flows_in_expression(array_access.array));
334            controls.extend(find_control_flows_in_expression(array_access.index));
335        }
336        Expression::ArrayAppend(array_append) => {
337            controls.extend(find_control_flows_in_expression(array_append.array));
338        }
339        Expression::AnonymousClass(anonymous_class) => {
340            if let Some(arguments) = &anonymous_class.argument_list {
341                for argument in &arguments.arguments {
342                    let Some(value) = argument.value() else {
343                        continue;
344                    };
345
346                    controls.extend(find_control_flows_in_expression(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<'arena>(variable: &'arena Variable<'arena>) -> Vec<ControlFlow<'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}