cairo_lang_semantic/items/
module.rs1use 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#[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 def_db: &dyn DefsGroup = db.upcast();
45 let syntax_db = db.upcast();
46 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 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
146pub 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
155pub 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
181pub 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
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.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 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}