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}