lisette_semantics/
call_classification.rs1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2
3use syntax::ast::Expression;
4use syntax::program::{DefinitionBody, Module};
5use syntax::types::{Symbol, Type};
6
7pub fn is_ufcs_method_type(method_ty: &Type, base_generics_count: usize) -> bool {
14 let Type::Forall { vars, body } = method_ty else {
15 return base_generics_count > 0;
16 };
17
18 if vars.len() > base_generics_count {
19 return true;
20 }
21
22 if let Type::Function { params, .. } = body.as_ref()
23 && let Some(receiver_param) = params.first()
24 && let Type::Nominal {
25 params: receiver_params,
26 ..
27 } = receiver_param.strip_refs()
28 {
29 for param in receiver_params {
30 if matches!(param, Type::Nominal { .. }) {
31 return true;
32 }
33 }
34 }
35
36 false
37}
38
39pub fn compute_module_ufcs(module: &Module, module_id: &str) -> Vec<(String, String)> {
46 let mut ufcs = Vec::new();
47
48 for (key, definition) in &module.definitions {
50 let (methods, base_generics_count) = match &definition.body {
51 DefinitionBody::Struct {
52 methods, generics, ..
53 } => (methods, generics.len()),
54 DefinitionBody::Enum {
55 methods, generics, ..
56 } => (methods, generics.len()),
57 DefinitionBody::TypeAlias {
58 methods, generics, ..
59 } => (methods, generics.len()),
60 _ => continue,
61 };
62
63 for (method_name, method_ty) in methods {
64 if is_ufcs_method_type(method_ty, base_generics_count) {
65 ufcs.push((key.to_string(), method_name.to_string()));
66 }
67 }
68 }
69
70 let mut constrained_methods: HashMap<String, Vec<String>> = HashMap::default();
72 let mut unconstrained_types: HashSet<String> = HashSet::default();
73
74 for file in module.files.values() {
75 for item in &file.items {
76 if let Expression::ImplBlock {
77 receiver_name,
78 generics,
79 methods,
80 ..
81 } = item
82 {
83 let qualified_type = Symbol::from_parts(module_id, receiver_name).to_string();
84 if generics.iter().any(|g| !g.bounds.is_empty()) {
85 let method_names: Vec<String> = methods
86 .iter()
87 .filter_map(|m| {
88 if let Expression::Function { name, .. } = m {
89 Some(name.to_string())
90 } else {
91 None
92 }
93 })
94 .collect();
95 constrained_methods
96 .entry(qualified_type)
97 .or_default()
98 .extend(method_names);
99 } else {
100 unconstrained_types.insert(qualified_type);
101 }
102 }
103 }
104 }
105
106 for (type_name, methods) in constrained_methods {
107 if unconstrained_types.contains(&type_name) {
108 for method_name in methods {
109 ufcs.push((type_name.clone(), method_name));
110 }
111 }
112 }
113
114 ufcs
115}