1use cairo_lang_defs::db::DefsGroup;
2use cairo_lang_defs::ids::{
3 GlobalUseId, LanguageElementId, LookupItemId, ModuleId, ModuleItemId, NamedLanguageElementId,
4 TraitId, UseId,
5};
6use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe, MaybeAsRef};
7use cairo_lang_filesystem::db::CrateSettings;
8use cairo_lang_filesystem::ids::{SmolStrId, Tracked};
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 itertools::chain;
15use salsa::Database;
16
17use super::feature_kind::FeatureKind;
18use super::us::SemanticUseEx;
19use super::visibility::{Visibility, peek_visible_in};
20use crate::SemanticDiagnostic;
21use crate::corelib::{core_submodule, get_submodule};
22use crate::db::{SemanticGroup, get_resolver_data_options};
23use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnosticsBuilder};
24use crate::items::feature_kind::HasFeatureKind;
25use crate::items::imp::ImplSemantic;
26use crate::items::impl_alias::ImplAliasSemantic;
27use crate::items::macro_call::module_macro_modules;
28use crate::items::trt::TraitSemantic;
29use crate::items::us::{ImportInfo, UseSemantic};
30use crate::resolve::ResolvedGenericItem;
31
32#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
34pub struct ModuleItemInfo<'db> {
35 pub item_id: ModuleItemId<'db>,
36 pub visibility: Visibility,
37 pub feature_kind: FeatureKind<'db>,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
41pub struct ModuleSemanticData<'db> {
42 pub items: OrderedHashMap<SmolStrId<'db>, ModuleItemInfo<'db>>,
44 pub global_uses: OrderedHashMap<GlobalUseId<'db>, Visibility>,
45 pub diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>,
46}
47
48#[salsa::tracked(returns(ref))]
50fn priv_module_semantic_data<'db>(
51 db: &'db dyn Database,
52 _tracked: Tracked,
53 module_id: ModuleId<'db>,
54) -> Maybe<ModuleSemanticData<'db>> {
55 if let Some(data) = db.cached_crate_semantic_data(module_id.owning_crate(db)) {
56 if let Some(module_data) = data.modules_semantic_data.get(&module_id) {
57 return Ok(module_data.clone());
58 } else {
59 panic!("module not found in cached modules_data {:?}", module_id.name(db));
60 }
61 };
62 let mut diagnostics = DiagnosticsBuilder::default();
64 let mut items = OrderedHashMap::default();
65 let module_data = module_id.module_data(db)?;
66 for item_id in module_data.items(db).iter().copied() {
67 let (name, attributes, visibility) = match &item_id {
68 ModuleItemId::Constant(item_id) => {
69 let item = &module_data.constants(db)[item_id];
70 (item_id.name(db), item.attributes(db), item.visibility(db))
71 }
72 ModuleItemId::Submodule(item_id) => {
73 let item = &module_data.submodules(db)[item_id];
74 (item_id.name(db), item.attributes(db), item.visibility(db))
75 }
76 ModuleItemId::Use(item_id) => {
77 let use_ast = &module_data.uses(db)[item_id];
78 let item = ast::UsePath::Leaf(use_ast.clone()).get_item(db);
79 (item_id.name(db), item.attributes(db), item.visibility(db))
80 }
81 ModuleItemId::FreeFunction(item_id) => {
82 let item = &module_data.free_functions(db)[item_id];
83 (item_id.name(db), item.attributes(db), item.visibility(db))
84 }
85 ModuleItemId::Struct(item_id) => {
86 let item = &module_data.structs(db)[item_id];
87 (item_id.name(db), item.attributes(db), item.visibility(db))
88 }
89 ModuleItemId::Enum(item_id) => {
90 let item = &module_data.enums(db)[item_id];
91 (item_id.name(db), item.attributes(db), item.visibility(db))
92 }
93 ModuleItemId::TypeAlias(item_id) => {
94 let item = &module_data.type_aliases(db)[item_id];
95 (item_id.name(db), item.attributes(db), item.visibility(db))
96 }
97 ModuleItemId::ImplAlias(item_id) => {
98 let item = &module_data.impl_aliases(db)[item_id];
99 (item_id.name(db), item.attributes(db), item.visibility(db))
100 }
101 ModuleItemId::Trait(item_id) => {
102 let item = &module_data.traits(db)[item_id];
103 (item_id.name(db), item.attributes(db), item.visibility(db))
104 }
105 ModuleItemId::Impl(item_id) => {
106 let item = &module_data.impls(db)[item_id];
107 (item_id.name(db), item.attributes(db), item.visibility(db))
108 }
109 ModuleItemId::ExternType(item_id) => {
110 let item = &module_data.extern_types(db)[item_id];
111 (item_id.name(db), item.attributes(db), item.visibility(db))
112 }
113 ModuleItemId::ExternFunction(item_id) => {
114 let item = &module_data.extern_functions(db)[item_id];
115 (item_id.name(db), item.attributes(db), item.visibility(db))
116 }
117 ModuleItemId::MacroDeclaration(item_id) => {
118 let item = &module_data.macro_declarations(db)[item_id];
119 (item_id.name(db), item.attributes(db), item.visibility(db))
120 }
121 };
122 let visibility = Visibility::from_ast(db, &mut diagnostics, &visibility);
123 let feature_kind = FeatureKind::from_ast(db, &mut diagnostics, &attributes);
124 if items.insert(name, ModuleItemInfo { item_id, visibility, feature_kind }).is_some() {
125 diagnostics.report(
128 db.module_item_name_stable_ptr(module_id, item_id).unwrap(),
129 SemanticDiagnosticKind::NameDefinedMultipleTimes(name),
130 );
131 }
132 }
133
134 let global_uses = module_data
135 .global_uses(db)
136 .iter()
137 .map(|(global_use_id, use_path_star)| {
138 let item = ast::UsePath::Star(use_path_star.clone()).get_item(db);
139 let visibility = item.visibility(db);
140 (*global_use_id, Visibility::from_ast(db, &mut diagnostics, &visibility))
141 })
142 .collect();
143 Ok(ModuleSemanticData { items, global_uses, diagnostics: diagnostics.build() })
144}
145
146pub fn module_item_by_name<'db>(
147 db: &'db dyn Database,
148 module_id: ModuleId<'db>,
149 name: SmolStrId<'db>,
150) -> Maybe<Option<ModuleItemId<'db>>> {
151 let module_data = db.priv_module_semantic_data(module_id)?;
152 Ok(module_data.items.get(&name).map(|info| info.item_id))
153}
154
155fn module_item_by_name_tracked<'db>(
156 db: &'db dyn Database,
157 module_id: ModuleId<'db>,
158 name: SmolStrId<'db>,
159) -> Maybe<Option<ModuleItemId<'db>>> {
160 module_item_by_name_helper(db, (), module_id, name)
161}
162
163#[salsa::tracked]
164fn module_item_by_name_helper<'db>(
165 db: &'db dyn Database,
166 _tracked: Tracked,
167 module_id: ModuleId<'db>,
168 name: SmolStrId<'db>,
169) -> Maybe<Option<ModuleItemId<'db>>> {
170 module_item_by_name(db, module_id, name)
171}
172
173pub fn module_item_info_by_name<'db>(
174 db: &'db dyn Database,
175 module_id: ModuleId<'db>,
176
177 name: SmolStrId<'db>,
178) -> Maybe<Option<ModuleItemInfo<'db>>> {
179 let module_data = db.priv_module_semantic_data(module_id)?;
180 Ok(module_data.items.get(&name).cloned())
181}
182
183fn module_item_info_by_name_tracked<'db>(
184 db: &'db dyn Database,
185 module_id: ModuleId<'db>,
186 name: SmolStrId<'db>,
187) -> Maybe<Option<ModuleItemInfo<'db>>> {
188 module_item_info_by_name_helper(db, (), module_id, name)
189}
190
191#[salsa::tracked]
192fn module_item_info_by_name_helper<'db>(
193 db: &'db dyn Database,
194 _tracked: Tracked,
195 module_id: ModuleId<'db>,
196 name: SmolStrId<'db>,
197) -> Maybe<Option<ModuleItemInfo<'db>>> {
198 module_item_info_by_name(db, module_id, name)
199}
200
201pub fn get_module_global_uses<'db>(
203 db: &'db dyn Database,
204 module_id: ModuleId<'db>,
205) -> Maybe<OrderedHashMap<GlobalUseId<'db>, Visibility>> {
206 let module_data = db.priv_module_semantic_data(module_id)?;
207 Ok(module_data.global_uses.clone())
208}
209
210#[salsa::tracked(returns(ref))]
212pub fn module_all_used_uses<'db>(
213 db: &'db dyn Database,
214 _tracked: Tracked,
215 module_id: ModuleId<'db>,
216) -> Maybe<OrderedHashSet<UseId<'db>>> {
217 let mut all_used_uses = OrderedHashSet::default();
218 let module_items = module_id.module_data(db)?.items(db);
219 for item in module_items.iter() {
220 if let Some(items) = match *item {
221 ModuleItemId::Submodule(submodule_id) => {
222 Some(db.module_all_used_uses(ModuleId::Submodule(submodule_id))?)
223 }
224 ModuleItemId::Trait(trait_id) => Some(db.trait_all_used_uses(trait_id)?),
225 ModuleItemId::Impl(impl_id) => Some(db.impl_all_used_uses(impl_id)?),
226 _ => None,
227 } {
228 all_used_uses.extend(items.iter().cloned());
229 } else {
230 for resolver_data in get_resolver_data_options(LookupItemId::ModuleItem(*item), db) {
231 all_used_uses.extend(resolver_data.used_uses.iter().cloned());
232 }
233 }
234 }
235 Ok(all_used_uses)
236}
237
238#[salsa::tracked(returns(ref))]
240pub fn module_attributes<'db>(
241 db: &'db dyn Database,
242 _tracked: Tracked,
243 module_id: ModuleId<'db>,
244) -> Maybe<Vec<Attribute<'db>>> {
245 Ok(match &module_id {
246 ModuleId::CrateRoot(_) | ModuleId::MacroCall { .. } => vec![],
247 ModuleId::Submodule(submodule_id) => {
248 let module_ast = &submodule_id.module_data(db)?.submodules(db)[submodule_id];
249
250 module_ast.attributes(db).structurize(db)
251 }
252 })
253}
254
255#[salsa::tracked(returns(ref))]
258pub fn module_usable_trait_ids<'db>(
259 db: &'db dyn Database,
260 _tracked: Tracked,
261 module_id: ModuleId<'db>,
262) -> OrderedHashMap<TraitId<'db>, LookupItemId<'db>> {
263 let mut module_traits = OrderedHashMap::<TraitId<'db>, LookupItemId<'db>>::default();
264 for (containing_module, info) in db.module_imported_modules((), module_id).iter() {
265 for defined_module in
266 chain!([containing_module], module_macro_modules(db, false, *containing_module))
267 {
268 extend_specific_module_usable_trait_ids(db, info, *defined_module, &mut module_traits);
269 }
270 }
271 module_traits
272}
273
274fn extend_specific_module_usable_trait_ids<'db>(
277 db: &'db dyn Database,
278 info: &ImportInfo<'db>,
279 containing_module: ModuleId<'db>,
280 module_traits: &mut OrderedHashMap<TraitId<'db>, LookupItemId<'db>>,
281) {
282 let Ok(data) = db.priv_module_semantic_data(containing_module) else {
283 return;
284 };
285 for item in data.items.values() {
286 if !matches!(
287 item.item_id,
288 ModuleItemId::Trait(_)
289 | ModuleItemId::Impl(_)
290 | ModuleItemId::ImplAlias(_)
291 | ModuleItemId::Use(_)
292 ) {
293 continue;
294 }
295 if !info.user_modules.iter().any(|user_module_id| {
296 peek_visible_in(db, item.visibility, containing_module, *user_module_id)
297 }) {
298 continue;
299 }
300 match item.item_id {
301 ModuleItemId::Trait(trait_id) => {
302 module_traits
303 .entry(trait_id)
304 .or_insert(LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id)));
305 }
306 ModuleItemId::Impl(impl_def_id) => {
307 let Ok(trait_id) = db.impl_def_trait(impl_def_id) else {
309 continue;
310 };
311 module_traits
312 .entry(trait_id)
313 .or_insert(LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id)));
314 }
315 ModuleItemId::ImplAlias(impl_alias_id) => {
316 let Ok(impl_id) = db.impl_alias_impl_def(impl_alias_id) else {
318 continue;
319 };
320 let Ok(trait_id) = db.impl_def_trait(impl_id) else {
321 continue;
322 };
323 module_traits
324 .entry(trait_id)
325 .or_insert(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)));
326 }
327 ModuleItemId::Use(use_id) => {
328 let Ok(resolved_item) = db.use_resolved_item(use_id) else {
330 continue;
331 };
332 match resolved_item {
333 ResolvedGenericItem::Trait(trait_id) => {
335 module_traits
336 .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
337 }
338 ResolvedGenericItem::Impl(impl_def_id) => {
340 if let Ok(trait_id) = db.impl_def_trait(impl_def_id) {
341 module_traits
342 .entry(trait_id)
343 .or_insert(LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
344 };
345 }
346 _ => {}
347 }
348 }
349 _ => {}
350 }
351 }
352}
353
354impl<'db> HasFeatureKind<'db> for ModuleItemInfo<'db> {
355 fn feature_kind(&self) -> &FeatureKind<'db> {
356 &self.feature_kind
357 }
358}
359
360pub trait ModuleSemantic<'db>: Database {
362 fn priv_module_semantic_data(
364 &'db self,
365 module_id: ModuleId<'db>,
366 ) -> Maybe<&'db ModuleSemanticData<'db>> {
367 priv_module_semantic_data(self.as_dyn_database(), (), module_id).maybe_as_ref()
368 }
369 fn module_item_by_name(
372 &'db self,
373 module_id: ModuleId<'db>,
374 name: SmolStrId<'db>,
375 ) -> Maybe<Option<ModuleItemId<'db>>> {
376 module_item_by_name_tracked(self.as_dyn_database(), module_id, name)
377 }
378 fn module_item_info_by_name(
381 &'db self,
382 module_id: ModuleId<'db>,
383
384 name: SmolStrId<'db>,
385 ) -> Maybe<Option<ModuleItemInfo<'db>>> {
386 module_item_info_by_name_tracked(self.as_dyn_database(), module_id, name)
387 }
388 fn module_all_used_uses(
390 &'db self,
391 module_id: ModuleId<'db>,
392 ) -> Maybe<&'db OrderedHashSet<UseId<'db>>> {
393 module_all_used_uses(self.as_dyn_database(), (), module_id).maybe_as_ref()
394 }
395 fn module_attributes(&'db self, module_id: ModuleId<'db>) -> Maybe<&'db [Attribute<'db>]> {
397 Ok(module_attributes(self.as_dyn_database(), (), module_id).maybe_as_ref()?)
398 }
399 fn module_usable_trait_ids(
401 &'db self,
402 module_id: ModuleId<'db>,
403 ) -> &'db OrderedHashMap<TraitId<'db>, LookupItemId<'db>> {
404 module_usable_trait_ids(self.as_dyn_database(), (), module_id)
405 }
406 fn get_prelude_submodule(&'db self, settings: &CrateSettings) -> Option<ModuleId<'db>> {
408 let db = self.as_dyn_database();
409 let prelude_submodule_name = settings.edition.prelude_submodule_name(db);
410 let core_prelude_submodule = core_submodule(db, SmolStrId::from(db, "prelude"));
411 get_submodule(db, core_prelude_submodule, prelude_submodule_name)
412 }
413}
414
415impl<'db, T: Database + ?Sized> ModuleSemantic<'db> for T {}