lisette_semantics/
call_target.rs1use ecow::EcoString;
2
3use syntax::ast::Expression;
4use syntax::types::Type;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct CallTarget {
8 pub module: EcoString,
9 pub recv_type: Option<EcoString>,
10 pub fn_name: EcoString,
11}
12
13impl CallTarget {
14 pub fn is(&self, module: &str, fn_name: &str) -> bool {
15 self.recv_type.is_none() && self.module == module && self.fn_name == fn_name
16 }
17
18 pub fn is_method(&self, module: &str, recv_type: &str, fn_name: &str) -> bool {
19 self.recv_type.as_ref().is_some_and(|r| r == recv_type)
20 && self.module == module
21 && self.fn_name == fn_name
22 }
23}
24
25pub fn resolve_call(expr: &Expression) -> Option<CallTarget> {
30 let Expression::Call {
31 expression: callee, ..
32 } = expr
33 else {
34 return None;
35 };
36 let Expression::DotAccess {
37 expression: base,
38 member,
39 ..
40 } = callee.unwrap_parens()
41 else {
42 return None;
43 };
44 let base_ty = base.get_type().strip_refs();
45 match base_ty {
46 Type::ImportNamespace(module_id) => Some(CallTarget {
47 module: module_id,
48 recv_type: None,
49 fn_name: member.clone(),
50 }),
51 Type::Nominal { id, .. } => {
52 let module = id.without_last_segment()?;
53 Some(CallTarget {
54 module: EcoString::from(module),
55 recv_type: Some(EcoString::from(id.last_segment())),
56 fn_name: member.clone(),
57 })
58 }
59 _ => None,
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn is_matches_free_function() {
69 let t = CallTarget {
70 module: EcoString::from("go:strings"),
71 recv_type: None,
72 fn_name: EcoString::from("Contains"),
73 };
74 assert!(t.is("go:strings", "Contains"));
75 assert!(!t.is("go:strings", "Other"));
76 assert!(!t.is("go:other", "Contains"));
77 assert!(!t.is_method("go:strings", "String", "Contains"));
78 }
79
80 #[test]
81 fn is_method_matches_nominal_receiver() {
82 let t = CallTarget {
83 module: EcoString::from("go:time"),
84 recv_type: Some(EcoString::from("Time")),
85 fn_name: EcoString::from("Equal"),
86 };
87 assert!(t.is_method("go:time", "Time", "Equal"));
88 assert!(!t.is_method("go:time", "Duration", "Equal"));
89 assert!(!t.is("go:time", "Equal"));
90 }
91}