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::items::feature_kind::HasFeatureKind;
23use crate::resolve::ResolvedGenericItem;
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct ModuleItemInfo {
28 pub item_id: ModuleItemId,
29 pub visibility: Visibility,
30 pub feature_kind: FeatureKind,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct ModuleSemanticData {
35 pub items: OrderedHashMap<SmolStr, ModuleItemInfo>,
37 pub global_uses: OrderedHashMap<GlobalUseId, Visibility>,
38 pub diagnostics: Diagnostics<SemanticDiagnostic>,
39}
40
41pub fn priv_module_semantic_data(
42 db: &dyn SemanticGroup,
43 module_id: ModuleId,
44) -> Maybe<Arc<ModuleSemanticData>> {
45 let def_db: &dyn DefsGroup = db.upcast();
46 let syntax_db = db.upcast();
47 let mut diagnostics = DiagnosticsBuilder::default();
49 let mut items = OrderedHashMap::default();
50 for item_id in db.module_items(module_id)?.iter().copied() {
51 let (name, attributes, visibility) = match &item_id {
52 ModuleItemId::Constant(item_id) => {
53 let item = &def_db.module_constants(module_id)?[item_id];
54 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
55 }
56 ModuleItemId::Submodule(item_id) => {
57 let item = &def_db.module_submodules(module_id)?[item_id];
58 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
59 }
60 ModuleItemId::Use(item_id) => {
61 let use_ast = &def_db.module_uses(module_id)?[item_id];
62 let item = ast::UsePath::Leaf(use_ast.clone()).get_item(syntax_db);
63 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
64 }
65 ModuleItemId::FreeFunction(item_id) => {
66 let item = &def_db.module_free_functions(module_id)?[item_id];
67 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
68 }
69 ModuleItemId::Struct(item_id) => {
70 let item = &def_db.module_structs(module_id)?[item_id];
71 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
72 }
73 ModuleItemId::Enum(item_id) => {
74 let item = &def_db.module_enums(module_id)?[item_id];
75 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
76 }
77 ModuleItemId::TypeAlias(item_id) => {
78 let item = &def_db.module_type_aliases(module_id)?[item_id];
79 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
80 }
81 ModuleItemId::ImplAlias(item_id) => {
82 let item = &def_db.module_impl_aliases(module_id)?[item_id];
83 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
84 }
85 ModuleItemId::Trait(item_id) => {
86 let item = &def_db.module_traits(module_id)?[item_id];
87 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
88 }
89 ModuleItemId::Impl(item_id) => {
90 let item = &def_db.module_impls(module_id)?[item_id];
91 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
92 }
93 ModuleItemId::ExternType(item_id) => {
94 let item = &def_db.module_extern_types(module_id)?[item_id];
95 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
96 }
97 ModuleItemId::ExternFunction(item_id) => {
98 let item = &def_db.module_extern_functions(module_id)?[item_id];
99 (item_id.name(def_db), item.attributes(syntax_db), item.visibility(syntax_db))
100 }
101 };
102 let visibility = Visibility::from_ast(db.upcast(), &mut diagnostics, &visibility);
103 let feature_kind = FeatureKind::from_ast(db.upcast(), &mut diagnostics, &attributes);
104 if items
105 .insert(name.clone(), ModuleItemInfo { item_id, visibility, feature_kind })
106 .is_some()
107 {
108 diagnostics.report(
111 db.module_item_name_stable_ptr(module_id, item_id).unwrap(),
112 SemanticDiagnosticKind::NameDefinedMultipleTimes(name.clone()),
113 );
114 }
115 }
116
117 let global_uses = db
118 .module_global_uses(module_id)?
119 .iter()
120 .map(|(global_use_id, use_path_star)| {
121 let item = ast::UsePath::Star(use_path_star.clone()).get_item(syntax_db);
122 let visibility = item.visibility(syntax_db);
123 (*global_use_id, Visibility::from_ast(db.upcast(), &mut diagnostics, &visibility))
124 })
125 .collect();
126 Ok(Arc::new(ModuleSemanticData { items, global_uses, diagnostics: diagnostics.build() }))
127}
128
129pub fn module_item_by_name(
130 db: &dyn SemanticGroup,
131 module_id: ModuleId,
132 name: SmolStr,
133) -> Maybe<Option<ModuleItemId>> {
134 let module_data = db.priv_module_semantic_data(module_id)?;
135 Ok(module_data.items.get(&name).map(|info| info.item_id))
136}
137
138pub fn module_item_info_by_name(
139 db: &dyn SemanticGroup,
140 module_id: ModuleId,
141 name: SmolStr,
142) -> Maybe<Option<ModuleItemInfo>> {
143 let module_data = db.priv_module_semantic_data(module_id)?;
144 Ok(module_data.items.get(&name).cloned())
145}
146
147pub fn get_module_global_uses(
149 db: &dyn SemanticGroup,
150 module_id: ModuleId,
151) -> Maybe<OrderedHashMap<GlobalUseId, Visibility>> {
152 let module_data = db.priv_module_semantic_data(module_id)?;
153 Ok(module_data.global_uses.clone())
154}
155
156pub fn module_all_used_items(
158 db: &dyn SemanticGroup,
159 module_id: ModuleId,
160) -> Maybe<Arc<OrderedHashSet<LookupItemId>>> {
161 let mut all_used_items = OrderedHashSet::default();
162 let module_items = db.module_items(module_id)?;
163 for item in module_items.iter() {
164 if let Some(items) = match *item {
165 ModuleItemId::Submodule(submodule_id) => {
166 Some(db.module_all_used_items(ModuleId::Submodule(submodule_id))?)
167 }
168 ModuleItemId::Trait(trait_id) => Some(db.trait_all_used_items(trait_id)?),
169 ModuleItemId::Impl(impl_id) => Some(db.impl_all_used_items(impl_id)?),
170 _ => None,
171 } {
172 all_used_items.extend(items.iter().cloned());
173 } else {
174 for resolver_data in get_resolver_data_options(LookupItemId::ModuleItem(*item), db) {
175 all_used_items.extend(resolver_data.used_items.iter().cloned());
176 }
177 }
178 }
179 Ok(all_used_items.into())
180}
181
182pub fn module_attributes(db: &dyn SemanticGroup, module_id: ModuleId) -> Maybe<Vec<Attribute>> {
184 Ok(match &module_id {
185 ModuleId::CrateRoot(_) => vec![],
186 ModuleId::Submodule(submodule_id) => {
187 let module_ast =
188 &db.module_submodules(submodule_id.parent_module(db.upcast()))?[submodule_id];
189 let syntax_db = db.upcast();
190
191 module_ast.attributes(syntax_db).structurize(syntax_db)
192 }
193 })
194}
195
196pub fn module_usable_trait_ids(
198 db: &dyn SemanticGroup,
199 module_id: ModuleId,
200) -> Maybe<Arc<OrderedHashMap<TraitId, LookupItemId>>> {
201 let mut module_traits = specific_module_usable_trait_ids(db, module_id, module_id)?;
203 for (user_module, containing_module) in &db.priv_module_use_star_modules(module_id).accessible {
204 if let Ok(star_module_traits) =
205 specific_module_usable_trait_ids(db, *user_module, *containing_module)
206 {
207 for (trait_id, local_item_id) in star_module_traits {
208 module_traits.entry(trait_id).or_insert(local_item_id);
209 }
210 }
211 }
212 Ok(module_traits.into())
213}
214
215fn specific_module_usable_trait_ids(
217 db: &dyn SemanticGroup,
218 user_module: ModuleId,
219 containing_module: ModuleId,
220) -> Maybe<OrderedHashMap<TraitId, LookupItemId>> {
221 let mut module_traits: OrderedHashMap<TraitId, LookupItemId> = OrderedHashMap::default();
222 for item in db.priv_module_semantic_data(containing_module)?.items.values() {
223 if !matches!(
224 item.item_id,
225 ModuleItemId::Trait(_)
226 | ModuleItemId::Impl(_)
227 | ModuleItemId::ImplAlias(_)
228 | ModuleItemId::Use(_)
229 ) {
230 continue;
231 }
232 if !peek_visible_in(db.upcast(), item.visibility, containing_module, user_module) {
233 continue;
234 }
235 match item.item_id {
236 ModuleItemId::Trait(trait_id) => {
237 module_traits
238 .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id)));
239 }
240 ModuleItemId::Impl(impl_def_id) => {
241 let Ok(trait_id) = db.impl_def_trait(impl_def_id) else {
243 continue;
244 };
245 module_traits
246 .entry(trait_id)
247 .or_insert(LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id)));
248 }
249 ModuleItemId::ImplAlias(impl_alias_id) => {
250 let Ok(impl_id) = db.impl_alias_impl_def(impl_alias_id) else {
252 continue;
253 };
254 let Ok(trait_id) = db.impl_def_trait(impl_id) else {
255 continue;
256 };
257 module_traits
258 .entry(trait_id)
259 .or_insert(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)));
260 }
261 ModuleItemId::Use(use_id) => {
262 let Ok(resolved_item) = db.use_resolved_item(use_id) else {
264 continue;
265 };
266 match resolved_item {
267 ResolvedGenericItem::Trait(trait_id) => {
269 module_traits
270 .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
271 }
272 ResolvedGenericItem::Impl(impl_def_id) => {
274 if let Ok(trait_id) = db.impl_def_trait(impl_def_id) {
275 module_traits
276 .entry(trait_id)
277 .or_insert(LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
278 };
279 }
280 _ => {}
281 }
282 }
283 _ => {}
284 }
285 }
286 Ok(module_traits)
287}
288
289impl HasFeatureKind for ModuleItemInfo {
290 fn feature_kind(&self) -> &FeatureKind {
291 &self.feature_kind
292 }
293}