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