mago_syntax/utils/
reference.rs

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