1use std::sync::Arc;
2
3use cairo_lang_defs::ids::{
4 FileIndex, LanguageElementId, ModuleFileId, ModuleId, NamedLanguageElementId, TraitFunctionId,
5 TraitId,
6};
7use cairo_lang_filesystem::ids::CrateId;
8use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
9use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
10use smol_str::SmolStr;
11
12use crate::corelib::{core_submodule, get_submodule};
13use crate::db::SemanticGroup;
14use crate::expr::inference::InferenceId;
15use crate::items::us::SemanticUseEx;
16use crate::items::visibility::peek_visible_in;
17use crate::resolve::{ResolvedGenericItem, Resolver};
18use crate::types::TypeHead;
19
20#[derive(Clone, Debug, Hash, PartialEq, Eq)]
22pub enum TypeFilter {
23 NoFilter,
25 TypeHead(TypeHead),
27}
28
29pub fn methods_in_module(
31 db: &dyn SemanticGroup,
32 module_id: ModuleId,
33 type_filter: TypeFilter,
34) -> Arc<[TraitFunctionId]> {
35 let mut result = Vec::new();
36 let Ok(module_traits_ids) = db.module_traits_ids(module_id) else {
37 return result.into();
38 };
39 for trait_id in module_traits_ids.iter().copied() {
40 for (_, trait_function) in db.trait_functions(trait_id).unwrap_or_default() {
41 let Ok(signature) = db.trait_function_signature(trait_function) else {
42 continue;
43 };
44 let Some(first_param) = signature.params.first() else {
45 continue;
46 };
47 if first_param.name != "self" {
48 continue;
49 }
50 if let TypeFilter::TypeHead(type_head) = &type_filter {
51 if let Some(head) = first_param.ty.head(db) {
52 if !fit_for_method(&head, type_head) {
53 continue;
54 }
55 }
56 }
57
58 result.push(trait_function)
59 }
60 }
61 result.into()
62}
63
64fn fit_for_method(head: &TypeHead, type_head: &TypeHead) -> bool {
66 if head == type_head {
67 return true;
68 }
69 if let TypeHead::Snapshot(snapshot_head) = head {
70 return snapshot_head.as_ref() == type_head;
71 }
72 false
73}
74
75pub fn methods_in_crate(
77 db: &dyn SemanticGroup,
78 crate_id: CrateId,
79 type_filter: TypeFilter,
80) -> Arc<[TraitFunctionId]> {
81 let mut result = Vec::new();
82 for module_id in db.crate_modules(crate_id).iter() {
83 result.extend_from_slice(&db.methods_in_module(*module_id, type_filter.clone())[..])
84 }
85 result.into()
86}
87
88pub fn visible_traits_in_module(
90 db: &dyn SemanticGroup,
91 module_id: ModuleId,
92 user_module_file_id: ModuleFileId,
93 include_parent: bool,
94) -> Arc<[(TraitId, String)]> {
95 let mut visited_modules = UnorderedHashSet::default();
96 visible_traits_in_module_ex(
97 db,
98 module_id,
99 user_module_file_id,
100 include_parent,
101 &mut visited_modules,
102 )
103 .unwrap_or_else(|| Vec::new().into())
104}
105
106fn visible_traits_in_module_ex(
109 db: &dyn SemanticGroup,
110 module_id: ModuleId,
111 user_module_file_id: ModuleFileId,
112 include_parent: bool,
113 visited_modules: &mut UnorderedHashSet<ModuleId>,
114) -> Option<Arc<[(TraitId, String)]>> {
115 let mut result = Vec::new();
116 if visited_modules.contains(&module_id) {
117 return Some(result.into());
118 }
119
120 let resolver = Resolver::new(db, user_module_file_id, InferenceId::NoContext);
121 let ignore_visibility = resolver.ignore_visibility_checks(module_id);
122 let is_visible = |item_name: SmolStr| {
124 if ignore_visibility {
125 Some(true)
126 } else {
127 let item_info = db.module_item_info_by_name(module_id, item_name).ok()??;
128 Some(peek_visible_in(
129 db.upcast(),
130 item_info.visibility,
131 module_id,
132 user_module_file_id.0,
133 ))
134 }
135 };
136 visited_modules.insert(module_id);
137 let mut modules_to_visit = vec![];
138 for use_id in db.module_uses_ids(module_id).ok()?.iter().copied() {
140 if !is_visible(use_id.name(db.upcast()))? {
141 continue;
142 }
143 let resolved_item = db.use_resolved_item(use_id).ok()?;
144 match resolved_item {
145 ResolvedGenericItem::Module(inner_module_id) => {
146 modules_to_visit.push(inner_module_id);
147 }
148 ResolvedGenericItem::Trait(trait_id) => {
149 result.push((trait_id, trait_id.name(db.upcast()).to_string()));
150 }
151 _ => continue,
152 }
153 }
154 for submodule_id in db.module_submodules_ids(module_id).ok()?.iter().copied() {
156 if !is_visible(submodule_id.name(db.upcast()))? {
157 continue;
158 }
159 modules_to_visit.push(ModuleId::Submodule(submodule_id));
160 }
161 for trait_id in db.module_traits_ids(module_id).ok()?.iter().copied() {
163 if !is_visible(trait_id.name(db.upcast()))? {
164 continue;
165 }
166 result.push((trait_id, trait_id.name(db.upcast()).to_string()));
167 }
168
169 for submodule in modules_to_visit {
170 for (trait_id, path) in visible_traits_in_module_ex(
171 db,
172 submodule,
173 user_module_file_id,
174 include_parent,
175 visited_modules,
176 )?
177 .iter()
178 {
179 result.push((*trait_id, format!("{}::{}", submodule.name(db.upcast()), path)));
180 }
181 }
182 if include_parent {
184 match module_id {
185 ModuleId::CrateRoot(_) => {}
186 ModuleId::Submodule(submodule_id) => {
187 let parent_module_id = submodule_id.parent_module(db.upcast());
188 for (trait_id, path) in visible_traits_in_module_ex(
189 db,
190 parent_module_id,
191 user_module_file_id,
192 include_parent,
193 visited_modules,
194 )?
195 .iter()
196 {
197 result.push((*trait_id, format!("super::{}", path)));
198 }
199 }
200 }
201 }
202 Some(result.into())
203}
204
205pub fn visible_traits_in_crate(
207 db: &dyn SemanticGroup,
208 crate_id: CrateId,
209 user_module_file_id: ModuleFileId,
210) -> Arc<[(TraitId, String)]> {
211 let crate_name = crate_id.name(db.upcast());
212 let crate_as_module = ModuleId::CrateRoot(crate_id);
213 db.visible_traits_in_module(crate_as_module, user_module_file_id, false)
214 .iter()
215 .cloned()
216 .map(|(trait_id, path)| (trait_id, format!("{crate_name}::{path}",)))
217 .collect::<Vec<_>>()
218 .into()
219}
220
221pub fn visible_traits_from_module(
223 db: &dyn SemanticGroup,
224 module_file_id: ModuleFileId,
225) -> Option<Arc<OrderedHashMap<TraitId, String>>> {
226 let module_id = module_file_id.0;
227 let mut current_top_module = module_id;
228 while let ModuleId::Submodule(submodule_id) = current_top_module {
229 current_top_module = submodule_id.parent_module(db.upcast());
230 }
231 let current_crate_id = match current_top_module {
232 ModuleId::CrateRoot(crate_id) => crate_id,
233 ModuleId::Submodule(_) => unreachable!("current module is not a top-level module"),
234 };
235 let edition = db.crate_config(current_crate_id)?.settings.edition;
236 let prelude_submodule_name = edition.prelude_submodule_name();
237 let core_prelude_submodule = core_submodule(db, "prelude");
238 let prelude_submodule = get_submodule(db, core_prelude_submodule, prelude_submodule_name)?;
239 let prelude_submodule_file_id = ModuleFileId(prelude_submodule, FileIndex(0));
240
241 let mut module_visible_traits = Vec::new();
242 module_visible_traits.extend_from_slice(
243 &db.visible_traits_in_module(prelude_submodule, prelude_submodule_file_id, false)[..],
244 );
245 module_visible_traits
246 .extend_from_slice(&db.visible_traits_in_module(module_id, module_file_id, true)[..]);
247 for crate_id in db.crates() {
248 if crate_id == current_crate_id {
249 continue;
250 }
251 module_visible_traits
252 .extend_from_slice(&db.visible_traits_in_crate(crate_id, module_file_id)[..]);
253 }
254 let mut result: OrderedHashMap<TraitId, String> = OrderedHashMap::default();
255 for (trait_id, path) in module_visible_traits {
256 match result.entry(trait_id) {
257 Entry::Occupied(existing_path) => {
258 if path.split("::").count() < existing_path.get().split("::").count() {
259 *existing_path.into_mut() = path;
260 }
261 }
262 Entry::Vacant(entry) => {
263 entry.insert(path);
264 }
265 }
266 }
267 Some(result.into())
268}