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