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