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<'ast, 'arena>(block: &'ast Block<'arena>) -> Vec<&'ast Return<'arena>> {
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<'ast, 'arena>(statement: &'ast Statement<'arena>) -> Vec<&'ast Return<'arena>> {
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<'ast, 'arena>(block: &'ast Block<'arena>) -> 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<'ast, 'arena>(statement: &'ast Statement<'arena>) -> 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<'ast, 'arena>(expression: &'ast Expression<'arena>) -> 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)).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) || expression_has_yield(key_value_array_element.value)
223            }
224            ArrayElement::Value(value_array_element) => expression_has_yield(value_array_element.value),
225            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(variadic_array_element.value),
226            _ => false,
227        }),
228        Expression::LegacyArray(legacy_array) => legacy_array.elements.iter().any(|element| match element {
229            ArrayElement::KeyValue(key_value_array_element) => {
230                expression_has_yield(key_value_array_element.key) || 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) || expression_has_yield(key_value_array_element.value)
239            }
240            ArrayElement::Value(value_array_element) => expression_has_yield(value_array_element.value),
241            ArrayElement::Variadic(variadic_array_element) => expression_has_yield(variadic_array_element.value),
242            _ => false,
243        }),
244        Expression::ArrayAccess(array_access) => {
245            expression_has_yield(array_access.array) || expression_has_yield(array_access.index)
246        }
247        Expression::ArrayAppend(array_append) => expression_has_yield(array_append.array),
248        Expression::Match(r#match) => {
249            expression_has_yield(r#match.expression)
250                || r#match.arms.iter().any(|arm| match arm {
251                    MatchArm::Expression(match_expression_arm) => {
252                        match_expression_arm.conditions.iter().any(expression_has_yield)
253                            || expression_has_yield(match_expression_arm.expression)
254                    }
255                    MatchArm::Default(match_default_arm) => expression_has_yield(match_default_arm.expression),
256                })
257        }
258        Expression::Construct(construct) => match construct {
259            Construct::Isset(isset_construct) => isset_construct.values.iter().any(expression_has_yield),
260            Construct::Empty(empty_construct) => expression_has_yield(empty_construct.value),
261            Construct::Eval(eval_construct) => expression_has_yield(eval_construct.value),
262            Construct::Include(include_construct) => expression_has_yield(include_construct.value),
263            Construct::IncludeOnce(include_once_construct) => expression_has_yield(include_once_construct.value),
264            Construct::Require(require_construct) => expression_has_yield(require_construct.value),
265            Construct::RequireOnce(require_once_construct) => expression_has_yield(require_once_construct.value),
266            Construct::Print(print_construct) => expression_has_yield(print_construct.value),
267            Construct::Exit(exit_construct) => exit_construct
268                .arguments
269                .as_ref()
270                .map(|arguments| {
271                    arguments.arguments.iter().any(|argument| match argument {
272                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
273                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
274                    })
275                })
276                .unwrap_or(false),
277            Construct::Die(die_construct) => die_construct
278                .arguments
279                .as_ref()
280                .map(|arguments| {
281                    arguments.arguments.iter().any(|argument| match argument {
282                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
283                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
284                    })
285                })
286                .unwrap_or(false),
287        },
288        Expression::Throw(throw) => expression_has_yield(throw.exception),
289        Expression::Clone(clone) => expression_has_yield(clone.object),
290        Expression::Call(call) => match call {
291            Call::Function(function_call) => {
292                expression_has_yield(function_call.function)
293                    || function_call.argument_list.arguments.iter().any(|argument| match argument {
294                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
295                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
296                    })
297            }
298            Call::Method(method_call) => {
299                expression_has_yield(method_call.object)
300                    || matches!(&method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
301                    || method_call.argument_list.arguments.iter().any(|argument| match argument {
302                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
303                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
304                    })
305            }
306            Call::NullSafeMethod(null_safe_method_call) => {
307                expression_has_yield(null_safe_method_call.object)
308                    || matches!(&null_safe_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
309                    || null_safe_method_call.argument_list.arguments.iter().any(|argument| match argument {
310                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
311                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
312                    })
313            }
314            Call::StaticMethod(static_method_call) => {
315                expression_has_yield(static_method_call.class)
316                    || matches!(&static_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
317                    || static_method_call.argument_list.arguments.iter().any(|argument| match argument {
318                        Argument::Positional(positional_argument) => expression_has_yield(&positional_argument.value),
319                        Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
320                    })
321            }
322        },
323        Expression::Access(access) => match access {
324            Access::Property(property_access) => {
325                expression_has_yield(property_access.object)
326                    || matches!(&property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
327            }
328            Access::NullSafeProperty(null_safe_property_access) => {
329                expression_has_yield(null_safe_property_access.object)
330                    || matches!(&null_safe_property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
331            }
332            Access::StaticProperty(static_property_access) => expression_has_yield(static_property_access.class),
333            Access::ClassConstant(class_constant_access) => {
334                expression_has_yield(class_constant_access.class)
335                    || matches!(&class_constant_access.constant, ClassLikeConstantSelector::Expression(selector) if expression_has_yield(selector.expression))
336            }
337        },
338        Expression::ClosureCreation(closure_creation) => match closure_creation {
339            ClosureCreation::Function(function_closure_creation) => {
340                expression_has_yield(function_closure_creation.function)
341            }
342            ClosureCreation::Method(method_closure_creation) => {
343                expression_has_yield(method_closure_creation.object)
344                    || matches!(&method_closure_creation.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
345            }
346            ClosureCreation::StaticMethod(static_method_closure_creation) => {
347                expression_has_yield(static_method_closure_creation.class)
348                    || matches!(&static_method_closure_creation.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
349            }
350        },
351        Expression::Instantiation(instantiation) => {
352            expression_has_yield(instantiation.class)
353                || instantiation
354                    .argument_list
355                    .as_ref()
356                    .map(|arguments| {
357                        arguments.arguments.iter().any(|argument| match argument {
358                            Argument::Positional(positional_argument) => {
359                                expression_has_yield(&positional_argument.value)
360                            }
361                            Argument::Named(named_argument) => expression_has_yield(&named_argument.value),
362                        })
363                    })
364                    .unwrap_or(false)
365        }
366        Expression::Yield(_) => true,
367        _ => false,
368    }
369}