cairo_lang_semantic/items/
module.rs1use 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#[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 };
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 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
144pub 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
153pub 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
179pub 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
191pub fn module_usable_trait_ids(
193 db: &dyn SemanticGroup,
194 module_id: ModuleId,
195) -> Maybe<Arc<OrderedHashMap<TraitId, LookupItemId>>> {
196 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
210fn 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 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 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 let Ok(resolved_item) = db.use_resolved_item(use_id) else {
259 continue;
260 };
261 match resolved_item {
262 ResolvedGenericItem::Trait(trait_id) => {
264 module_traits
265 .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
266 }
267 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}