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]
229#[must_use]
230pub fn expression_has_yield(expression: &Expression) -> bool {
231 match &expression {
232 Expression::Parenthesized(parenthesized) => expression_has_yield(parenthesized.expression),
233 Expression::Literal(_) => false,
234 Expression::CompositeString(_) => false,
235 Expression::Binary(operation) => expression_has_yield(operation.lhs) || expression_has_yield(operation.rhs),
236 Expression::UnaryPrefix(operation) => expression_has_yield(operation.operand),
237 Expression::UnaryPostfix(operation) => expression_has_yield(operation.operand),
238 Expression::Assignment(assignment_operation) => {
239 expression_has_yield(assignment_operation.lhs) || expression_has_yield(assignment_operation.rhs)
240 }
241 Expression::Conditional(conditional) => {
242 expression_has_yield(conditional.condition)
243 || conditional.then.as_ref().is_some_and(|e| expression_has_yield(e))
244 || expression_has_yield(conditional.r#else)
245 }
246 Expression::Array(array) => array.elements.iter().any(|element| match element {
247 ArrayElement::KeyValue(key_value_array_element) => {
248 expression_has_yield(key_value_array_element.key) || expression_has_yield(key_value_array_element.value)
249 }
250 ArrayElement::Value(value_array_element) => expression_has_yield(value_array_element.value),
251 ArrayElement::Variadic(variadic_array_element) => expression_has_yield(variadic_array_element.value),
252 ArrayElement::Missing(_) => false,
253 }),
254 Expression::LegacyArray(legacy_array) => legacy_array.elements.iter().any(|element| match element {
255 ArrayElement::KeyValue(key_value_array_element) => {
256 expression_has_yield(key_value_array_element.key) || expression_has_yield(key_value_array_element.value)
257 }
258 ArrayElement::Value(value_array_element) => expression_has_yield(value_array_element.value),
259 ArrayElement::Variadic(variadic_array_element) => expression_has_yield(variadic_array_element.value),
260 ArrayElement::Missing(_) => false,
261 }),
262 Expression::List(list) => list.elements.iter().any(|element| match element {
263 ArrayElement::KeyValue(key_value_array_element) => {
264 expression_has_yield(key_value_array_element.key) || expression_has_yield(key_value_array_element.value)
265 }
266 ArrayElement::Value(value_array_element) => expression_has_yield(value_array_element.value),
267 ArrayElement::Variadic(variadic_array_element) => expression_has_yield(variadic_array_element.value),
268 ArrayElement::Missing(_) => false,
269 }),
270 Expression::ArrayAccess(array_access) => {
271 expression_has_yield(array_access.array) || expression_has_yield(array_access.index)
272 }
273 Expression::ArrayAppend(array_append) => expression_has_yield(array_append.array),
274 Expression::Match(r#match) => {
275 expression_has_yield(r#match.expression)
276 || r#match.arms.iter().any(|arm| match arm {
277 MatchArm::Expression(match_expression_arm) => {
278 match_expression_arm.conditions.iter().any(|e| expression_has_yield(e))
279 || expression_has_yield(match_expression_arm.expression)
280 }
281 MatchArm::Default(match_default_arm) => expression_has_yield(match_default_arm.expression),
282 })
283 }
284 Expression::Construct(construct) => match construct {
285 Construct::Isset(isset_construct) => isset_construct.values.iter().any(|e| expression_has_yield(e)),
286 Construct::Empty(empty_construct) => expression_has_yield(empty_construct.value),
287 Construct::Eval(eval_construct) => expression_has_yield(eval_construct.value),
288 Construct::Include(include_construct) => expression_has_yield(include_construct.value),
289 Construct::IncludeOnce(include_once_construct) => expression_has_yield(include_once_construct.value),
290 Construct::Require(require_construct) => expression_has_yield(require_construct.value),
291 Construct::RequireOnce(require_once_construct) => expression_has_yield(require_once_construct.value),
292 Construct::Print(print_construct) => expression_has_yield(print_construct.value),
293 Construct::Exit(exit_construct) => exit_construct.arguments.as_ref().is_some_and(|arguments| {
294 arguments.arguments.iter().any(|argument| match argument {
295 Argument::Positional(positional_argument) => expression_has_yield(positional_argument.value),
296 Argument::Named(named_argument) => expression_has_yield(named_argument.value),
297 })
298 }),
299 Construct::Die(die_construct) => die_construct.arguments.as_ref().is_some_and(|arguments| {
300 arguments.arguments.iter().any(|argument| match argument {
301 Argument::Positional(positional_argument) => expression_has_yield(positional_argument.value),
302 Argument::Named(named_argument) => expression_has_yield(named_argument.value),
303 })
304 }),
305 },
306 Expression::Throw(throw) => expression_has_yield(throw.exception),
307 Expression::Clone(clone) => expression_has_yield(clone.object),
308 Expression::Call(call) => match call {
309 Call::Function(function_call) => {
310 expression_has_yield(function_call.function)
311 || function_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::Method(method_call) => {
317 expression_has_yield(method_call.object)
318 || matches!(&method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
319 || 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 Call::NullSafeMethod(null_safe_method_call) => {
325 expression_has_yield(null_safe_method_call.object)
326 || matches!(&null_safe_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
327 || null_safe_method_call.argument_list.arguments.iter().any(|argument| match argument {
328 Argument::Positional(positional_argument) => expression_has_yield(positional_argument.value),
329 Argument::Named(named_argument) => expression_has_yield(named_argument.value),
330 })
331 }
332 Call::StaticMethod(static_method_call) => {
333 expression_has_yield(static_method_call.class)
334 || matches!(&static_method_call.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
335 || static_method_call.argument_list.arguments.iter().any(|argument| match argument {
336 Argument::Positional(positional_argument) => expression_has_yield(positional_argument.value),
337 Argument::Named(named_argument) => expression_has_yield(named_argument.value),
338 })
339 }
340 },
341 Expression::Access(access) => match access {
342 Access::Property(property_access) => {
343 expression_has_yield(property_access.object)
344 || matches!(&property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
345 }
346 Access::NullSafeProperty(null_safe_property_access) => {
347 expression_has_yield(null_safe_property_access.object)
348 || matches!(&null_safe_property_access.property, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
349 }
350 Access::StaticProperty(static_property_access) => expression_has_yield(static_property_access.class),
351 Access::ClassConstant(class_constant_access) => {
352 expression_has_yield(class_constant_access.class)
353 || matches!(&class_constant_access.constant, ClassLikeConstantSelector::Expression(selector) if expression_has_yield(selector.expression))
354 }
355 },
356 Expression::PartialApplication(partial_application) => match partial_application {
357 PartialApplication::Function(function_partial_application) => {
358 expression_has_yield(function_partial_application.function)
359 }
360 PartialApplication::Method(method_partial_application) => {
361 expression_has_yield(method_partial_application.object)
362 || matches!(&method_partial_application.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
363 }
364 PartialApplication::StaticMethod(static_method_partial_application) => {
365 expression_has_yield(static_method_partial_application.class)
366 || matches!(&static_method_partial_application.method, ClassLikeMemberSelector::Expression(selector) if expression_has_yield(selector.expression))
367 }
368 },
369 Expression::Instantiation(instantiation) => {
370 expression_has_yield(instantiation.class)
371 || instantiation.argument_list.as_ref().is_some_and(|arguments| {
372 arguments.arguments.iter().any(|argument| match argument {
373 Argument::Positional(positional_argument) => expression_has_yield(positional_argument.value),
374 Argument::Named(named_argument) => expression_has_yield(named_argument.value),
375 })
376 })
377 }
378 Expression::Yield(_) => true,
379 _ => false,
380 }
381}