cairo_lang_semantic/items/
module.rs

1use std::sync::Arc;
2
3use cairo_lang_defs::db::DefsGroup;
4use cairo_lang_defs::ids::{
5    GlobalUseId, LanguageElementId, LookupItemId, ModuleId, ModuleItemId, NamedLanguageElementId,
6    TraitId,
7};
8use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe};
9use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
10use cairo_lang_syntax::node::ast;
11use cairo_lang_syntax::node::helpers::UsePathEx;
12use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
13use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
14use smol_str::SmolStr;
15
16use super::feature_kind::FeatureKind;
17use super::us::SemanticUseEx;
18use super::visibility::{Visibility, peek_visible_in};
19use crate::SemanticDiagnostic;
20use crate::db::{SemanticGroup, get_resolver_data_options};
21use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnosticsBuilder};
22use crate::resolve::ResolvedGenericItem;
23
24/// Information per item in a module.
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct ModuleItemInfo {
27    pub item_id: ModuleItemId,
28    pub visibility: Visibility,
29    pub feature_kind: FeatureKind,
30}
31
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct ModuleSemanticData {
34    /// The items in the module without duplicates.
35    pub items: OrderedHashMap<SmolStr, ModuleItemInfo>,
36    pub global_uses: OrderedHashMap<GlobalUseId, Visibility>,
37    pub diagnostics: Diagnostics<SemanticDiagnostic>,
38}
39
40pub fn priv_module_semantic_data(
41    db: &dyn SemanticGroup,
42    module_id: ModuleId,
43) -> Maybe<Arc<ModuleSemanticData>> {
44    let def_db: &dyn DefsGroup = db.upcast();
45    let syntax_db = db.upcast();
46    // We use the builder here since the items can come from different file_ids.
47    let mut diagnostics = DiagnosticsBuilder::default();
48    let mut items = OrderedHashMap::default();
49    for item_id in db.module_items(module_id)?.iter().copied() {
50        let (name, attributes, visibility) = match &item_id {
51            ModuleItemId::Constant(item_id) => {
52                let item = &def_db.module_constants(module_id)?[item_id];
53                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
54            }
55            ModuleItemId::Submodule(item_id) => {
56                let item = &def_db.module_submodules(module_id)?[item_id];
57                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
58            }
59            ModuleItemId::Use(item_id) => {
60                let use_ast = &def_db.module_uses(module_id)?[item_id];
61                let item = ast::UsePath::Leaf(use_ast.clone()).get_item(syntax_db);
62                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
63            }
64            ModuleItemId::FreeFunction(item_id) => {
65                let item = &def_db.module_free_functions(module_id)?[item_id];
66                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
67            }
68            ModuleItemId::Struct(item_id) => {
69                let item = &def_db.module_structs(module_id)?[item_id];
70                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
71            }
72            ModuleItemId::Enum(item_id) => {
73                let item = &def_db.module_enums(module_id)?[item_id];
74                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
75            }
76            ModuleItemId::TypeAlias(item_id) => {
77                let item = &def_db.module_type_aliases(module_id)?[item_id];
78                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
79            }
80            ModuleItemId::ImplAlias(item_id) => {
81                let item = &def_db.module_impl_aliases(module_id)?[item_id];
82                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
83            }
84            ModuleItemId::Trait(item_id) => {
85                let item = &def_db.module_traits(module_id)?[item_id];
86                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
87            }
88            ModuleItemId::Impl(item_id) => {
89                let item = &def_db.module_impls(module_id)?[item_id];
90                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
91            }
92            ModuleItemId::ExternType(item_id) => {
93                let item = &def_db.module_extern_types(module_id)?[item_id];
94                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
95            }
96            ModuleItemId::ExternFunction(item_id) => {
97                let item = &def_db.module_extern_functions(module_id)?[item_id];
98                (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
99            }
100        };
101        let visibility = Visibility::from_ast(db.upcast(), &mut diagnostics, &visibility);
102        let feature_kind = FeatureKind::from_ast(db.upcast(), &mut diagnostics, &attributes);
103        if items
104            .insert(name.clone(), ModuleItemInfo { item_id, visibility, feature_kind })
105            .is_some()
106        {
107            // `item` is extracted from `module_items` and thus `module_item_name_stable_ptr` is
108            // guaranteed to succeed.
109            diagnostics.report(
110                db.module_item_name_stable_ptr(module_id, item_id).unwrap(),
111                SemanticDiagnosticKind::NameDefinedMultipleTimes(name.clone()),
112            );
113        }
114    }
115
116    let global_uses = db
117        .module_global_uses(module_id)?
118        .iter()
119        .map(|(global_use_id, use_path_star)| {
120            let item = ast::UsePath::Star(use_path_star.clone()).get_item(syntax_db);
121            let visibility = item.visibility(syntax_db);
122            (*global_use_id, Visibility::from_ast(db.upcast(), &mut diagnostics, &visibility))
123        })
124        .collect();
125    Ok(Arc::new(ModuleSemanticData { items, global_uses, diagnostics: diagnostics.build() }))
126}
127
128pub fn module_item_by_name(
129    db: &dyn SemanticGroup,
130    module_id: ModuleId,
131    name: SmolStr,
132) -> Maybe<Option<ModuleItemId>> {
133    let module_data = db.priv_module_semantic_data(module_id)?;
134    Ok(module_data.items.get(&name).map(|info| info.item_id))
135}
136
137pub fn module_item_info_by_name(
138    db: &dyn SemanticGroup,
139    module_id: ModuleId,
140    name: SmolStr,
141) -> Maybe<Option<ModuleItemInfo>> {
142    let module_data = db.priv_module_semantic_data(module_id)?;
143    Ok(module_data.items.get(&name).cloned())
144}
145
146/// Get the imported global uses of a module, and their visibility.
147pub fn get_module_global_uses(
148    db: &dyn SemanticGroup,
149    module_id: ModuleId,
150) -> Maybe<OrderedHashMap<GlobalUseId, Visibility>> {
151    let module_data = db.priv_module_semantic_data(module_id)?;
152    Ok(module_data.global_uses.clone())
153}
154
155/// Query implementation of [SemanticGroup::module_all_used_items].
156pub fn module_all_used_items(
157    db: &dyn SemanticGroup,
158    module_id: ModuleId,
159) -> Maybe<Arc<OrderedHashSet<LookupItemId>>> {
160    let mut all_used_items = OrderedHashSet::default();
161    let module_items = db.module_items(module_id)?;
162    for item in module_items.iter() {
163        if let Some(items) = match *item {
164            ModuleItemId::Submodule(submodule_id) => {
165                Some(db.module_all_used_items(ModuleId::Submodule(submodule_id))?)
166            }
167            ModuleItemId::Trait(trait_id) => Some(db.trait_all_used_items(trait_id)?),
168            ModuleItemId::Impl(impl_id) => Some(db.impl_all_used_items(impl_id)?),
169            _ => None,
170        } {
171            all_used_items.extend(items.iter().cloned());
172        } else {
173            for resolver_data in get_resolver_data_options(LookupItemId::ModuleItem(*item), db) {
174                all_used_items.extend(resolver_data.used_items.iter().cloned());
175            }
176        }
177    }
178    Ok(all_used_items.into())
179}
180
181/// Query implementation of [SemanticGroup::module_attributes].
182pub fn module_attributes(db: &dyn SemanticGroup, module_id: ModuleId) -> Maybe<Vec<Attribute>> {
183    Ok(match &module_id {
184        ModuleId::CrateRoot(_) => vec![],
185        ModuleId::Submodule(submodule_id) => {
186            let module_ast =
187                &db.module_submodules(submodule_id.parent_module(db.upcast()))?[submodule_id];
188            let syntax_db = db.upcast();
189
190            module_ast.attributes(syntax_db).structurize(syntax_db)
191        }
192    })
193}
194
195/// Finds all the trait ids usable in the current context, using `global use` imports.
196pub fn module_usable_trait_ids(
197    db: &dyn SemanticGroup,
198    module_id: ModuleId,
199) -> Maybe<Arc<OrderedHashMap<TraitId, LookupItemId>>> {
200    // Get the traits first from the module, do not change this order.
201    let mut module_traits = specific_module_usable_trait_ids(db, module_id, module_id)?;
202    for (user_module, containing_module) in &db.priv_module_use_star_modules(module_id).accessible {
203        if let Ok(star_module_traits) =
204            specific_module_usable_trait_ids(db, *user_module, *containing_module)
205        {
206            for (trait_id, local_item_id) in star_module_traits {
207                module_traits.entry(trait_id).or_insert(local_item_id);
208            }
209        }
210    }
211    Ok(module_traits.into())
212}
213
214/// Finds all the trait ids usable in the current context, not using `global use` imports.
215fn specific_module_usable_trait_ids(
216    db: &dyn SemanticGroup,
217    user_module: ModuleId,
218    containing_module: ModuleId,
219) -> Maybe<OrderedHashMap<TraitId, LookupItemId>> {
220    let mut module_traits: OrderedHashMap<TraitId, LookupItemId> = OrderedHashMap::default();
221    for item in db.priv_module_semantic_data(containing_module)?.items.values() {
222        if !matches!(
223            item.item_id,
224            ModuleItemId::Trait(_)
225                | ModuleItemId::Impl(_)
226                | ModuleItemId::ImplAlias(_)
227                | ModuleItemId::Use(_)
228        ) {
229            continue;
230        }
231        if !peek_visible_in(db.upcast(), item.visibility, containing_module, user_module) {
232            continue;
233        }
234        match item.item_id {
235            ModuleItemId::Trait(trait_id) => {
236                module_traits
237                    .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id)));
238            }
239            ModuleItemId::Impl(impl_def_id) => {
240                // Add traits from impls in the module.
241                let Ok(trait_id) = db.impl_def_trait(impl_def_id) else {
242                    continue;
243                };
244                module_traits
245                    .entry(trait_id)
246                    .or_insert(LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id)));
247            }
248            ModuleItemId::ImplAlias(impl_alias_id) => {
249                // Add traits from impl aliases in the module.
250                let Ok(impl_id) = db.impl_alias_impl_def(impl_alias_id) else {
251                    continue;
252                };
253                let Ok(trait_id) = db.impl_def_trait(impl_id) else {
254                    continue;
255                };
256                module_traits
257                    .entry(trait_id)
258                    .or_insert(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)));
259            }
260            ModuleItemId::Use(use_id) => {
261                // Add traits from uses in the module.
262                let Ok(resolved_item) = db.use_resolved_item(use_id) else {
263                    continue;
264                };
265                match resolved_item {
266                    // use of a trait.
267                    ResolvedGenericItem::Trait(trait_id) => {
268                        module_traits
269                            .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
270                    }
271                    // use of an impl from which we get the trait.
272                    ResolvedGenericItem::Impl(impl_def_id) => {
273                        if let Ok(trait_id) = db.impl_def_trait(impl_def_id) {
274                            module_traits
275                                .entry(trait_id)
276                                .or_insert(LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
277                        };
278                    }
279                    _ => {}
280                }
281            }
282            _ => {}
283        }
284    }
285    Ok(module_traits)
286}