1use std::sync::Arc;
2
3use cairo_lang_defs::ids::{
4 FileIndex, GenericTypeId, ImportableId, LanguageElementId, ModuleFileId, ModuleId,
5 NamedLanguageElementId, TraitFunctionId, TraitId,
6};
7use cairo_lang_filesystem::db::CORELIB_CRATE_NAME;
8use cairo_lang_filesystem::ids::{CrateId, CrateLongId};
9use cairo_lang_utils::Intern;
10use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
11use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
12use itertools::chain;
13use smol_str::SmolStr;
14
15use crate::Variant;
16use crate::corelib::{self, core_submodule, get_submodule};
17use crate::db::SemanticGroup;
18use crate::expr::inference::InferenceId;
19use crate::items::functions::GenericFunctionId;
20use crate::items::us::SemanticUseEx;
21use crate::items::visibility::peek_visible_in;
22use crate::resolve::{ResolvedGenericItem, Resolver};
23use crate::types::TypeHead;
24
25#[derive(Clone, Debug, Hash, PartialEq, Eq)]
27pub enum TypeFilter {
28 NoFilter,
30 TypeHead(TypeHead),
32}
33
34pub fn methods_in_module(
36 db: &dyn SemanticGroup,
37 module_id: ModuleId,
38 type_filter: TypeFilter,
39) -> Arc<[TraitFunctionId]> {
40 let mut result = Vec::new();
41 let Ok(module_traits_ids) = db.module_traits_ids(module_id) else {
42 return result.into();
43 };
44 for trait_id in module_traits_ids.iter().copied() {
45 for (_, trait_function) in db.trait_functions(trait_id).unwrap_or_default() {
46 let Ok(signature) = db.trait_function_signature(trait_function) else {
47 continue;
48 };
49 let Some(first_param) = signature.params.first() else {
50 continue;
51 };
52 if first_param.name != "self" {
53 continue;
54 }
55 if let TypeFilter::TypeHead(type_head) = &type_filter {
56 if let Some(head) = first_param.ty.head(db) {
57 if !fit_for_method(&head, type_head) {
58 continue;
59 }
60 }
61 }
62
63 result.push(trait_function)
64 }
65 }
66 result.into()
67}
68
69fn fit_for_method(head: &TypeHead, type_head: &TypeHead) -> bool {
71 if head == type_head {
72 return true;
73 }
74 if let TypeHead::Snapshot(snapshot_head) = head {
75 return snapshot_head.as_ref() == type_head;
76 }
77 false
78}
79
80pub fn methods_in_crate(
82 db: &dyn SemanticGroup,
83 crate_id: CrateId,
84 type_filter: TypeFilter,
85) -> Arc<[TraitFunctionId]> {
86 let mut result = Vec::new();
87 for module_id in db.crate_modules(crate_id).iter() {
88 result.extend_from_slice(&db.methods_in_module(*module_id, type_filter.clone())[..])
89 }
90 result.into()
91}
92
93pub fn visible_importables_in_module(
95 db: &dyn SemanticGroup,
96 module_id: ModuleId,
97 user_module_file_id: ModuleFileId,
98 include_parent: bool,
99) -> Arc<[(ImportableId, String)]> {
100 let mut visited_modules = UnorderedHashSet::default();
101 visible_importables_in_module_ex(
102 db,
103 module_id,
104 user_module_file_id,
105 include_parent,
106 &mut visited_modules,
107 )
108 .unwrap_or_else(|| Vec::new().into())
109}
110
111fn visible_importables_in_module_ex(
114 db: &dyn SemanticGroup,
115 module_id: ModuleId,
116 user_module_file_id: ModuleFileId,
117 include_parent: bool,
118 visited_modules: &mut UnorderedHashSet<ModuleId>,
119) -> Option<Arc<[(ImportableId, String)]>> {
120 let mut result = Vec::new();
121 if visited_modules.contains(&module_id) {
122 return Some(result.into());
123 }
124
125 let resolver = Resolver::new(db, user_module_file_id, InferenceId::NoContext);
126 let ignore_visibility = resolver.ignore_visibility_checks(module_id);
127 let is_visible = |item_name: SmolStr| {
129 if ignore_visibility {
130 Some(true)
131 } else {
132 let item_info = db.module_item_info_by_name(module_id, item_name).ok()??;
133 Some(peek_visible_in(
134 db.upcast(),
135 item_info.visibility,
136 module_id,
137 user_module_file_id.0,
138 ))
139 }
140 };
141 visited_modules.insert(module_id);
142 let mut modules_to_visit = vec![];
143 for use_id in db.module_uses_ids(module_id).ok()?.iter().copied() {
145 if !is_visible(use_id.name(db.upcast()))? {
146 continue;
147 }
148 let resolved_item = db.use_resolved_item(use_id).ok()?;
149 let (resolved_item, name) = match resolved_item {
150 ResolvedGenericItem::Module(ModuleId::CrateRoot(_)) => continue,
151 ResolvedGenericItem::Module(inner_module_id @ ModuleId::Submodule(module)) => {
152 modules_to_visit.push(inner_module_id);
153
154 (ImportableId::Submodule(module), module.name(db.upcast()))
155 }
156 ResolvedGenericItem::GenericConstant(item_id) => {
157 (ImportableId::Constant(item_id), item_id.name(db.upcast()))
158 }
159 ResolvedGenericItem::GenericFunction(GenericFunctionId::Free(item_id)) => {
160 (ImportableId::FreeFunction(item_id), item_id.name(db.upcast()))
161 }
162 ResolvedGenericItem::GenericFunction(GenericFunctionId::Extern(item_id)) => {
163 (ImportableId::ExternFunction(item_id), item_id.name(db.upcast()))
164 }
165 ResolvedGenericItem::GenericFunction(GenericFunctionId::Impl(_item_id)) => continue,
166 ResolvedGenericItem::GenericType(GenericTypeId::Struct(item_id)) => {
167 (ImportableId::Struct(item_id), item_id.name(db.upcast()))
168 }
169 ResolvedGenericItem::GenericType(GenericTypeId::Enum(item_id)) => {
170 (ImportableId::Enum(item_id), item_id.name(db.upcast()))
171 }
172 ResolvedGenericItem::GenericType(GenericTypeId::Extern(item_id)) => {
173 (ImportableId::ExternType(item_id), item_id.name(db.upcast()))
174 }
175 ResolvedGenericItem::GenericTypeAlias(item_id) => {
176 (ImportableId::TypeAlias(item_id), item_id.name(db.upcast()))
177 }
178 ResolvedGenericItem::GenericImplAlias(item_id) => {
179 (ImportableId::ImplAlias(item_id), item_id.name(db.upcast()))
180 }
181 ResolvedGenericItem::Variant(Variant { id, .. }) => {
182 (ImportableId::Variant(id), id.name(db.upcast()))
183 }
184 ResolvedGenericItem::Trait(item_id) => {
185 (ImportableId::Trait(item_id), item_id.name(db.upcast()))
186 }
187 ResolvedGenericItem::Impl(item_id) => {
188 (ImportableId::Impl(item_id), item_id.name(db.upcast()))
189 }
190 ResolvedGenericItem::Variable(_) => continue,
191 };
192
193 result.push((resolved_item, name.to_string()));
194 }
195 for submodule_id in db.module_submodules_ids(module_id).ok()?.iter().copied() {
196 if !is_visible(submodule_id.name(db.upcast()))? {
197 continue;
198 }
199 modules_to_visit.push(ModuleId::Submodule(submodule_id));
200 }
201
202 macro_rules! module_importables {
203 ($query:ident, $map:expr) => {
204 for item_id in db.$query(module_id).ok()?.iter().copied() {
205 if !is_visible(item_id.name(db.upcast()))? {
206 continue;
207 }
208 result.push(($map(item_id), item_id.name(db.upcast()).to_string()));
209 }
210 };
211 }
212
213 module_importables!(module_constants_ids, ImportableId::Constant);
214 module_importables!(module_free_functions_ids, ImportableId::FreeFunction);
215 module_importables!(module_structs_ids, ImportableId::Struct);
216 module_importables!(module_enums_ids, ImportableId::Enum);
217 module_importables!(module_type_aliases_ids, ImportableId::TypeAlias);
218 module_importables!(module_impl_aliases_ids, ImportableId::ImplAlias);
219 module_importables!(module_traits_ids, ImportableId::Trait);
220 module_importables!(module_impls_ids, ImportableId::Impl);
221 module_importables!(module_extern_functions_ids, ImportableId::ExternFunction);
222 module_importables!(module_extern_types_ids, ImportableId::ExternType);
223
224 for submodule in modules_to_visit {
225 for (item_id, path) in visible_importables_in_module_ex(
226 db,
227 submodule,
228 user_module_file_id,
229 false,
230 visited_modules,
231 )?
232 .iter()
233 {
234 result.push((*item_id, format!("{}::{}", submodule.name(db.upcast()), path)));
235 }
236 }
237 if include_parent {
239 match module_id {
240 ModuleId::CrateRoot(_) => {}
241 ModuleId::Submodule(submodule_id) => {
242 let parent_module_id = submodule_id.parent_module(db.upcast());
243 for (item_id, path) in visible_importables_in_module_ex(
244 db,
245 parent_module_id,
246 user_module_file_id,
247 include_parent,
248 visited_modules,
249 )?
250 .iter()
251 {
252 result.push((*item_id, format!("super::{}", path)));
253 }
254 }
255 }
256 }
257 Some(result.into())
258}
259
260pub fn visible_importables_in_crate(
262 db: &dyn SemanticGroup,
263 crate_id: CrateId,
264 user_module_file_id: ModuleFileId,
265) -> Arc<[(ImportableId, String)]> {
266 let is_current_crate = user_module_file_id.0.owning_crate(db.upcast()) == crate_id;
267 let crate_name = if is_current_crate { "crate" } else { &crate_id.name(db.upcast()) };
268 let crate_as_module = ModuleId::CrateRoot(crate_id);
269 db.visible_importables_in_module(crate_as_module, user_module_file_id, false)
270 .iter()
271 .cloned()
272 .map(|(item_id, path)| (item_id, format!("{crate_name}::{path}",)))
273 .collect::<Vec<_>>()
274 .into()
275}
276
277pub fn visible_importables_from_module(
279 db: &dyn SemanticGroup,
280 module_file_id: ModuleFileId,
281) -> Option<Arc<OrderedHashMap<ImportableId, String>>> {
282 let module_id = module_file_id.0;
283 let mut current_top_module = module_id;
284 while let ModuleId::Submodule(submodule_id) = current_top_module {
285 current_top_module = submodule_id.parent_module(db.upcast());
286 }
287 let current_crate_id = match current_top_module {
288 ModuleId::CrateRoot(crate_id) => crate_id,
289 ModuleId::Submodule(_) => unreachable!("current module is not a top-level module"),
290 };
291 let edition = db.crate_config(current_crate_id)?.settings.edition;
292 let prelude_submodule_name = edition.prelude_submodule_name();
293 let core_prelude_submodule = core_submodule(db, "prelude");
294 let prelude_submodule = get_submodule(db, core_prelude_submodule, prelude_submodule_name)?;
295 let prelude_submodule_file_id = ModuleFileId(prelude_submodule, FileIndex(0));
296
297 let mut module_visible_importables = Vec::new();
298 module_visible_importables.extend_from_slice(
300 &db.visible_importables_in_module(prelude_submodule, prelude_submodule_file_id, false)[..],
301 );
302 let settings = db.crate_config(current_crate_id).map(|c| c.settings).unwrap_or_default();
304 for crate_id in chain!(
305 [current_crate_id],
306 (!settings.dependencies.contains_key(CORELIB_CRATE_NAME)).then(|| corelib::core_crate(db)),
307 settings.dependencies.iter().map(|(name, setting)| {
308 CrateLongId::Real {
309 name: name.clone().into(),
310 discriminator: setting.discriminator.clone(),
311 }
312 .intern(db)
313 })
314 ) {
315 module_visible_importables
316 .extend_from_slice(&db.visible_importables_in_crate(crate_id, module_file_id)[..]);
317 }
318
319 module_visible_importables
321 .extend_from_slice(&db.visible_importables_in_module(module_id, module_file_id, true)[..]);
322
323 let mut result: OrderedHashMap<ImportableId, String> = OrderedHashMap::default();
328 for (trait_id, path) in module_visible_importables {
329 match result.entry(trait_id) {
330 Entry::Occupied(existing_path) => {
331 if path.split("::").count() < existing_path.get().split("::").count() {
332 *existing_path.into_mut() = path;
333 }
334 }
335 Entry::Vacant(entry) => {
336 entry.insert(path);
337 }
338 }
339 }
340 Some(result.into())
341}
342
343pub fn visible_traits_from_module(
345 db: &dyn SemanticGroup,
346 module_file_id: ModuleFileId,
347) -> Option<Arc<OrderedHashMap<TraitId, String>>> {
348 let importables = db.visible_importables_from_module(module_file_id)?;
349
350 let traits = importables
351 .iter()
352 .filter_map(|(item, path)| {
353 if let ImportableId::Trait(trait_id) = item {
354 Some((*trait_id, path.clone()))
355 } else {
356 None
357 }
358 })
359 .collect::<OrderedHashMap<_, _>>()
360 .into();
361
362 Some(traits)
363}