mago_syntax/utils/
reference.rs

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