cairo_lang_semantic/items/
module.rs

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