cairo_lang_semantic/items/
us.rs1use std::sync::Arc;
2
3use cairo_lang_defs::ids::{
4 GlobalUseId, LanguageElementId, LookupItemId, ModuleId, ModuleItemId, UseId,
5};
6use cairo_lang_diagnostics::{Diagnostics, Maybe, ToMaybe};
7use cairo_lang_proc_macros::DebugWithDb;
8use cairo_lang_syntax::node::db::SyntaxGroup;
9use cairo_lang_syntax::node::helpers::{GetIdentifier, UsePathEx};
10use cairo_lang_syntax::node::kind::SyntaxKind;
11use cairo_lang_syntax::node::{TypedSyntaxNode, ast};
12use cairo_lang_utils::Upcast;
13use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
14use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
15
16use super::module::get_module_global_uses;
17use super::visibility::peek_visible_in;
18use crate::SemanticDiagnostic;
19use crate::db::SemanticGroup;
20use crate::diagnostic::SemanticDiagnosticKind::*;
21use crate::diagnostic::{
22 ElementKind, NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder,
23};
24use crate::expr::inference::InferenceId;
25use crate::keyword::SELF_PARAM_KW;
26use crate::resolve::{ResolutionContext, ResolvedGenericItem, Resolver, ResolverData};
27
28#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
29#[debug_db(dyn SemanticGroup + 'static)]
30pub struct UseData {
31 diagnostics: Diagnostics<SemanticDiagnostic>,
32 resolved_item: Maybe<ResolvedGenericItem>,
33 resolver_data: Arc<ResolverData>,
34}
35
36pub fn priv_use_semantic_data(db: &dyn SemanticGroup, use_id: UseId) -> Maybe<UseData> {
38 let module_file_id = use_id.module_file_id(db);
39 let mut diagnostics = SemanticDiagnostics::default();
40 let module_item_id = ModuleItemId::Use(use_id);
41 let inference_id = InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(module_item_id));
42 let mut resolver = Resolver::new(db, module_file_id, inference_id);
43 let use_ast = ast::UsePath::Leaf(db.module_use_by_id(use_id)?.to_maybe()?);
47 let item = use_ast.get_item(db);
48 resolver.set_feature_config(&use_id, &item, &mut diagnostics);
49 let segments = get_use_path_segments(db, use_ast.clone())?;
50 let resolved_item = match handle_self_path(db, &mut diagnostics, segments, use_ast) {
51 Err(diag_added) => Err(diag_added),
52 Ok(segments) => resolver.resolve_generic_path(
53 &mut diagnostics,
54 segments,
55 NotFoundItemType::Identifier,
56 ResolutionContext::ModuleItem(module_item_id),
57 ),
58 };
59 let resolver_data: Arc<ResolverData> = Arc::new(resolver.data);
60
61 Ok(UseData { diagnostics: diagnostics.build(), resolved_item, resolver_data })
62}
63
64fn handle_self_path(
70 db: &dyn SemanticGroup,
71 diagnostics: &mut SemanticDiagnostics,
72 mut segments: Vec<ast::PathSegment>,
73 use_path: ast::UsePath,
74) -> Maybe<Vec<ast::PathSegment>> {
75 if let Some(last) = segments.last() {
76 if last.identifier(db) == SELF_PARAM_KW {
77 if use_path.as_syntax_node().parent(db).unwrap().kind(db) != SyntaxKind::UsePathList {
78 diagnostics.report(use_path.stable_ptr(db), UseSelfNonMulti);
79 }
80 segments.pop();
81 }
82 }
83 if segments.is_empty() {
84 Err(diagnostics.report(use_path.stable_ptr(db), UseSelfEmptyPath))
85 } else {
86 Ok(segments)
87 }
88}
89
90pub fn get_use_path_segments(
99 db: &dyn SyntaxGroup,
100 use_path: ast::UsePath,
101) -> Maybe<Vec<ast::PathSegment>> {
102 let mut rev_segments = vec![];
103 match &use_path {
104 ast::UsePath::Leaf(use_ast) => rev_segments.push(use_ast.ident(db)),
105 ast::UsePath::Single(use_ast) => rev_segments.push(use_ast.ident(db)),
106 ast::UsePath::Star(_) => {}
107 ast::UsePath::Multi(_) => {
108 panic!("Only `UsePathLeaf` and `UsePathSingle` are supported.")
109 }
110 }
111 let mut current_use_path = use_path;
112 while let Some(parent_use_path) = get_parent_single_use_path(db, ¤t_use_path) {
113 rev_segments.push(parent_use_path.ident(db));
114 current_use_path = ast::UsePath::Single(parent_use_path);
115 }
116 Ok(rev_segments.into_iter().rev().collect())
117}
118
119fn get_parent_single_use_path(
121 db: &dyn SyntaxGroup,
122 use_path: &ast::UsePath,
123) -> Option<ast::UsePathSingle> {
124 use SyntaxKind::*;
125 let mut node = use_path.as_syntax_node();
126 loop {
127 node = node.parent(db).expect("`UsePath` is not under an `ItemUse`.");
128 match node.kind(db) {
129 ItemUse => return None,
130 UsePathSingle => return Some(ast::UsePathSingle::from_syntax_node(db, node)),
131 UsePathList | UsePathMulti => continue,
132 UsePathLeaf => unreachable!("`UsePathLeaf` can't be a parent of another `UsePath`."),
133 other => unreachable!("`{other:?}` can't be a parent of `UsePath`."),
134 };
135 }
136}
137
138pub fn priv_use_semantic_data_cycle(
140 db: &dyn SemanticGroup,
141 _cycle: &salsa::Cycle,
142 use_id: &UseId,
143) -> Maybe<UseData> {
144 let module_file_id = use_id.module_file_id(db);
145 let mut diagnostics = SemanticDiagnostics::default();
146 let use_ast = db.module_use_by_id(*use_id)?.to_maybe()?;
147 let err = Err(diagnostics.report(use_ast.stable_ptr(db), UseCycle));
148 let inference_id =
149 InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(ModuleItemId::Use(*use_id)));
150 Ok(UseData {
151 diagnostics: diagnostics.build(),
152 resolved_item: err,
153 resolver_data: Arc::new(ResolverData::new(module_file_id, inference_id)),
154 })
155}
156
157pub fn use_semantic_diagnostics(
159 db: &dyn SemanticGroup,
160 use_id: UseId,
161) -> Diagnostics<SemanticDiagnostic> {
162 db.priv_use_semantic_data(use_id).map(|data| data.diagnostics).unwrap_or_default()
163}
164
165pub fn use_resolver_data(db: &dyn SemanticGroup, use_id: UseId) -> Maybe<Arc<ResolverData>> {
167 Ok(db.priv_use_semantic_data(use_id)?.resolver_data)
168}
169
170pub fn use_resolver_data_cycle(
172 db: &dyn SemanticGroup,
173 _cycle: &salsa::Cycle,
174 use_id: &UseId,
175) -> Maybe<Arc<ResolverData>> {
176 use_resolver_data(db, *use_id)
178}
179
180pub trait SemanticUseEx<'a>: Upcast<dyn SemanticGroup + 'a> {
181 fn use_resolved_item(&self, use_id: UseId) -> Maybe<ResolvedGenericItem> {
185 let db = self.upcast();
186 db.priv_use_semantic_data(use_id)?.resolved_item
187 }
188}
189
190impl<'a, T: Upcast<dyn SemanticGroup + 'a> + ?Sized> SemanticUseEx<'a> for T {}
191
192#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
193#[debug_db(dyn SemanticGroup + 'static)]
194pub struct UseGlobalData {
195 diagnostics: Diagnostics<SemanticDiagnostic>,
196 imported_module: Maybe<ModuleId>,
197}
198
199pub fn priv_global_use_semantic_data(
201 db: &dyn SemanticGroup,
202 global_use_id: GlobalUseId,
203) -> Maybe<UseGlobalData> {
204 let module_file_id = global_use_id.module_file_id(db);
205 let mut diagnostics = SemanticDiagnostics::default();
206 let inference_id = InferenceId::GlobalUseStar(global_use_id);
207 let star_ast = ast::UsePath::Star(db.module_global_use_by_id(global_use_id)?.to_maybe()?);
208 let mut resolver = Resolver::new(db, module_file_id, inference_id);
209 let edition = resolver.settings.edition;
210 if edition.ignore_visibility() {
211 diagnostics.report(star_ast.stable_ptr(db), GlobalUsesNotSupportedInEdition(edition));
213 }
214
215 let item = star_ast.get_item(db);
216 let segments = get_use_path_segments(db, star_ast.clone())?;
217 if segments.is_empty() {
218 let imported_module = Err(diagnostics.report(star_ast.stable_ptr(db), UseStarEmptyPath));
219 return Ok(UseGlobalData { diagnostics: diagnostics.build(), imported_module });
220 }
221 resolver.set_feature_config(&global_use_id, &item, &mut diagnostics);
222 let resolved_item = resolver.resolve_generic_path(
223 &mut diagnostics,
224 segments.clone(),
225 NotFoundItemType::Identifier,
226 ResolutionContext::Default,
227 )?;
228 let last_segment = segments.last().unwrap();
230 let imported_module = match resolved_item {
231 ResolvedGenericItem::Module(module_id) => Ok(module_id),
232 _ => Err(diagnostics.report(
233 last_segment.stable_ptr(db),
234 UnexpectedElement {
235 expected: vec![ElementKind::Module],
236 actual: (&resolved_item).into(),
237 },
238 )),
239 };
240 Ok(UseGlobalData { diagnostics: diagnostics.build(), imported_module })
241}
242
243pub fn priv_global_use_imported_module(
245 db: &dyn SemanticGroup,
246 global_use_id: GlobalUseId,
247) -> Maybe<ModuleId> {
248 db.priv_global_use_semantic_data(global_use_id)?.imported_module
249}
250
251pub fn global_use_semantic_diagnostics(
253 db: &dyn SemanticGroup,
254 global_use_id: GlobalUseId,
255) -> Diagnostics<SemanticDiagnostic> {
256 db.priv_global_use_semantic_data(global_use_id).map(|data| data.diagnostics).unwrap_or_default()
257}
258
259pub fn priv_global_use_semantic_data_cycle(
261 db: &dyn SemanticGroup,
262 cycle: &salsa::Cycle,
263 global_use_id: &GlobalUseId,
264) -> Maybe<UseGlobalData> {
265 let mut diagnostics = SemanticDiagnostics::default();
266 let global_use_ast = db.module_global_use_by_id(*global_use_id)?.to_maybe()?;
267 let star_ast = ast::UsePath::Star(db.module_global_use_by_id(*global_use_id)?.to_maybe()?);
268 let segments = get_use_path_segments(db, star_ast)?;
269 let err = if cycle.participant_keys().count() <= 3 && segments.len() == 1 {
270 diagnostics.report(
273 segments.last().unwrap().stable_ptr(db),
274 PathNotFound(NotFoundItemType::Identifier),
275 )
276 } else {
277 diagnostics.report(global_use_ast.stable_ptr(db), UseCycle)
278 };
279 Ok(UseGlobalData { diagnostics: diagnostics.build(), imported_module: Err(err) })
280}
281
282#[derive(Debug, Clone, PartialEq, Eq)]
284pub struct ImportedModules {
285 pub accessible: OrderedHashSet<(ModuleId, ModuleId)>,
287 pub all: OrderedHashSet<ModuleId>,
290}
291pub fn priv_module_use_star_modules(
294 db: &dyn SemanticGroup,
295 module_id: ModuleId,
296) -> Arc<ImportedModules> {
297 let mut visited = UnorderedHashSet::<_>::default();
298 let mut stack = vec![(module_id, module_id)];
299 let mut accessible_modules = OrderedHashSet::default();
300 while let Some((user_module, containing_module)) = stack.pop() {
303 if !visited.insert((user_module, containing_module)) {
304 continue;
305 }
306 let Ok(glob_uses) = get_module_global_uses(db, containing_module) else {
307 continue;
308 };
309 for (glob_use, item_visibility) in glob_uses.iter() {
310 let Ok(module_id_found) = db.priv_global_use_imported_module(*glob_use) else {
311 continue;
312 };
313 if peek_visible_in(db, *item_visibility, containing_module, user_module) {
314 stack.push((containing_module, module_id_found));
315 accessible_modules.insert((containing_module, module_id_found));
316 }
317 }
318 }
319 let mut visited = UnorderedHashSet::<_>::default();
320 let mut stack = vec![module_id];
321 let mut all_modules = OrderedHashSet::default();
322 while let Some(curr_module_id) = stack.pop() {
324 if !visited.insert(curr_module_id) {
325 continue;
326 }
327 all_modules.insert(curr_module_id);
328 let Ok(glob_uses) = get_module_global_uses(db, curr_module_id) else { continue };
329 for glob_use in glob_uses.keys() {
330 let Ok(module_id_found) = db.priv_global_use_imported_module(*glob_use) else {
331 continue;
332 };
333 stack.push(module_id_found);
334 }
335 }
336 all_modules.swap_remove(&module_id);
339 Arc::new(ImportedModules { accessible: accessible_modules, all: all_modules })
340}