mago_ast_utils/
reference.rs

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}