Skip to main content

mago_syntax/utils/
mod.rs

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