mago_syntax/utils/
control_flow.rs1use 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}