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