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}