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