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, UseId,
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            ModuleItemId::MacroDeclaration(item_id) => {
99                let item = &db.module_macro_declarations(module_id)?[item_id];
100                (item_id.name(db), item.attributes(db), item.visibility(db))
101            }
102        };
103        let visibility = Visibility::from_ast(db, &mut diagnostics, &visibility);
104        let feature_kind = FeatureKind::from_ast(db, &mut diagnostics, &attributes);
105        if items
106            .insert(name.clone(), ModuleItemInfo { item_id, visibility, feature_kind })
107            .is_some()
108        {
109            // `item` is extracted from `module_items` and thus `module_item_name_stable_ptr` is
110            // guaranteed to succeed.
111            diagnostics.report(
112                db.module_item_name_stable_ptr(module_id, item_id).unwrap(),
113                SemanticDiagnosticKind::NameDefinedMultipleTimes(name.clone()),
114            );
115        }
116    }
117
118    let global_uses = db
119        .module_global_uses(module_id)?
120        .iter()
121        .map(|(global_use_id, use_path_star)| {
122            let item = ast::UsePath::Star(use_path_star.clone()).get_item(db);
123            let visibility = item.visibility(db);
124            (*global_use_id, Visibility::from_ast(db, &mut diagnostics, &visibility))
125        })
126        .collect();
127    Ok(Arc::new(ModuleSemanticData { items, global_uses, diagnostics: diagnostics.build() }))
128}
129
130pub fn module_item_by_name(
131    db: &dyn SemanticGroup,
132    module_id: ModuleId,
133    name: SmolStr,
134) -> Maybe<Option<ModuleItemId>> {
135    let module_data = db.priv_module_semantic_data(module_id)?;
136    Ok(module_data.items.get(&name).map(|info| info.item_id))
137}
138
139pub fn module_item_info_by_name(
140    db: &dyn SemanticGroup,
141    module_id: ModuleId,
142    name: SmolStr,
143) -> Maybe<Option<ModuleItemInfo>> {
144    let module_data = db.priv_module_semantic_data(module_id)?;
145    Ok(module_data.items.get(&name).cloned())
146}
147
148/// Get the imported global uses of a module, and their visibility.
149pub fn get_module_global_uses(
150    db: &dyn SemanticGroup,
151    module_id: ModuleId,
152) -> Maybe<OrderedHashMap<GlobalUseId, Visibility>> {
153    let module_data = db.priv_module_semantic_data(module_id)?;
154    Ok(module_data.global_uses.clone())
155}
156
157/// Query implementation of [SemanticGroup::module_all_used_uses].
158pub fn module_all_used_uses(
159    db: &dyn SemanticGroup,
160    module_id: ModuleId,
161) -> Maybe<Arc<OrderedHashSet<UseId>>> {
162    let mut all_used_uses = OrderedHashSet::default();
163    let module_items = db.module_items(module_id)?;
164    for item in module_items.iter() {
165        if let Some(items) = match *item {
166            ModuleItemId::Submodule(submodule_id) => {
167                Some(db.module_all_used_uses(ModuleId::Submodule(submodule_id))?)
168            }
169            ModuleItemId::Trait(trait_id) => Some(db.trait_all_used_uses(trait_id)?),
170            ModuleItemId::Impl(impl_id) => Some(db.impl_all_used_uses(impl_id)?),
171            _ => None,
172        } {
173            all_used_uses.extend(items.iter().cloned());
174        } else {
175            for resolver_data in get_resolver_data_options(LookupItemId::ModuleItem(*item), db) {
176                all_used_uses.extend(resolver_data.used_uses.iter().cloned());
177            }
178        }
179    }
180    Ok(all_used_uses.into())
181}
182
183/// Query implementation of [SemanticGroup::module_attributes].
184pub fn module_attributes(db: &dyn SemanticGroup, module_id: ModuleId) -> Maybe<Vec<Attribute>> {
185    Ok(match &module_id {
186        ModuleId::CrateRoot(_) => vec![],
187        ModuleId::Submodule(submodule_id) => {
188            let module_ast = &db.module_submodules(submodule_id.parent_module(db))?[submodule_id];
189
190            module_ast.attributes(db).structurize(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, 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}
287
288impl HasFeatureKind for ModuleItemInfo {
289    fn feature_kind(&self) -> &FeatureKind {
290        &self.feature_kind
291    }
292}