1use mago_span::*;
2
3use crate::ast::*;
4
5#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)]
6pub enum MethodReference<'ast, 'arena> {
7 MethodCall(&'ast MethodCall<'arena>),
8 StaticMethodCall(&'ast StaticMethodCall<'arena>),
9 MethodClosureCreation(&'ast MethodClosureCreation<'arena>),
10 StaticMethodClosureCreation(&'ast StaticMethodClosureCreation<'arena>),
11}
12
13impl<'ast, 'arena> MethodReference<'ast, 'arena> {
14 pub fn get_class_or_object(&self) -> &'ast Expression<'arena> {
15 match self {
16 MethodReference::MethodCall(call) => call.object,
17 MethodReference::StaticMethodCall(call) => call.class,
18 MethodReference::MethodClosureCreation(closure) => closure.object,
19 MethodReference::StaticMethodClosureCreation(closure) => closure.class,
20 }
21 }
22
23 pub fn get_selector(&self) -> &'ast ClassLikeMemberSelector<'arena> {
24 match self {
25 MethodReference::MethodCall(call) => &call.method,
26 MethodReference::StaticMethodCall(call) => &call.method,
27 MethodReference::MethodClosureCreation(closure) => &closure.method,
28 MethodReference::StaticMethodClosureCreation(closure) => &closure.method,
29 }
30 }
31}
32
33impl HasSpan for MethodReference<'_, '_> {
34 fn span(&self) -> Span {
35 match self {
36 MethodReference::MethodCall(call) => call.span(),
37 MethodReference::StaticMethodCall(call) => call.span(),
38 MethodReference::MethodClosureCreation(closure) => closure.span(),
39 MethodReference::StaticMethodClosureCreation(closure) => closure.span(),
40 }
41 }
42}
43
44pub fn find_method_references_in_block<'ast, 'arena, F>(
45 block: &'ast Block<'arena>,
46 predicate: &F,
47) -> Vec<MethodReference<'ast, 'arena>>
48where
49 F: Fn(&MethodReference<'ast, 'arena>) -> bool,
50{
51 let mut method_references = vec![];
52 for statement in block.statements.iter() {
53 method_references.extend(find_method_references_in_statement(statement, predicate));
54 }
55
56 method_references
57}
58
59pub fn find_method_references_in_statement<'ast, 'arena, F>(
60 statement: &'ast Statement<'arena>,
61 predicate: &F,
62) -> Vec<MethodReference<'ast, 'arena>>
63where
64 F: Fn(&MethodReference<'ast, 'arena>) -> bool,
65{
66 match statement {
67 Statement::Block(block) => {
68 let mut references = vec![];
69 for statement in block.statements.iter() {
70 references.extend(find_method_references_in_statement(statement, predicate));
71 }
72
73 references
74 }
75 Statement::Try(try_catch) => {
76 let mut references = vec![];
77 for statement in try_catch.block.statements.iter() {
78 references.extend(find_method_references_in_statement(statement, predicate));
79 }
80
81 for catch in try_catch.catch_clauses.iter() {
82 for statement in catch.block.statements.iter() {
83 references.extend(find_method_references_in_statement(statement, predicate));
84 }
85 }
86
87 if let Some(finally) = &try_catch.finally_clause {
88 for statement in finally.block.statements.iter() {
89 references.extend(find_method_references_in_statement(statement, predicate));
90 }
91 }
92
93 references
94 }
95 Statement::Foreach(foreach) => {
96 let mut references = vec![];
97
98 references.extend(find_method_references_in_expression(foreach.expression, predicate));
99
100 match &foreach.target {
101 ForeachTarget::Value(foreach_value_target) => {
102 references.extend(find_method_references_in_expression(foreach_value_target.value, predicate));
103 }
104 ForeachTarget::KeyValue(foreach_key_value_target) => {
105 references.extend(find_method_references_in_expression(foreach_key_value_target.key, predicate));
106 references.extend(find_method_references_in_expression(foreach_key_value_target.value, predicate));
107 }
108 }
109
110 match &foreach.body {
111 ForeachBody::Statement(statement) => {
112 references.extend(find_method_references_in_statement(statement, predicate));
113 }
114 ForeachBody::ColonDelimited(foreach_colon_delimited_body) => {
115 for statement in foreach_colon_delimited_body.statements.iter() {
116 references.extend(find_method_references_in_statement(statement, predicate));
117 }
118 }
119 }
120
121 references
122 }
123 Statement::For(for_loop) => {
124 let mut references = vec![];
125
126 for init in for_loop.initializations.iter() {
127 references.extend(find_method_references_in_expression(init, predicate));
128 }
129
130 for condition in for_loop.conditions.iter() {
131 references.extend(find_method_references_in_expression(condition, predicate));
132 }
133
134 for increment in for_loop.increments.iter() {
135 references.extend(find_method_references_in_expression(increment, predicate));
136 }
137
138 match &for_loop.body {
139 ForBody::Statement(statement) => {
140 references.extend(find_method_references_in_statement(statement, predicate));
141 }
142 ForBody::ColonDelimited(for_colon_delimited_body) => {
143 for statement in for_colon_delimited_body.statements.iter() {
144 references.extend(find_method_references_in_statement(statement, predicate));
145 }
146 }
147 }
148
149 references
150 }
151 Statement::While(while_loop) => {
152 let mut references = vec![];
153
154 references.extend(find_method_references_in_expression(while_loop.condition, predicate));
155
156 match &while_loop.body {
157 WhileBody::Statement(statement) => {
158 references.extend(find_method_references_in_statement(statement, predicate));
159 }
160 WhileBody::ColonDelimited(while_colon_delimited_body) => {
161 for statement in while_colon_delimited_body.statements.iter() {
162 references.extend(find_method_references_in_statement(statement, predicate));
163 }
164 }
165 }
166
167 references
168 }
169 Statement::DoWhile(do_while) => {
170 let mut references = vec![];
171
172 references.extend(find_method_references_in_expression(do_while.condition, predicate));
173 references.extend(find_method_references_in_statement(do_while.statement, predicate));
174
175 references
176 }
177 Statement::Switch(switch) => {
178 let mut references = find_method_references_in_expression(switch.expression, predicate);
179
180 for case in switch.body.cases() {
181 match case {
182 SwitchCase::Expression(expression_case) => {
183 references.extend(find_method_references_in_expression(expression_case.expression, predicate));
184
185 for statement in expression_case.statements.iter() {
186 references.extend(find_method_references_in_statement(statement, predicate));
187 }
188 }
189 SwitchCase::Default(default_case) => {
190 for statement in default_case.statements.iter() {
191 references.extend(find_method_references_in_statement(statement, predicate));
192 }
193 }
194 }
195 }
196
197 references
198 }
199 Statement::If(if_stmt) => {
200 let mut references = vec![];
201
202 references.extend(find_method_references_in_expression(if_stmt.condition, predicate));
203 match &if_stmt.body {
204 IfBody::Statement(if_stmt_body) => {
205 references.extend(find_method_references_in_statement(if_stmt_body.statement, predicate));
206 for else_if_clause in if_stmt_body.else_if_clauses.iter() {
207 references.extend(find_method_references_in_expression(else_if_clause.condition, predicate));
208 references.extend(find_method_references_in_statement(else_if_clause.statement, predicate));
209 }
210
211 if let Some(else_clause) = &if_stmt_body.else_clause {
212 references.extend(find_method_references_in_statement(else_clause.statement, predicate));
213 }
214 }
215 IfBody::ColonDelimited(if_colon_delimited_body) => {
216 for statement in if_colon_delimited_body.statements.iter() {
217 references.extend(find_method_references_in_statement(statement, predicate));
218 }
219
220 for else_if_clause in if_colon_delimited_body.else_if_clauses.iter() {
221 references.extend(find_method_references_in_expression(else_if_clause.condition, predicate));
222 for statement in else_if_clause.statements.iter() {
223 references.extend(find_method_references_in_statement(statement, predicate));
224 }
225 }
226
227 if let Some(else_clause) = &if_colon_delimited_body.else_clause {
228 for statement in else_clause.statements.iter() {
229 references.extend(find_method_references_in_statement(statement, predicate));
230 }
231 }
232 }
233 }
234
235 references
236 }
237 Statement::Return(r#return) => {
238 if let Some(expression) = &r#return.value {
239 find_method_references_in_expression(expression, predicate)
240 } else {
241 vec![]
242 }
243 }
244 Statement::Expression(expression_statement) => {
245 find_method_references_in_expression(expression_statement.expression, predicate)
246 }
247 Statement::Echo(echo) => {
248 let mut references = vec![];
249 for expression in echo.values.iter() {
250 references.extend(find_method_references_in_expression(expression, predicate));
251 }
252
253 references
254 }
255 _ => {
256 vec![]
257 }
258 }
259}
260
261pub fn find_method_references_in_expression<'ast, 'arena, F>(
262 expression: &'ast Expression<'arena>,
263 predicate: &F,
264) -> Vec<MethodReference<'ast, 'arena>>
265where
266 F: Fn(&MethodReference<'ast, 'arena>) -> bool,
267{
268 match expression {
269 Expression::Binary(binary) => {
270 let mut references = vec![];
271 references.extend(find_method_references_in_expression(binary.lhs, predicate));
272 references.extend(find_method_references_in_expression(binary.rhs, predicate));
273
274 references
275 }
276 Expression::UnaryPrefix(unary_prefix) => find_method_references_in_expression(unary_prefix.operand, predicate),
277 Expression::UnaryPostfix(unary_postfix) => {
278 find_method_references_in_expression(unary_postfix.operand, predicate)
279 }
280 Expression::Parenthesized(parenthesized) => {
281 find_method_references_in_expression(parenthesized.expression, predicate)
282 }
283 Expression::Assignment(assignment) => {
284 let mut references = vec![];
285 references.extend(find_method_references_in_expression(assignment.lhs, predicate));
286 references.extend(find_method_references_in_expression(assignment.rhs, predicate));
287
288 references
289 }
290 Expression::Conditional(conditional) => {
291 let mut references = vec![];
292 references.extend(find_method_references_in_expression(conditional.condition, predicate));
293 if let Some(then) = &conditional.then {
294 references.extend(find_method_references_in_expression(then, predicate));
295 }
296 references.extend(find_method_references_in_expression(conditional.r#else, predicate));
297
298 references
299 }
300 Expression::Array(Array { elements, .. })
301 | Expression::LegacyArray(LegacyArray { elements, .. })
302 | Expression::List(List { elements, .. }) => {
303 let mut references = vec![];
304 for element in elements.iter() {
305 match element {
306 ArrayElement::KeyValue(kv) => {
307 references.extend(find_method_references_in_expression(kv.key, predicate));
308 references.extend(find_method_references_in_expression(kv.value, predicate));
309 }
310 ArrayElement::Value(v) => {
311 references.extend(find_method_references_in_expression(v.value, predicate));
312 }
313 ArrayElement::Variadic(v) => {
314 references.extend(find_method_references_in_expression(v.value, predicate));
315 }
316 ArrayElement::Missing(_) => {}
317 }
318 }
319
320 references
321 }
322 Expression::ArrayAccess(array_access) => {
323 let mut references = vec![];
324 references.extend(find_method_references_in_expression(array_access.array, predicate));
325 references.extend(find_method_references_in_expression(array_access.index, predicate));
326
327 references
328 }
329 Expression::ArrayAppend(array_append) => find_method_references_in_expression(array_append.array, predicate),
330 Expression::AnonymousClass(anonymous_class) => {
331 if let Some(argument_list) = &anonymous_class.argument_list {
332 find_references_in_argument_list(argument_list, predicate)
333 } else {
334 vec![]
335 }
336 }
337 Expression::Match(r#match) => {
338 let mut references = vec![];
339 references.extend(find_method_references_in_expression(r#match.expression, predicate));
340
341 for arm in r#match.arms.iter() {
342 match arm {
343 MatchArm::Expression(match_expression_arm) => {
344 for condition in match_expression_arm.conditions.iter() {
345 references.extend(find_method_references_in_expression(condition, predicate));
346 }
347
348 references
349 .extend(find_method_references_in_expression(match_expression_arm.expression, predicate));
350 }
351 MatchArm::Default(match_default_arm) => {
352 references
353 .extend(find_method_references_in_expression(match_default_arm.expression, predicate));
354 }
355 }
356 }
357
358 references
359 }
360 Expression::Yield(r#yield) => match r#yield {
361 Yield::Value(yield_value) => match &yield_value.value {
362 Some(value) => find_method_references_in_expression(value, predicate),
363 None => vec![],
364 },
365 Yield::Pair(yield_pair) => {
366 let mut references = vec![];
367 references.extend(find_method_references_in_expression(yield_pair.key, predicate));
368 references.extend(find_method_references_in_expression(yield_pair.value, predicate));
369
370 references
371 }
372 Yield::From(yield_from) => find_method_references_in_expression(yield_from.iterator, predicate),
373 },
374 Expression::Throw(throw) => find_method_references_in_expression(throw.exception, predicate),
375 Expression::Clone(clone) => find_method_references_in_expression(clone.object, predicate),
376 Expression::Call(call) => match call {
377 Call::Function(function_call) => {
378 let mut references = vec![];
379
380 references.extend(find_method_references_in_expression(function_call.function, predicate));
381 references.extend(find_references_in_argument_list(&function_call.argument_list, predicate));
382 references
383 }
384 Call::Method(method_call) => {
385 let reference = MethodReference::MethodCall(method_call);
386 let mut references = if predicate(&reference) { vec![reference] } else { vec![] };
387
388 references.extend(find_method_references_in_expression(method_call.object, predicate));
389 references.extend(find_references_in_argument_list(&method_call.argument_list, predicate));
390 references
391 }
392 Call::NullSafeMethod(null_safe_method_call) => {
393 let mut references = vec![];
394
395 references.extend(find_method_references_in_expression(null_safe_method_call.object, predicate));
396 references.extend(find_references_in_argument_list(&null_safe_method_call.argument_list, predicate));
397 references
398 }
399 Call::StaticMethod(static_method_call) => {
400 let reference = MethodReference::StaticMethodCall(static_method_call);
401 let mut references = if predicate(&reference) { vec![reference] } else { vec![] };
402
403 references.extend(find_method_references_in_expression(static_method_call.class, predicate));
404 references.extend(find_references_in_argument_list(&static_method_call.argument_list, predicate));
405 references
406 }
407 },
408 Expression::ClosureCreation(closure_creation) => match closure_creation {
409 ClosureCreation::Method(method_closure_creation) => {
410 let reference = MethodReference::MethodClosureCreation(method_closure_creation);
411 let mut references = if predicate(&reference) { vec![reference] } else { vec![] };
412
413 references.extend(find_method_references_in_expression(method_closure_creation.object, predicate));
414 references
415 }
416 ClosureCreation::StaticMethod(static_method_closure_creation) => {
417 let reference = MethodReference::StaticMethodClosureCreation(static_method_closure_creation);
418 let mut references = if predicate(&reference) { vec![reference] } else { vec![] };
419
420 references
421 .extend(find_method_references_in_expression(static_method_closure_creation.class, predicate));
422 references
423 }
424 ClosureCreation::Function(_) => vec![],
425 },
426 Expression::Instantiation(instantiation) => {
427 if let Some(argument_list) = &instantiation.argument_list {
428 find_references_in_argument_list(argument_list, predicate)
429 } else {
430 vec![]
431 }
432 }
433 _ => {
434 vec![]
435 }
436 }
437}
438
439fn find_references_in_argument_list<'ast, 'arena, F>(
440 argument_list: &'ast ArgumentList<'arena>,
441 predicate: &F,
442) -> Vec<MethodReference<'ast, 'arena>>
443where
444 F: Fn(&MethodReference<'ast, 'arena>) -> bool,
445{
446 let mut references = vec![];
447 for argument in argument_list.arguments.iter() {
448 match argument {
449 Argument::Positional(positional_argument) => {
450 references.extend(find_method_references_in_expression(&positional_argument.value, predicate));
451 }
452 Argument::Named(named_argument) => {
453 references.extend(find_method_references_in_expression(&named_argument.value, predicate));
454 }
455 }
456 }
457
458 references
459}