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