1use std::sync::Arc;
2
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5 GenericTypeId, ImportableId, LanguageElementId, ModuleId, NamedLanguageElementId,
6 TraitFunctionId, TraitId,
7};
8use cairo_lang_filesystem::db::{CORELIB_CRATE_NAME, FilesGroup, default_crate_settings};
9use cairo_lang_filesystem::ids::{
10 CrateId, CrateLongId, FileId, FileInput, FileLongId, SmolStrId, Tracked,
11};
12use cairo_lang_utils::Intern;
13use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
14use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
15use itertools::chain;
16use salsa::{Accumulator, Database};
17
18use crate::Variant;
19use crate::corelib::{self, core_submodule, get_submodule};
20use crate::db::module_inline_macro_expansions;
21use crate::expr::inference::InferenceId;
22use crate::items::enm::EnumSemantic;
23use crate::items::functions::GenericFunctionId;
24use crate::items::macro_call::module_macro_modules;
25use crate::items::module::ModuleSemantic;
26use crate::items::trt::TraitSemantic;
27use crate::items::us::SemanticUseEx;
28use crate::keyword::SELF_PARAM_KW;
29use crate::resolve::{ResolvedGenericItem, Resolver};
30use crate::types::TypeHead;
31
32#[derive(Clone, Debug, Hash, PartialEq, Eq)]
34pub enum TypeFilter<'db> {
35 NoFilter,
37 TypeHead(TypeHead<'db>),
39}
40
41pub fn methods_in_module<'db>(
43 db: &'db dyn Database,
44 module_id: ModuleId<'db>,
45 type_filter: TypeFilter<'db>,
46) -> Arc<Vec<TraitFunctionId<'db>>> {
47 let mut result = Vec::new();
48 let Ok(module_traits_ids) = db.module_traits_ids(module_id) else {
49 return result.into();
50 };
51 for trait_id in module_traits_ids.iter().copied() {
52 if let Ok(trait_functions) = db.trait_functions(trait_id) {
53 for trait_function in trait_functions.values() {
54 let Ok(signature) = db.trait_function_signature(*trait_function) else {
55 continue;
56 };
57 let Some(first_param) = signature.params.first() else {
58 continue;
59 };
60 if first_param.name.long(db) != SELF_PARAM_KW {
61 continue;
62 }
63 if let TypeFilter::TypeHead(type_head) = &type_filter
64 && let Some(head) = first_param.ty.head(db)
65 && !fit_for_method(&head, type_head)
66 {
67 continue;
68 }
69
70 result.push(*trait_function)
71 }
72 }
73 }
74 Arc::new(result)
75}
76
77#[salsa::tracked]
79pub fn methods_in_module_tracked<'db>(
80 db: &'db dyn Database,
81 module_id: ModuleId<'db>,
82 type_filter: TypeFilter<'db>,
83) -> Arc<Vec<TraitFunctionId<'db>>> {
84 methods_in_module(db, module_id, type_filter)
85}
86
87fn fit_for_method(head: &TypeHead<'_>, type_head: &TypeHead<'_>) -> bool {
89 if let TypeHead::Generic(_) = head {
91 return true;
92 }
93 if head == type_head {
94 return true;
95 }
96 if let TypeHead::Snapshot(snapshot_head) = head {
97 return fit_for_method(snapshot_head.as_ref(), type_head);
98 }
99 false
100}
101
102pub fn methods_in_crate<'db>(
104 db: &'db dyn Database,
105 crate_id: CrateId<'db>,
106 type_filter: TypeFilter<'db>,
107) -> Arc<Vec<TraitFunctionId<'db>>> {
108 let mut result = Vec::new();
109 for module_id in db.crate_modules(crate_id).iter() {
110 result.extend_from_slice(&db.methods_in_module(*module_id, type_filter.clone())[..])
111 }
112 Arc::new(result)
113}
114
115#[salsa::tracked]
117pub fn methods_in_crate_tracked<'db>(
118 db: &'db dyn Database,
119 crate_id: CrateId<'db>,
120 type_filter: TypeFilter<'db>,
121) -> Arc<Vec<TraitFunctionId<'db>>> {
122 methods_in_crate(db, crate_id, type_filter)
123}
124
125pub fn visible_importables_in_module<'db>(
127 db: &'db dyn Database,
128 module_id: ModuleId<'db>,
129 user_module_id: ModuleId<'db>,
130 include_parent: bool,
131) -> Arc<Vec<(ImportableId<'db>, String)>> {
132 let mut visited_modules = UnorderedHashSet::default();
133 visible_importables_in_module_ex(
134 db,
135 module_id,
136 user_module_id,
137 include_parent,
138 &mut visited_modules,
139 )
140 .unwrap_or_else(|| Arc::new(Vec::new()))
141}
142
143#[salsa::tracked]
145pub fn visible_importables_in_module_tracked<'db>(
146 db: &'db dyn Database,
147 module_id: ModuleId<'db>,
148 user_module_id: ModuleId<'db>,
149 include_parent: bool,
150) -> Arc<Vec<(ImportableId<'db>, String)>> {
151 visible_importables_in_module(db, module_id, user_module_id, include_parent)
152}
153
154fn visible_importables_in_module_ex<'db>(
157 db: &'db dyn Database,
158 module_id: ModuleId<'db>,
159 user_module_id: ModuleId<'db>,
160 include_parent: bool,
161 visited_modules: &mut UnorderedHashSet<ModuleId<'db>>,
162) -> Option<Arc<Vec<(ImportableId<'db>, String)>>> {
163 let mut result = Vec::new();
164 if visited_modules.contains(&module_id) {
165 return Some(result.into());
166 }
167
168 let resolver = Resolver::new(db, user_module_id, InferenceId::NoContext);
169
170 let is_visible = |item_name: SmolStrId<'_>| {
172 let item_info = db.module_item_info_by_name(module_id, item_name).ok()??;
173 Some(resolver.is_item_visible(module_id, &item_info, user_module_id))
174 };
175 visited_modules.insert(module_id);
176 let mut modules_to_visit = vec![];
177 for use_id in db.module_uses_ids(module_id).unwrap_or_default().iter().copied() {
179 if !is_visible(use_id.name(db)).unwrap_or_default() {
180 continue;
181 }
182 let Ok(resolved_item) = db.use_resolved_item(use_id) else {
183 continue;
184 };
185
186 let (resolved_item, name) = match resolved_item {
187 ResolvedGenericItem::Module(ModuleId::CrateRoot(crate_id)) => {
188 result.extend_from_slice(&db.visible_importables_in_crate(crate_id, module_id)[..]);
189
190 (ImportableId::Crate(crate_id), crate_id.long(db).name().long(db))
191 }
192 ResolvedGenericItem::Module(inner_module_id @ ModuleId::Submodule(module)) => {
193 modules_to_visit.push(inner_module_id);
194
195 (ImportableId::Submodule(module), module.name(db).long(db))
196 }
197 ResolvedGenericItem::Module(ModuleId::MacroCall { .. }) => {
198 continue;
199 }
200 ResolvedGenericItem::GenericConstant(item_id) => {
201 (ImportableId::Constant(item_id), item_id.name(db).long(db))
202 }
203 ResolvedGenericItem::GenericFunction(GenericFunctionId::Free(item_id)) => {
204 (ImportableId::FreeFunction(item_id), item_id.name(db).long(db))
205 }
206 ResolvedGenericItem::GenericFunction(GenericFunctionId::Extern(item_id)) => {
207 (ImportableId::ExternFunction(item_id), item_id.name(db).long(db))
208 }
209 ResolvedGenericItem::GenericType(GenericTypeId::Struct(item_id)) => {
210 (ImportableId::Struct(item_id), item_id.name(db).long(db))
211 }
212 ResolvedGenericItem::GenericType(GenericTypeId::Enum(item_id)) => {
213 let enum_name = item_id.name(db);
214
215 if let Ok(variants) = db.enum_variants(item_id) {
216 for (name, id) in variants.iter() {
217 result.push((
218 ImportableId::Variant(*id),
219 format!("{}::{}", enum_name.long(db), name.long(db)),
220 ));
221 }
222 }
223
224 (ImportableId::Enum(item_id), enum_name.long(db))
225 }
226 ResolvedGenericItem::GenericType(GenericTypeId::Extern(item_id)) => {
227 (ImportableId::ExternType(item_id), item_id.name(db).long(db))
228 }
229 ResolvedGenericItem::GenericTypeAlias(item_id) => {
230 (ImportableId::TypeAlias(item_id), item_id.name(db).long(db))
231 }
232 ResolvedGenericItem::GenericImplAlias(item_id) => {
233 (ImportableId::ImplAlias(item_id), item_id.name(db).long(db))
234 }
235 ResolvedGenericItem::Variant(Variant { id, .. }) => {
236 (ImportableId::Variant(id), id.name(db).long(db))
237 }
238 ResolvedGenericItem::Trait(item_id) => {
239 (ImportableId::Trait(item_id), item_id.name(db).long(db))
240 }
241 ResolvedGenericItem::Impl(item_id) => {
242 (ImportableId::Impl(item_id), item_id.name(db).long(db))
243 }
244 ResolvedGenericItem::Macro(item_id) => {
245 (ImportableId::MacroDeclaration(item_id), item_id.name(db).long(db))
246 }
247 ResolvedGenericItem::Variable(_)
248 | ResolvedGenericItem::TraitItem(_)
249 | ResolvedGenericItem::GenericFunction(GenericFunctionId::Impl(_)) => continue,
250 };
251
252 result.push((resolved_item, name.to_string()));
253 }
254
255 if !matches!(module_id, ModuleId::MacroCall { .. }) {
256 modules_to_visit.extend(module_macro_modules(db, false, module_id).iter().copied());
257 }
258
259 for submodule_id in db.module_submodules_ids(module_id).unwrap_or_default().iter().copied() {
260 if !is_visible(submodule_id.name(db)).unwrap_or_default() {
261 continue;
262 }
263 result.push((ImportableId::Submodule(submodule_id), submodule_id.name(db).to_string(db)));
264 modules_to_visit.push(ModuleId::Submodule(submodule_id));
265 }
266
267 for enum_id in db.module_enums_ids(module_id).unwrap_or_default().iter().copied() {
269 let enum_name = enum_id.name(db);
270 if !is_visible(enum_name).unwrap_or_default() {
271 continue;
272 }
273
274 result.push((ImportableId::Enum(enum_id), enum_name.to_string(db)));
275
276 if let Ok(variants) = db.enum_variants(enum_id) {
277 for (name, id) in variants.iter() {
278 result.push((
279 ImportableId::Variant(*id),
280 format!("{}::{}", enum_name.long(db), name.long(db)),
281 ));
282 }
283 }
284 }
285
286 macro_rules! module_importables {
287 ($query:ident, $map:expr) => {
288 for item_id in db.$query(module_id).ok().unwrap_or_default().iter().copied() {
289 if !is_visible(item_id.name(db)).unwrap_or_default() {
290 continue;
291 }
292 result.push(($map(item_id), item_id.name(db).to_string(db)));
293 }
294 };
295 }
296
297 module_importables!(module_constants_ids, ImportableId::Constant);
298 module_importables!(module_free_functions_ids, ImportableId::FreeFunction);
299 module_importables!(module_structs_ids, ImportableId::Struct);
300 module_importables!(module_type_aliases_ids, ImportableId::TypeAlias);
301 module_importables!(module_impl_aliases_ids, ImportableId::ImplAlias);
302 module_importables!(module_traits_ids, ImportableId::Trait);
303 module_importables!(module_impls_ids, ImportableId::Impl);
304 module_importables!(module_extern_functions_ids, ImportableId::ExternFunction);
305 module_importables!(module_extern_types_ids, ImportableId::ExternType);
306 module_importables!(module_macro_declarations_ids, ImportableId::MacroDeclaration);
307
308 for submodule in modules_to_visit {
309 for (item_id, path) in
310 visible_importables_in_module_ex(db, submodule, user_module_id, false, visited_modules)
311 .unwrap_or_default()
312 .iter()
313 {
314 result.push((*item_id, format!("{}::{}", submodule.name(db).long(db), path)));
315 }
316 }
317 if include_parent {
319 match module_id {
320 ModuleId::CrateRoot(_) => {}
321 ModuleId::Submodule(submodule_id) => {
322 let parent_module_id = submodule_id.parent_module(db);
323 for (item_id, path) in visible_importables_in_module_ex(
324 db,
325 parent_module_id,
326 user_module_id,
327 include_parent,
328 visited_modules,
329 )
330 .unwrap_or_default()
331 .iter()
332 {
333 result.push((*item_id, format!("super::{path}")));
334 }
335 }
336 ModuleId::MacroCall { .. } => {}
337 }
338 }
339 Some(Arc::new(result))
340}
341
342pub fn visible_importables_in_crate<'db>(
344 db: &'db dyn Database,
345 crate_id: CrateId<'db>,
346 user_module_id: ModuleId<'db>,
347) -> Arc<Vec<(ImportableId<'db>, String)>> {
348 let is_current_crate = user_module_id.owning_crate(db) == crate_id;
349 let crate_name = if is_current_crate { "crate" } else { crate_id.long(db).name().long(db) };
350 let crate_as_module = ModuleId::CrateRoot(crate_id);
351 db.visible_importables_in_module(crate_as_module, user_module_id, false)
352 .iter()
353 .map(|(item_id, path)| (*item_id, format!("{crate_name}::{path}")))
354 .collect::<Vec<_>>()
355 .into()
356}
357
358#[salsa::tracked]
360pub fn visible_importables_in_crate_tracked<'db>(
361 db: &'db dyn Database,
362 crate_id: CrateId<'db>,
363 user_module_id: ModuleId<'db>,
364) -> Arc<Vec<(ImportableId<'db>, String)>> {
365 visible_importables_in_crate(db, crate_id, user_module_id)
366}
367
368pub fn visible_importables_from_module<'db>(
370 db: &'db dyn Database,
371 module_id: ModuleId<'db>,
372) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
373 let current_crate_id = module_id.owning_crate(db);
374 let prelude_submodule_name =
375 db.crate_config(current_crate_id)?.settings.edition.prelude_submodule_name(db);
376 let core_prelude_submodule = core_submodule(db, SmolStrId::from(db, "prelude"));
377 let prelude_submodule = get_submodule(db, core_prelude_submodule, prelude_submodule_name)?;
378
379 let mut module_visible_importables = Vec::new();
380 module_visible_importables.extend_from_slice(
382 &db.visible_importables_in_module(prelude_submodule, prelude_submodule, false)[..],
383 );
384 let settings = db
386 .crate_config(current_crate_id)
387 .map(|c| &c.settings)
388 .unwrap_or_else(|| default_crate_settings(db));
389 for crate_id in chain!(
390 [current_crate_id],
391 (!settings.dependencies.contains_key(CORELIB_CRATE_NAME)).then(|| corelib::core_crate(db)),
392 settings.dependencies.iter().map(|(name, setting)| {
393 CrateLongId::Real {
394 name: SmolStrId::from(db, name.clone()),
395 discriminator: setting.discriminator.clone(),
396 }
397 .intern(db)
398 })
399 ) {
400 module_visible_importables
401 .extend_from_slice(&db.visible_importables_in_crate(crate_id, module_id)[..]);
402 module_visible_importables
403 .push((ImportableId::Crate(crate_id), crate_id.long(db).name().to_string(db)));
404 }
405
406 module_visible_importables
408 .extend_from_slice(&db.visible_importables_in_module(module_id, module_id, true)[..]);
409
410 let mut result: OrderedHashMap<ImportableId<'_>, String> = OrderedHashMap::default();
415 for (trait_id, path) in module_visible_importables {
416 match result.entry(trait_id) {
417 Entry::Occupied(existing_path) => {
418 if path.split("::").count() < existing_path.get().split("::").count() {
419 *existing_path.into_mut() = path;
420 }
421 }
422 Entry::Vacant(entry) => {
423 entry.insert(path);
424 }
425 }
426 }
427 Some(Arc::new(result))
428}
429
430pub fn visible_importables_from_module_tracked<'db>(
432 db: &'db dyn Database,
433 module_id: ModuleId<'db>,
434) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
435 visible_importables_from_module_helper(db, (), module_id)
436}
437
438#[salsa::tracked]
439fn visible_importables_from_module_helper<'db>(
440 db: &'db dyn Database,
441 _tracked: Tracked,
442 module_id: ModuleId<'db>,
443) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
444 visible_importables_from_module(db, module_id)
445}
446
447pub fn visible_traits_from_module<'db>(
449 db: &'db dyn Database,
450 module_id: ModuleId<'db>,
451) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
452 let importables = db.visible_importables_from_module(module_id)?;
453
454 let traits = importables
455 .iter()
456 .filter_map(|(item, path)| {
457 if let ImportableId::Trait(trait_id) = item {
458 Some((*trait_id, path.clone()))
459 } else {
460 None
461 }
462 })
463 .collect::<OrderedHashMap<_, _>>()
464 .into();
465
466 Some(traits)
467}
468
469fn visible_traits_from_module_tracked<'db>(
471 db: &'db dyn Database,
472 module_id: ModuleId<'db>,
473) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
474 visible_traits_from_module_helper(db, (), module_id)
475}
476
477#[salsa::tracked]
478fn visible_traits_from_module_helper<'db>(
479 db: &'db dyn Database,
480 _tracked: Tracked,
481 module_id: ModuleId<'db>,
482) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
483 visible_traits_from_module(db, module_id)
484}
485
486#[salsa::tracked(returns(ref))]
488fn inline_macro_expansion_files_tracked<'db>(
489 db: &'db dyn Database,
490 _tracked: Tracked,
491 module_id: ModuleId<'db>,
492) -> Vec<FileId<'db>> {
493 module_inline_macro_expansions(db, module_id)
494 .iter()
495 .map(|expansion| expansion.file.clone().into_file_long_id(db).intern(db))
496 .collect()
497}
498
499#[salsa::accumulator]
500pub struct InlineMacroExpansionAccumulator {
501 file: FileInput,
502}
503
504pub trait LspHelpers<'db>: Database {
506 fn methods_in_module(
508 &'db self,
509 module_id: ModuleId<'db>,
510 type_filter: TypeFilter<'db>,
511 ) -> Arc<Vec<TraitFunctionId<'db>>> {
512 methods_in_module_tracked(self.as_dyn_database(), module_id, type_filter)
513 }
514 fn methods_in_crate(
516 &'db self,
517 crate_id: CrateId<'db>,
518 type_filter: TypeFilter<'db>,
519 ) -> Arc<Vec<TraitFunctionId<'db>>> {
520 methods_in_crate_tracked(self.as_dyn_database(), crate_id, type_filter)
521 }
522 fn visible_importables_from_module(
525 &'db self,
526 module_id: ModuleId<'db>,
527 ) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
528 visible_importables_from_module_tracked(self.as_dyn_database(), module_id)
529 }
530 fn visible_importables_in_module(
534 &'db self,
535 module_id: ModuleId<'db>,
536 user_module_id: ModuleId<'db>,
537 include_parent: bool,
538 ) -> Arc<Vec<(ImportableId<'db>, String)>> {
539 visible_importables_in_module_tracked(
540 self.as_dyn_database(),
541 module_id,
542 user_module_id,
543 include_parent,
544 )
545 }
546 fn visible_importables_in_crate(
549 &'db self,
550 crate_id: CrateId<'db>,
551 user_module_id: ModuleId<'db>,
552 ) -> Arc<Vec<(ImportableId<'db>, String)>> {
553 visible_importables_in_crate_tracked(self.as_dyn_database(), crate_id, user_module_id)
554 }
555 fn visible_traits_from_module(
557 &'db self,
558 module_id: ModuleId<'db>,
559 ) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
560 visible_traits_from_module_tracked(self.as_dyn_database(), module_id)
561 }
562 fn inline_macro_expansion_files(&'db self, module_id: ModuleId<'db>) -> &'db Vec<FileId<'db>> {
564 inline_macro_expansion_files_tracked(self.as_dyn_database(), (), module_id)
565 }
566 fn accumulate_inline_macro_expansion(&'db self, file: &FileLongId<'db>) {
568 let db = self.as_dyn_database();
569 InlineMacroExpansionAccumulator { file: file.into_file_input(db) }.accumulate(db);
570 }
571}
572impl<'db, T: Database + ?Sized> LspHelpers<'db> for T {}