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