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    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}