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