Skip to main content

mago_syntax/utils/
reference.rs

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