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