mago_ast_utils/
lib.rs

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