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.block.statements.iter().any(statement_has_yield)
93                || r#try.catch_clauses.iter().any(|catch| block_has_yield(&catch.block))
94            {
95                return true;
96            }
97
98            for catch in r#try.catch_clauses.iter() {
99                if block_has_yield(&catch.block) {
100                    return true;
101                }
102            }
103
104            if let Some(finally) = &r#try.finally_clause
105                && block_has_yield(&finally.block)
106            {
107                return true;
108            }
109
110            false
111        }
112        Statement::Foreach(foreach) => match &foreach.body {
113            ForeachBody::Statement(statement) => statement_has_yield(statement),
114            ForeachBody::ColonDelimited(foreach_colon_delimited_body) => {
115                foreach_colon_delimited_body.statements.iter().any(statement_has_yield)
116            }
117        },
118        Statement::For(r#for) => match &r#for.body {
119            ForBody::Statement(statement) => statement_has_yield(statement),
120            ForBody::ColonDelimited(foreach_colon_delimited_body) => {
121                foreach_colon_delimited_body.statements.iter().any(statement_has_yield)
122            }
123        },
124        Statement::While(r#while) => match &r#while.body {
125            WhileBody::Statement(statement) => statement_has_yield(statement),
126            WhileBody::ColonDelimited(foreach_colon_delimited_body) => {
127                foreach_colon_delimited_body.statements.iter().any(statement_has_yield)
128            }
129        },
130        Statement::DoWhile(do_while) => statement_has_yield(&do_while.statement),
131        Statement::Switch(switch) => {
132            let cases = match &switch.body {
133                SwitchBody::BraceDelimited(switch_brace_delimited_body) => &switch_brace_delimited_body.cases,
134                SwitchBody::ColonDelimited(switch_colon_delimited_body) => &switch_colon_delimited_body.cases,
135            };
136
137            for case in cases.iter() {
138                match &case {
139                    SwitchCase::Expression(switch_expression_case) => {
140                        for statement in switch_expression_case.statements.iter() {
141                            if statement_has_yield(statement) {
142                                return true;
143                            }
144                        }
145                    }
146                    SwitchCase::Default(switch_default_case) => {
147                        for statement in switch_default_case.statements.iter() {
148                            if statement_has_yield(statement) {
149                                return true;
150                            }
151                        }
152                    }
153                }
154            }
155
156            false
157        }
158        Statement::If(r#if) => match &r#if.body {
159            IfBody::Statement(if_statement_body) => {
160                if statement_has_yield(&if_statement_body.statement) {
161                    return true;
162                }
163
164                for else_if in if_statement_body.else_if_clauses.iter() {
165                    if statement_has_yield(&else_if.statement) {
166                        return true;
167                    }
168                }
169
170                if let Some(else_clause) = &if_statement_body.else_clause
171                    && statement_has_yield(&else_clause.statement)
172                {
173                    return true;
174                }
175
176                false
177            }
178            IfBody::ColonDelimited(if_colon_delimited_body) => {
179                if if_colon_delimited_body.statements.iter().any(statement_has_yield) {
180                    return true;
181                }
182
183                for else_if in if_colon_delimited_body.else_if_clauses.iter() {
184                    if else_if.statements.iter().any(statement_has_yield) {
185                        return true;
186                    }
187                }
188
189                if let Some(else_clause) = &if_colon_delimited_body.else_clause
190                    && else_clause.statements.iter().any(statement_has_yield)
191                {
192                    return true;
193                }
194
195                false
196            }
197        },
198        Statement::Expression(expression) => expression_has_yield(&expression.expression),
199        _ => false,
200    }
201}
202
203#[inline]
204pub fn expression_has_yield(expression: &Expression) -> bool {
205    match &expression {
206        Expression::Parenthesized(parenthesized) => expression_has_yield(&parenthesized.expression),
207        Expression::Literal(_) => false,
208        Expression::CompositeString(_) => false,
209        Expression::Binary(operation) => expression_has_yield(&operation.lhs) || expression_has_yield(&operation.rhs),
210        Expression::UnaryPrefix(operation) => expression_has_yield(&operation.operand),
211        Expression::UnaryPostfix(operation) => expression_has_yield(&operation.operand),
212        Expression::Assignment(assignment_operation) => {
213            expression_has_yield(&assignment_operation.lhs) || expression_has_yield(&assignment_operation.rhs)
214        }
215        Expression::Conditional(conditional) => {
216            expression_has_yield(&conditional.condition)
217                || conditional.then.as_ref().map(|e| expression_has_yield(e.as_ref())).unwrap_or(false)
218                || expression_has_yield(&conditional.r#else)
219        }
220        Expression::Array(array) => array.elements.iter().any(|element| match element {
221            ArrayElement::KeyValue(key_value_array_element) => {
222                expression_has_yield(&key_value_array_element.key)
223                    || expression_has_yield(&key_value_array_element.value)
224            }
225            ArrayElement::Value(value_array_element) => expression_has_yield(&value_array_element.value),
226            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(&variadic_array_element.value),
227            _ => false,
228        }),
229        Expression::LegacyArray(legacy_array) => legacy_array.elements.iter().any(|element| match element {
230            ArrayElement::KeyValue(key_value_array_element) => {
231                expression_has_yield(&key_value_array_element.key)
232                    || expression_has_yield(&key_value_array_element.value)
233            }
234            ArrayElement::Value(value_array_element) => expression_has_yield(&value_array_element.value),
235            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(&variadic_array_element.value),
236            _ => false,
237        }),
238        Expression::List(list) => list.elements.iter().any(|element| match element {
239            ArrayElement::KeyValue(key_value_array_element) => {
240                expression_has_yield(&key_value_array_element.key)
241                    || expression_has_yield(&key_value_array_element.value)
242            }
243            ArrayElement::Value(value_array_element) => expression_has_yield(&value_array_element.value),
244            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(&variadic_array_element.value),
245            _ => false,
246        }),
247        Expression::ArrayAccess(array_access) => {
248            expression_has_yield(&array_access.array) || expression_has_yield(&array_access.index)
249        }
250        Expression::ArrayAppend(array_append) => expression_has_yield(&array_append.array),
251        Expression::Match(r#match) => {
252            expression_has_yield(&r#match.expression)
253                || r#match.arms.iter().any(|arm| match arm {
254                    MatchArm::Expression(match_expression_arm) => {
255                        match_expression_arm.conditions.iter().any(expression_has_yield)
256                            || expression_has_yield(&match_expression_arm.expression)
257                    }
258                    MatchArm::Default(match_default_arm) => expression_has_yield(&match_default_arm.expression),
259                })
260        }
261        Expression::Construct(construct) => match construct {
262            Construct::Isset(isset_construct) => isset_construct.values.iter().any(expression_has_yield),
263            Construct::Empty(empty_construct) => expression_has_yield(&empty_construct.value),
264            Construct::Eval(eval_construct) => expression_has_yield(&eval_construct.value),
265            Construct::Include(include_construct) => expression_has_yield(&include_construct.value),
266            Construct::IncludeOnce(include_once_construct) => expression_has_yield(&include_once_construct.value),
267            Construct::Require(require_construct) => expression_has_yield(&require_construct.value),
268            Construct::RequireOnce(require_once_construct) => expression_has_yield(&require_once_construct.value),
269            Construct::Print(print_construct) => expression_has_yield(&print_construct.value),
270            Construct::Exit(exit_construct) => exit_construct
271                .arguments
272                .as_ref()
273                .map(|arguments| {
274                    arguments.arguments.iter().any(|argument| match argument {
275                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
276                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
277                    })
278                })
279                .unwrap_or(false),
280            Construct::Die(die_construct) => die_construct
281                .arguments
282                .as_ref()
283                .map(|arguments| {
284                    arguments.arguments.iter().any(|argument| match argument {
285                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
286                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
287                    })
288                })
289                .unwrap_or(false),
290        },
291        Expression::Throw(throw) => expression_has_yield(&throw.exception),
292        Expression::Clone(clone) => expression_has_yield(&clone.object),
293        Expression::Call(call) => match call {
294            Call::Function(function_call) => {
295                expression_has_yield(&function_call.function)
296                    || function_call.argument_list.arguments.iter().any(|argument| match argument {
297                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
298                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
299                    })
300            }
301            Call::Method(method_call) => {
302                expression_has_yield(&method_call.object)
303                    || matches!(&method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
304                    || method_call.argument_list.arguments.iter().any(|argument| match argument {
305                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
306                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
307                    })
308            }
309            Call::NullSafeMethod(null_safe_method_call) => {
310                expression_has_yield(&null_safe_method_call.object)
311                    || matches!(&null_safe_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
312                    || null_safe_method_call.argument_list.arguments.iter().any(|argument| match argument {
313                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
314                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
315                    })
316            }
317            Call::StaticMethod(static_method_call) => {
318                expression_has_yield(&static_method_call.class)
319                    || matches!(&static_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
320                    || static_method_call.argument_list.arguments.iter().any(|argument| match argument {
321                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
322                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
323                    })
324            }
325        },
326        Expression::Access(access) => match access {
327            Access::Property(property_access) => {
328                expression_has_yield(&property_access.object)
329                    || matches!(&property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
330            }
331            Access::NullSafeProperty(null_safe_property_access) => {
332                expression_has_yield(&null_safe_property_access.object)
333                    || matches!(&null_safe_property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
334            }
335            Access::StaticProperty(static_property_access) => expression_has_yield(&static_property_access.class),
336            Access::ClassConstant(class_constant_access) => {
337                expression_has_yield(&class_constant_access.class)
338                    || matches!(&class_constant_access.constant, ClassLikeConstantSelector::Expression(selector) if expression_has_yield(&selector.expression))
339            }
340        },
341        Expression::ClosureCreation(closure_creation) => match closure_creation {
342            ClosureCreation::Function(function_closure_creation) => {
343                expression_has_yield(&function_closure_creation.function)
344            }
345            ClosureCreation::Method(method_closure_creation) => {
346                expression_has_yield(&method_closure_creation.object)
347                    || matches!(&method_closure_creation.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
348            }
349            ClosureCreation::StaticMethod(static_method_closure_creation) => {
350                expression_has_yield(&static_method_closure_creation.class)
351                    || matches!(&static_method_closure_creation.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(&selector.expression))
352            }
353        },
354        Expression::Instantiation(instantiation) => {
355            expression_has_yield(&instantiation.class)
356                || instantiation
357                    .argument_list
358                    .as_ref()
359                    .map(|arguments| {
360                        arguments.arguments.iter().any(|argument| match argument {
361                            Argument::Positional(positional_argument) => {
362                                expression_has_yield(&positional_argument.value)
363                            }
364                            Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
365                        })
366                    })
367                    .unwrap_or(false)
368        }
369        Expression::Yield(_) => true,
370        _ => false,
371    }
372}