mago_syntax/utils/
mod.rs

1use crate::ast::*;
2use crate::utils::control_flow::ControlFlow;
3
4pub mod assignment;
5pub mod condition;
6pub mod control_flow;
7pub mod definition;
8pub mod reference;
9
10#[inline]
11pub fn find_returns_in_block(block: &Block) -> Vec<&Return> {
12    let mut returns = vec![];
13    for control_flow in control_flow::find_control_flows_in_block(block) {
14        if let ControlFlow::Return(r#return) = control_flow {
15            returns.push(r#return);
16        }
17    }
18
19    returns
20}
21
22#[inline]
23pub fn find_returns_in_statement(statement: &Statement) -> Vec<&Return> {
24    let mut returns = vec![];
25    for control_flow in control_flow::find_control_flows_in_statement(statement) {
26        if let ControlFlow::Return(r#return) = control_flow {
27            returns.push(r#return);
28        }
29    }
30
31    returns
32}
33
34#[inline]
35pub fn block_has_throws(block: &Block) -> bool {
36    for control_flow in control_flow::find_control_flows_in_block(block) {
37        if let ControlFlow::Throw(_) = control_flow {
38            return true;
39        }
40    }
41
42    false
43}
44
45#[inline]
46pub fn statement_has_throws(statement: &Statement) -> bool {
47    for control_flow in control_flow::find_control_flows_in_statement(statement) {
48        if let ControlFlow::Throw(_) = control_flow {
49            return true;
50        }
51    }
52
53    false
54}
55
56#[inline]
57pub fn expression_has_throws(expression: &Expression) -> bool {
58    for control_flow in control_flow::find_control_flows_in_expression(expression) {
59        if let ControlFlow::Throw(_) = control_flow {
60            return true;
61        }
62    }
63
64    false
65}
66
67#[inline]
68pub fn block_has_yield(block: &Block) -> bool {
69    for statement in block.statements.iter() {
70        if statement_has_yield(statement) {
71            return true;
72        }
73    }
74
75    false
76}
77
78#[inline]
79pub fn statement_has_yield(statement: &Statement) -> bool {
80    match statement {
81        Statement::Namespace(namespace) => {
82            for statement in namespace.statements().iter() {
83                if statement_has_yield(statement) {
84                    return true;
85                }
86            }
87
88            false
89        }
90        Statement::Block(block) => block_has_yield(block),
91        Statement::Try(r#try) => {
92            if r#try.catch_clauses.iter().any(|catch| block_has_yield(&catch.block)) {
93                return true;
94            }
95
96            for catch in r#try.catch_clauses.iter() {
97                if block_has_yield(&catch.block) {
98                    return true;
99                }
100            }
101
102            if let Some(finally) = &r#try.finally_clause
103                && block_has_yield(&finally.block)
104            {
105                return true;
106            }
107
108            false
109        }
110        Statement::Foreach(foreach) => match &foreach.body {
111            ForeachBody::Statement(statement) => statement_has_yield(statement),
112            ForeachBody::ColonDelimited(foreach_colon_delimited_body) => {
113                foreach_colon_delimited_body.statements.iter().any(statement_has_yield)
114            }
115        },
116        Statement::For(r#for) => match &r#for.body {
117            ForBody::Statement(statement) => statement_has_yield(statement),
118            ForBody::ColonDelimited(foreach_colon_delimited_body) => {
119                foreach_colon_delimited_body.statements.iter().any(statement_has_yield)
120            }
121        },
122        Statement::While(r#while) => match &r#while.body {
123            WhileBody::Statement(statement) => statement_has_yield(statement),
124            WhileBody::ColonDelimited(foreach_colon_delimited_body) => {
125                foreach_colon_delimited_body.statements.iter().any(statement_has_yield)
126            }
127        },
128        Statement::DoWhile(do_while) => statement_has_yield(&do_while.statement),
129        Statement::Switch(switch) => {
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            for case in cases.iter() {
136                match &case {
137                    SwitchCase::Expression(switch_expression_case) => {
138                        for statement in switch_expression_case.statements.iter() {
139                            if statement_has_yield(statement) {
140                                return true;
141                            }
142                        }
143                    }
144                    SwitchCase::Default(switch_default_case) => {
145                        for statement in switch_default_case.statements.iter() {
146                            if statement_has_yield(statement) {
147                                return true;
148                            }
149                        }
150                    }
151                }
152            }
153
154            false
155        }
156        Statement::If(r#if) => match &r#if.body {
157            IfBody::Statement(if_statement_body) => {
158                if statement_has_yield(&if_statement_body.statement) {
159                    return true;
160                }
161
162                for else_if in if_statement_body.else_if_clauses.iter() {
163                    if statement_has_yield(&else_if.statement) {
164                        return true;
165                    }
166                }
167
168                if let Some(else_clause) = &if_statement_body.else_clause
169                    && statement_has_yield(&else_clause.statement)
170                {
171                    return true;
172                }
173
174                false
175            }
176            IfBody::ColonDelimited(if_colon_delimited_body) => {
177                if if_colon_delimited_body.statements.iter().any(statement_has_yield) {
178                    return true;
179                }
180
181                for else_if in if_colon_delimited_body.else_if_clauses.iter() {
182                    if else_if.statements.iter().any(statement_has_yield) {
183                        return true;
184                    }
185                }
186
187                if let Some(else_clause) = &if_colon_delimited_body.else_clause
188                    && else_clause.statements.iter().any(statement_has_yield)
189                {
190                    return true;
191                }
192
193                false
194            }
195        },
196        Statement::Expression(expression) => expression_has_yield(&expression.expression),
197        _ => false,
198    }
199}
200
201#[inline]
202pub fn expression_has_yield(expression: &Expression) -> bool {
203    match &expression {
204        Expression::Parenthesized(parenthesized) => expression_has_yield(&parenthesized.expression),
205        Expression::Literal(_) => false,
206        Expression::CompositeString(_) => false,
207        Expression::Binary(operation) => expression_has_yield(&operation.lhs) || expression_has_yield(&operation.rhs),
208        Expression::UnaryPrefix(operation) => expression_has_yield(&operation.operand),
209        Expression::UnaryPostfix(operation) => expression_has_yield(&operation.operand),
210        Expression::Assignment(assignment_operation) => {
211            expression_has_yield(&assignment_operation.lhs) || expression_has_yield(&assignment_operation.rhs)
212        }
213        Expression::Conditional(conditional) => {
214            expression_has_yield(&conditional.condition)
215                || conditional.then.as_ref().map(|e| expression_has_yield(e.as_ref())).unwrap_or(false)
216                || expression_has_yield(&conditional.r#else)
217        }
218        Expression::Array(array) => array.elements.iter().any(|element| match element {
219            ArrayElement::KeyValue(key_value_array_element) => {
220                expression_has_yield(&key_value_array_element.key)
221                    || expression_has_yield(&key_value_array_element.value)
222            }
223            ArrayElement::Value(value_array_element) => expression_has_yield(&value_array_element.value),
224            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(&variadic_array_element.value),
225            _ => false,
226        }),
227        Expression::LegacyArray(legacy_array) => legacy_array.elements.iter().any(|element| match element {
228            ArrayElement::KeyValue(key_value_array_element) => {
229                expression_has_yield(&key_value_array_element.key)
230                    || expression_has_yield(&key_value_array_element.value)
231            }
232            ArrayElement::Value(value_array_element) => expression_has_yield(&value_array_element.value),
233            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(&variadic_array_element.value),
234            _ => false,
235        }),
236        Expression::List(list) => list.elements.iter().any(|element| match element {
237            ArrayElement::KeyValue(key_value_array_element) => {
238                expression_has_yield(&key_value_array_element.key)
239                    || expression_has_yield(&key_value_array_element.value)
240            }
241            ArrayElement::Value(value_array_element) => expression_has_yield(&value_array_element.value),
242            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(&variadic_array_element.value),
243            _ => false,
244        }),
245        Expression::ArrayAccess(array_access) => {
246            expression_has_yield(&array_access.array) || expression_has_yield(&array_access.index)
247        }
248        Expression::ArrayAppend(array_append) => expression_has_yield(&array_append.array),
249        Expression::Match(r#match) => {
250            expression_has_yield(&r#match.expression)
251                || r#match.arms.iter().any(|arm| match arm {
252                    MatchArm::Expression(match_expression_arm) => {
253                        match_expression_arm.conditions.iter().any(expression_has_yield)
254                            || expression_has_yield(&match_expression_arm.expression)
255                    }
256                    MatchArm::Default(match_default_arm) => expression_has_yield(&match_default_arm.expression),
257                })
258        }
259        Expression::Construct(construct) => match construct {
260            Construct::Isset(isset_construct) => isset_construct.values.iter().any(expression_has_yield),
261            Construct::Empty(empty_construct) => expression_has_yield(&empty_construct.value),
262            Construct::Eval(eval_construct) => expression_has_yield(&eval_construct.value),
263            Construct::Include(include_construct) => expression_has_yield(&include_construct.value),
264            Construct::IncludeOnce(include_once_construct) => expression_has_yield(&include_once_construct.value),
265            Construct::Require(require_construct) => expression_has_yield(&require_construct.value),
266            Construct::RequireOnce(require_once_construct) => expression_has_yield(&require_once_construct.value),
267            Construct::Print(print_construct) => expression_has_yield(&print_construct.value),
268            Construct::Exit(exit_construct) => exit_construct
269                .arguments
270                .as_ref()
271                .map(|arguments| {
272                    arguments.arguments.iter().any(|argument| match argument {
273                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
274                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
275                    })
276                })
277                .unwrap_or(false),
278            Construct::Die(die_construct) => die_construct
279                .arguments
280                .as_ref()
281                .map(|arguments| {
282                    arguments.arguments.iter().any(|argument| match argument {
283                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
284                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
285                    })
286                })
287                .unwrap_or(false),
288        },
289        Expression::Throw(throw) => expression_has_yield(&throw.exception),
290        Expression::Clone(clone) => expression_has_yield(&clone.object),
291        Expression::Call(call) => match call {
292            Call::Function(function_call) => {
293                expression_has_yield(&function_call.function)
294                    || function_call.argument_list.arguments.iter().any(|argument| match argument {
295                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
296                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
297                    })
298            }
299            Call::Method(method_call) => {
300                expression_has_yield(&method_call.object)
301                    || matches!(&method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
302                    || method_call.argument_list.arguments.iter().any(|argument| match argument {
303                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
304                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
305                    })
306            }
307            Call::NullSafeMethod(null_safe_method_call) => {
308                expression_has_yield(&null_safe_method_call.object)
309                    || matches!(&null_safe_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
310                    || null_safe_method_call.argument_list.arguments.iter().any(|argument| match argument {
311                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
312                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
313                    })
314            }
315            Call::StaticMethod(static_method_call) => {
316                expression_has_yield(&static_method_call.class)
317                    || matches!(&static_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
318                    || static_method_call.argument_list.arguments.iter().any(|argument| match argument {
319                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
320                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
321                    })
322            }
323        },
324        Expression::Access(access) => match access {
325            Access::Property(property_access) => {
326                expression_has_yield(&property_access.object)
327                    || matches!(&property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
328            }
329            Access::NullSafeProperty(null_safe_property_access) => {
330                expression_has_yield(&null_safe_property_access.object)
331                    || matches!(&null_safe_property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
332            }
333            Access::StaticProperty(static_property_access) => expression_has_yield(&static_property_access.class),
334            Access::ClassConstant(class_constant_access) => {
335                expression_has_yield(&class_constant_access.class)
336                    || matches!(&class_constant_access.constant, ClassLikeConstantSelector::Expression(selector) if expression_has_yield(&selector.expression))
337            }
338        },
339        Expression::ClosureCreation(closure_creation) => match closure_creation {
340            ClosureCreation::Function(function_closure_creation) => {
341                expression_has_yield(&function_closure_creation.function)
342            }
343            ClosureCreation::Method(method_closure_creation) => {
344                expression_has_yield(&method_closure_creation.object)
345                    || matches!(&method_closure_creation.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
346            }
347            ClosureCreation::StaticMethod(static_method_closure_creation) => {
348                expression_has_yield(&static_method_closure_creation.class)
349                    || matches!(&static_method_closure_creation.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
350            }
351        },
352        Expression::Instantiation(instantiation) => {
353            expression_has_yield(&instantiation.class)
354                || instantiation
355                    .argument_list
356                    .as_ref()
357                    .map(|arguments| {
358                        arguments.arguments.iter().any(|argument| match argument {
359                            Argument::Positional(positional_argument) => {
360                                expression_has_yield(&positional_argument.value)
361                            }
362                            Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
363                        })
364                    })
365                    .unwrap_or(false)
366        }
367        Expression::Yield(_) => true,
368        _ => false,
369    }
370}