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