1use std::sync::Arc;
2
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5 GenericTypeId, ImportableId, LanguageElementId, ModuleId, ModuleItemId, NamedLanguageElementId,
6 TraitFunctionId, TraitId,
7};
8use cairo_lang_filesystem::db::{CORELIB_CRATE_NAME, FilesGroup, default_crate_settings};
9use cairo_lang_filesystem::ids::{CrateId, CrateLongId, FileId, SmolStrId, Tracked};
10use cairo_lang_syntax::node::ast::ItemModule;
11use cairo_lang_syntax::node::helpers::GetIdentifier;
12use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
13use cairo_lang_utils::Intern;
14use cairo_lang_utils::ordered_hash_map::{Entry, OrderedHashMap};
15use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
16use itertools::chain;
17use salsa::Database;
18
19use crate::expr::inference::InferenceId;
20use crate::items::constant::ConstantSemantic;
21use crate::items::enm::EnumSemantic;
22use crate::items::free_function::FreeFunctionSemantic;
23use crate::items::functions::GenericFunctionId;
24use crate::items::imp::ImplSemantic;
25use crate::items::macro_call::{MacroCallSemantic, module_macro_modules};
26use crate::items::module::ModuleSemantic;
27use crate::items::module_type_alias::ModuleTypeAliasSemantic;
28use crate::items::trt::TraitSemantic;
29use crate::items::us::SemanticUseEx;
30use crate::keyword::SELF_PARAM_KW;
31use crate::resolve::{ResolvedGenericItem, Resolver};
32use crate::types::TypeHead;
33use crate::{Variant, corelib};
34
35#[derive(Clone, Debug, Hash, PartialEq, Eq)]
37pub enum TypeFilter<'db> {
38 NoFilter,
40 TypeHead(TypeHead<'db>),
42}
43
44pub fn methods_in_module<'db>(
46 db: &'db dyn Database,
47 module_id: ModuleId<'db>,
48 type_filter: TypeFilter<'db>,
49) -> Arc<Vec<TraitFunctionId<'db>>> {
50 let mut result = Vec::new();
51 let Ok(module_traits_ids) = db.module_traits_ids(module_id) else {
52 return result.into();
53 };
54 for trait_id in module_traits_ids.iter().copied() {
55 if let Ok(trait_functions) = db.trait_functions(trait_id) {
56 for trait_function in trait_functions.values() {
57 let Ok(signature) = db.trait_function_signature(*trait_function) else {
58 continue;
59 };
60 let Some(first_param) = signature.params.first() else {
61 continue;
62 };
63 if first_param.name.long(db) != SELF_PARAM_KW {
64 continue;
65 }
66 if let TypeFilter::TypeHead(type_head) = &type_filter
67 && let Some(head) = first_param.ty.head(db)
68 && !fit_for_method(&head, type_head)
69 {
70 continue;
71 }
72
73 result.push(*trait_function)
74 }
75 }
76 }
77 Arc::new(result)
78}
79
80#[salsa::tracked]
82pub fn methods_in_module_tracked<'db>(
83 db: &'db dyn Database,
84 module_id: ModuleId<'db>,
85 type_filter: TypeFilter<'db>,
86) -> Arc<Vec<TraitFunctionId<'db>>> {
87 methods_in_module(db, module_id, type_filter)
88}
89
90fn fit_for_method(head: &TypeHead<'_>, type_head: &TypeHead<'_>) -> bool {
92 if let TypeHead::Generic(_) = head {
94 return true;
95 }
96 if head == type_head {
97 return true;
98 }
99 if let TypeHead::Snapshot(snapshot_head) = head {
100 return fit_for_method(snapshot_head.as_ref(), type_head);
101 }
102 false
103}
104
105pub fn methods_in_crate<'db>(
107 db: &'db dyn Database,
108 crate_id: CrateId<'db>,
109 type_filter: TypeFilter<'db>,
110) -> Arc<Vec<TraitFunctionId<'db>>> {
111 let mut result = Vec::new();
112 for module_id in db.crate_modules(crate_id).iter() {
113 result.extend_from_slice(&db.methods_in_module(*module_id, type_filter.clone())[..])
114 }
115 Arc::new(result)
116}
117
118#[salsa::tracked]
120pub fn methods_in_crate_tracked<'db>(
121 db: &'db dyn Database,
122 crate_id: CrateId<'db>,
123 type_filter: TypeFilter<'db>,
124) -> Arc<Vec<TraitFunctionId<'db>>> {
125 methods_in_crate(db, crate_id, type_filter)
126}
127
128pub fn visible_importables_in_module<'db>(
130 db: &'db dyn Database,
131 module_id: ModuleId<'db>,
132 user_module_id: ModuleId<'db>,
133 include_parent: bool,
134) -> Arc<Vec<(ImportableId<'db>, String)>> {
135 let mut visited_modules = UnorderedHashSet::default();
136 visible_importables_in_module_ex(
137 db,
138 module_id,
139 user_module_id,
140 include_parent,
141 &mut visited_modules,
142 )
143 .unwrap_or_else(|| Arc::new(Vec::new()))
144}
145
146#[salsa::tracked]
148pub fn visible_importables_in_module_tracked<'db>(
149 db: &'db dyn Database,
150 module_id: ModuleId<'db>,
151 user_module_id: ModuleId<'db>,
152 include_parent: bool,
153) -> Arc<Vec<(ImportableId<'db>, String)>> {
154 visible_importables_in_module(db, module_id, user_module_id, include_parent)
155}
156
157fn visible_importables_in_module_ex<'db>(
160 db: &'db dyn Database,
161 module_id: ModuleId<'db>,
162 user_module_id: ModuleId<'db>,
163 include_parent: bool,
164 visited_modules: &mut UnorderedHashSet<ModuleId<'db>>,
165) -> Option<Arc<Vec<(ImportableId<'db>, String)>>> {
166 let mut result = Vec::new();
167 if visited_modules.contains(&module_id) {
168 return Some(result.into());
169 }
170
171 let resolver = Resolver::new(db, user_module_id, InferenceId::NoContext);
172
173 let is_visible = |item_name: SmolStrId<'_>| {
175 let item_info = db.module_item_info_by_name(module_id, item_name).ok()??;
176 Some(resolver.is_item_visible(module_id, &item_info, user_module_id))
177 };
178 visited_modules.insert(module_id);
179 let mut modules_to_visit = vec![];
180 for use_id in db.module_uses_ids(module_id).unwrap_or_default().iter().copied() {
182 if !is_visible(use_id.name(db)).unwrap_or_default() {
183 continue;
184 }
185 let Ok(resolved_item) = db.use_resolved_item(use_id) else {
186 continue;
187 };
188
189 let (resolved_item, name) = match resolved_item {
190 ResolvedGenericItem::Module(ModuleId::CrateRoot(crate_id)) => {
191 result.extend_from_slice(&db.visible_importables_in_crate(crate_id, module_id)[..]);
192
193 (ImportableId::Crate(crate_id), crate_id.long(db).name().long(db))
194 }
195 ResolvedGenericItem::Module(inner_module_id @ ModuleId::Submodule(module)) => {
196 modules_to_visit.push(inner_module_id);
197
198 (ImportableId::Submodule(module), module.name(db).long(db))
199 }
200 ResolvedGenericItem::Module(ModuleId::MacroCall { .. }) => {
201 continue;
202 }
203 ResolvedGenericItem::GenericConstant(item_id) => {
204 (ImportableId::Constant(item_id), item_id.name(db).long(db))
205 }
206 ResolvedGenericItem::GenericFunction(GenericFunctionId::Free(item_id)) => {
207 (ImportableId::FreeFunction(item_id), item_id.name(db).long(db))
208 }
209 ResolvedGenericItem::GenericFunction(GenericFunctionId::Extern(item_id)) => {
210 (ImportableId::ExternFunction(item_id), item_id.name(db).long(db))
211 }
212 ResolvedGenericItem::GenericType(GenericTypeId::Struct(item_id)) => {
213 (ImportableId::Struct(item_id), item_id.name(db).long(db))
214 }
215 ResolvedGenericItem::GenericType(GenericTypeId::Enum(item_id)) => {
216 let enum_name = item_id.name(db);
217
218 if let Ok(variants) = db.enum_variants(item_id) {
219 for (name, id) in variants.iter() {
220 result.push((
221 ImportableId::Variant(*id),
222 format!("{}::{}", enum_name.long(db), name.long(db)),
223 ));
224 }
225 }
226
227 (ImportableId::Enum(item_id), enum_name.long(db))
228 }
229 ResolvedGenericItem::GenericType(GenericTypeId::Extern(item_id)) => {
230 (ImportableId::ExternType(item_id), item_id.name(db).long(db))
231 }
232 ResolvedGenericItem::GenericTypeAlias(item_id) => {
233 (ImportableId::TypeAlias(item_id), item_id.name(db).long(db))
234 }
235 ResolvedGenericItem::GenericImplAlias(item_id) => {
236 (ImportableId::ImplAlias(item_id), item_id.name(db).long(db))
237 }
238 ResolvedGenericItem::Variant(Variant { id, .. }) => {
239 (ImportableId::Variant(id), id.name(db).long(db))
240 }
241 ResolvedGenericItem::Trait(item_id) => {
242 (ImportableId::Trait(item_id), item_id.name(db).long(db))
243 }
244 ResolvedGenericItem::Impl(item_id) => {
245 (ImportableId::Impl(item_id), item_id.name(db).long(db))
246 }
247 ResolvedGenericItem::Macro(item_id) => {
248 (ImportableId::MacroDeclaration(item_id), item_id.name(db).long(db))
249 }
250 ResolvedGenericItem::Variable(_)
251 | ResolvedGenericItem::TraitItem(_)
252 | ResolvedGenericItem::GenericFunction(GenericFunctionId::Impl(_)) => continue,
253 };
254
255 result.push((resolved_item, name.to_string()));
256 }
257
258 if !matches!(module_id, ModuleId::MacroCall { .. }) {
259 modules_to_visit.extend(module_macro_modules(db, false, module_id).iter().copied());
260 }
261
262 for submodule_id in db.module_submodules_ids(module_id).unwrap_or_default().iter().copied() {
263 if !is_visible(submodule_id.name(db)).unwrap_or_default() {
264 continue;
265 }
266 result.push((ImportableId::Submodule(submodule_id), submodule_id.name(db).to_string(db)));
267 modules_to_visit.push(ModuleId::Submodule(submodule_id));
268 }
269
270 for enum_id in db.module_enums_ids(module_id).unwrap_or_default().iter().copied() {
272 let enum_name = enum_id.name(db);
273 if !is_visible(enum_name).unwrap_or_default() {
274 continue;
275 }
276
277 result.push((ImportableId::Enum(enum_id), enum_name.to_string(db)));
278
279 if let Ok(variants) = db.enum_variants(enum_id) {
280 for (name, id) in variants.iter() {
281 result.push((
282 ImportableId::Variant(*id),
283 format!("{}::{}", enum_name.long(db), name.long(db)),
284 ));
285 }
286 }
287 }
288
289 macro_rules! module_importables {
290 ($query:ident, $map:expr) => {
291 for item_id in db.$query(module_id).ok().unwrap_or_default().iter().copied() {
292 if !is_visible(item_id.name(db)).unwrap_or_default() {
293 continue;
294 }
295 result.push(($map(item_id), item_id.name(db).to_string(db)));
296 }
297 };
298 }
299
300 module_importables!(module_constants_ids, ImportableId::Constant);
301 module_importables!(module_free_functions_ids, ImportableId::FreeFunction);
302 module_importables!(module_structs_ids, ImportableId::Struct);
303 module_importables!(module_type_aliases_ids, ImportableId::TypeAlias);
304 module_importables!(module_impl_aliases_ids, ImportableId::ImplAlias);
305 module_importables!(module_traits_ids, ImportableId::Trait);
306 module_importables!(module_impls_ids, ImportableId::Impl);
307 module_importables!(module_extern_functions_ids, ImportableId::ExternFunction);
308 module_importables!(module_extern_types_ids, ImportableId::ExternType);
309 module_importables!(module_macro_declarations_ids, ImportableId::MacroDeclaration);
310
311 for submodule in modules_to_visit {
312 for (item_id, path) in
313 visible_importables_in_module_ex(db, submodule, user_module_id, false, visited_modules)
314 .unwrap_or_default()
315 .iter()
316 {
317 result.push((*item_id, format!("{}::{}", submodule.name(db).long(db), path)));
318 }
319 }
320 if include_parent {
322 match module_id {
323 ModuleId::CrateRoot(_) => {}
324 ModuleId::Submodule(submodule_id) => {
325 let parent_module_id = submodule_id.parent_module(db);
326 for (item_id, path) in visible_importables_in_module_ex(
327 db,
328 parent_module_id,
329 user_module_id,
330 include_parent,
331 visited_modules,
332 )
333 .unwrap_or_default()
334 .iter()
335 {
336 result.push((*item_id, format!("super::{path}")));
337 }
338 }
339 ModuleId::MacroCall { .. } => {}
340 }
341 }
342 Some(Arc::new(result))
343}
344
345pub fn visible_importables_in_crate<'db>(
347 db: &'db dyn Database,
348 crate_id: CrateId<'db>,
349 user_module_id: ModuleId<'db>,
350) -> Arc<Vec<(ImportableId<'db>, String)>> {
351 let is_current_crate = user_module_id.owning_crate(db) == crate_id;
352 let crate_name = if is_current_crate { "crate" } else { crate_id.long(db).name().long(db) };
353 let crate_as_module = ModuleId::CrateRoot(crate_id);
354 db.visible_importables_in_module(crate_as_module, user_module_id, false)
355 .iter()
356 .map(|(item_id, path)| (*item_id, format!("{crate_name}::{path}")))
357 .collect::<Vec<_>>()
358 .into()
359}
360
361#[salsa::tracked]
363pub fn visible_importables_in_crate_tracked<'db>(
364 db: &'db dyn Database,
365 crate_id: CrateId<'db>,
366 user_module_id: ModuleId<'db>,
367) -> Arc<Vec<(ImportableId<'db>, String)>> {
368 visible_importables_in_crate(db, crate_id, user_module_id)
369}
370
371pub fn visible_importables_from_module<'db>(
373 db: &'db dyn Database,
374 module_id: ModuleId<'db>,
375) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
376 let current_crate_id = module_id.owning_crate(db);
377 let prelude_submodule =
378 db.get_prelude_submodule(&db.crate_config(current_crate_id)?.settings)?;
379
380 let mut module_visible_importables = Vec::new();
381 module_visible_importables.extend_from_slice(
383 &db.visible_importables_in_module(prelude_submodule, prelude_submodule, false)[..],
384 );
385 let settings = db
387 .crate_config(current_crate_id)
388 .map(|c| &c.settings)
389 .unwrap_or_else(|| default_crate_settings(db));
390 for crate_id in chain!(
391 [current_crate_id],
392 (!settings.dependencies.contains_key(CORELIB_CRATE_NAME)).then(|| corelib::core_crate(db)),
393 settings.dependencies.iter().map(|(name, setting)| {
394 CrateLongId::Real {
395 name: SmolStrId::from(db, name.clone()),
396 discriminator: setting.discriminator.clone(),
397 }
398 .intern(db)
399 })
400 ) {
401 module_visible_importables
402 .extend_from_slice(&db.visible_importables_in_crate(crate_id, module_id)[..]);
403 module_visible_importables
404 .push((ImportableId::Crate(crate_id), crate_id.long(db).name().to_string(db)));
405 }
406
407 module_visible_importables
409 .extend_from_slice(&db.visible_importables_in_module(module_id, module_id, true)[..]);
410
411 let mut result: OrderedHashMap<ImportableId<'_>, String> = OrderedHashMap::default();
416 for (trait_id, path) in module_visible_importables {
417 match result.entry(trait_id) {
418 Entry::Occupied(existing_path) => {
419 if path.split("::").count() < existing_path.get().split("::").count() {
420 *existing_path.into_mut() = path;
421 }
422 }
423 Entry::Vacant(entry) => {
424 entry.insert(path);
425 }
426 }
427 }
428 Some(Arc::new(result))
429}
430
431pub fn visible_importables_from_module_tracked<'db>(
433 db: &'db dyn Database,
434 module_id: ModuleId<'db>,
435) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
436 visible_importables_from_module_helper(db, (), module_id)
437}
438
439#[salsa::tracked]
440fn visible_importables_from_module_helper<'db>(
441 db: &'db dyn Database,
442 _tracked: Tracked,
443 module_id: ModuleId<'db>,
444) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
445 visible_importables_from_module(db, module_id)
446}
447
448pub fn visible_traits_from_module<'db>(
450 db: &'db dyn Database,
451 module_id: ModuleId<'db>,
452) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
453 let importables = db.visible_importables_from_module(module_id)?;
454
455 let traits = importables
456 .iter()
457 .filter_map(|(item, path)| {
458 if let ImportableId::Trait(trait_id) = item {
459 Some((*trait_id, path.clone()))
460 } else {
461 None
462 }
463 })
464 .collect::<OrderedHashMap<_, _>>()
465 .into();
466
467 Some(traits)
468}
469
470fn visible_traits_from_module_tracked<'db>(
472 db: &'db dyn Database,
473 module_id: ModuleId<'db>,
474) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
475 visible_traits_from_module_helper(db, (), module_id)
476}
477
478#[salsa::tracked]
479fn visible_traits_from_module_helper<'db>(
480 db: &'db dyn Database,
481 _tracked: Tracked,
482 module_id: ModuleId<'db>,
483) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
484 visible_traits_from_module(db, module_id)
485}
486
487#[salsa::tracked(returns(ref))]
489fn inline_macro_expansion_files_tracked<'db>(
490 db: &'db dyn Database,
491 _tracked: Tracked,
492 module_id: ModuleId<'db>,
493) -> Vec<FileId<'db>> {
494 let mut files = vec![];
495 if let Ok(module_macro_calls_ids) = db.module_macro_calls_ids(module_id) {
496 for macro_call in module_macro_calls_ids {
497 if let Ok(macro_call_data) = db.priv_macro_call_data(*macro_call)
498 && let Ok(ModuleId::MacroCall { generated_file_id, .. }) =
499 macro_call_data.macro_call_module
500 {
501 files.push(generated_file_id);
502 }
503 }
504 }
505 if let Ok(traits) = db.module_traits_ids(module_id) {
506 for trait_id in traits {
507 if let Ok(trait_functions) = db.trait_functions(*trait_id) {
508 for trait_function_id in trait_functions.values() {
509 if let Ok(Some(data)) = db.trait_function_body_resolver_data(*trait_function_id)
510 {
511 files.extend(data.files.iter().copied())
512 }
513 }
514 }
515 }
516 }
517 if let Ok(impls) = db.module_impls_ids(module_id) {
518 for impls_id in impls {
519 if let Ok(impl_functions) = db.impl_functions(*impls_id) {
520 for impl_function_id in impl_functions.values() {
521 if let Ok(data) = db.impl_function_body_resolver_data(*impl_function_id) {
522 files.extend(data.files.iter().copied())
523 }
524 }
525 }
526 }
527 }
528 if let Ok(free_functions) = db.module_free_functions_ids(module_id) {
529 for free_function_id in free_functions {
530 if let Ok(data) = db.free_function_body_resolver_data(*free_function_id) {
531 files.extend(data.files.iter().copied())
532 }
533 }
534 }
535 if let Ok(constants) = db.module_constants_ids(module_id) {
536 for constant_id in constants {
537 if let Ok(data) = db.constant_resolver_data(*constant_id) {
538 files.extend(data.files.iter().copied())
539 }
540 }
541 }
542 if let Ok(type_aliases) = db.module_type_aliases_ids(module_id) {
543 for type_alias_id in type_aliases {
544 if let Ok(data) = db.module_type_alias_resolver_data(*type_alias_id) {
545 files.extend(data.files.iter().copied())
546 }
547 }
548 }
549
550 files
551}
552
553#[salsa::tracked]
554fn find_module_containing_node<'db>(
555 db: &'db dyn Database,
556 _tracked: Tracked,
557 node: SyntaxNode<'db>,
558) -> Option<ModuleId<'db>> {
559 let main_module = {
562 let node_file_id = node.stable_ptr(db).file_id(db);
565
566 *db.file_modules(node_file_id).ok()?.first()?
568 };
569
570 node.ancestors(db)
573 .filter_map(|node| ItemModule::cast(db, node))
574 .map(|item_module| {
575 item_module
576 .stable_ptr(db)
577 .name_green(db)
578 .identifier(db)
579 })
580 .collect::<Vec<_>>()
582 .into_iter()
583 .try_rfold(main_module, |module, name| {
585 let ModuleItemId::Submodule(submodule) =
586 db.module_item_by_name(module, name).ok()??
587 else {
588 return None;
589 };
590 Some(ModuleId::Submodule(submodule))
591 })
592}
593
594pub trait LspHelpers<'db>: Database {
596 fn methods_in_module(
598 &'db self,
599 module_id: ModuleId<'db>,
600 type_filter: TypeFilter<'db>,
601 ) -> Arc<Vec<TraitFunctionId<'db>>> {
602 methods_in_module_tracked(self.as_dyn_database(), module_id, type_filter)
603 }
604 fn methods_in_crate(
606 &'db self,
607 crate_id: CrateId<'db>,
608 type_filter: TypeFilter<'db>,
609 ) -> Arc<Vec<TraitFunctionId<'db>>> {
610 methods_in_crate_tracked(self.as_dyn_database(), crate_id, type_filter)
611 }
612 fn visible_importables_from_module(
615 &'db self,
616 module_id: ModuleId<'db>,
617 ) -> Option<Arc<OrderedHashMap<ImportableId<'db>, String>>> {
618 visible_importables_from_module_tracked(self.as_dyn_database(), module_id)
619 }
620 fn visible_importables_in_module(
624 &'db self,
625 module_id: ModuleId<'db>,
626 user_module_id: ModuleId<'db>,
627 include_parent: bool,
628 ) -> Arc<Vec<(ImportableId<'db>, String)>> {
629 visible_importables_in_module_tracked(
630 self.as_dyn_database(),
631 module_id,
632 user_module_id,
633 include_parent,
634 )
635 }
636 fn visible_importables_in_crate(
639 &'db self,
640 crate_id: CrateId<'db>,
641 user_module_id: ModuleId<'db>,
642 ) -> Arc<Vec<(ImportableId<'db>, String)>> {
643 visible_importables_in_crate_tracked(self.as_dyn_database(), crate_id, user_module_id)
644 }
645 fn visible_traits_from_module(
647 &'db self,
648 module_id: ModuleId<'db>,
649 ) -> Option<Arc<OrderedHashMap<TraitId<'db>, String>>> {
650 visible_traits_from_module_tracked(self.as_dyn_database(), module_id)
651 }
652 fn inline_macro_expansion_files(&'db self, module_id: ModuleId<'db>) -> &'db Vec<FileId<'db>> {
654 inline_macro_expansion_files_tracked(self.as_dyn_database(), (), module_id)
655 }
656
657 fn find_module_containing_node(&'db self, node: SyntaxNode<'db>) -> Option<ModuleId<'db>> {
659 find_module_containing_node(self.as_dyn_database(), (), node)
660 }
661}
662impl<'db, T: Database + ?Sized> LspHelpers<'db> for T {}