cairo_lang_semantic/items/
module.rs1use 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#[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 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 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 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
148pub 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
157pub 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
183pub 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
195pub fn module_usable_trait_ids(
197 db: &dyn SemanticGroup,
198 module_id: ModuleId,
199) -> Maybe<Arc<OrderedHashMap<TraitId, LookupItemId>>> {
200 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
214fn 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 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 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 let Ok(resolved_item) = db.use_resolved_item(use_id) else {
263 continue;
264 };
265 match resolved_item {
266 ResolvedGenericItem::Trait(trait_id) => {
268 module_traits
269 .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
270 }
271 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}