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::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::resolve::{ResolvedGenericItem, Resolver, ResolverData};
26
27#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
28#[debug_db(dyn SemanticGroup + 'static)]
29pub struct UseData {
30 diagnostics: Diagnostics<SemanticDiagnostic>,
31 resolved_item: Maybe<ResolvedGenericItem>,
32 resolver_data: Arc<ResolverData>,
33}
34
35pub fn priv_use_semantic_data(db: &dyn SemanticGroup, use_id: UseId) -> Maybe<UseData> {
37 let module_file_id = use_id.module_file_id(db.upcast());
38 let mut diagnostics = SemanticDiagnostics::default();
39 let inference_id =
40 InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(ModuleItemId::Use(use_id)));
41 let mut resolver = Resolver::new(db, module_file_id, inference_id);
42 let use_ast = ast::UsePath::Leaf(db.module_use_by_id(use_id)?.to_maybe()?);
46 let item = use_ast.get_item(db.upcast());
47 resolver.set_feature_config(&use_id, &item, &mut diagnostics);
48 let segments = get_use_path_segments(db.upcast(), use_ast)?;
49 let resolved_item = resolver.resolve_generic_path(
50 &mut diagnostics,
51 segments,
52 NotFoundItemType::Identifier,
53 None,
54 );
55 let resolver_data: Arc<ResolverData> = Arc::new(resolver.data);
56
57 Ok(UseData { diagnostics: diagnostics.build(), resolved_item, resolver_data })
58}
59
60pub fn get_use_path_segments(
69 db: &dyn SyntaxGroup,
70 use_path: ast::UsePath,
71) -> Maybe<Vec<ast::PathSegment>> {
72 let mut rev_segments = vec![];
73 match &use_path {
74 ast::UsePath::Leaf(use_ast) => rev_segments.push(use_ast.ident(db)),
75 ast::UsePath::Single(use_ast) => rev_segments.push(use_ast.ident(db)),
76 ast::UsePath::Star(_) => {}
77 ast::UsePath::Multi(_) => {
78 panic!("Only `UsePathLeaf` and `UsePathSingle` are supported.")
79 }
80 }
81 let mut current_use_path = use_path;
82 while let Some(parent_use_path) = get_parent_single_use_path(db, ¤t_use_path) {
83 rev_segments.push(parent_use_path.ident(db));
84 current_use_path = ast::UsePath::Single(parent_use_path);
85 }
86 Ok(rev_segments.into_iter().rev().collect())
87}
88
89fn get_parent_single_use_path(
91 db: &dyn SyntaxGroup,
92 use_path: &ast::UsePath,
93) -> Option<ast::UsePathSingle> {
94 use SyntaxKind::*;
95 let mut node = use_path.as_syntax_node();
96 loop {
97 node = node.parent().expect("`UsePath` is not under an `ItemUse`.");
98 match node.kind(db) {
99 ItemUse => return None,
100 UsePathSingle => return Some(ast::UsePathSingle::from_syntax_node(db, node)),
101 UsePathList | UsePathMulti => continue,
102 UsePathLeaf => unreachable!("`UsePathLeaf` can't be a parent of another `UsePath`."),
103 other => unreachable!("`{other:?}` can't be a parent of `UsePath`."),
104 };
105 }
106}
107
108pub fn priv_use_semantic_data_cycle(
110 db: &dyn SemanticGroup,
111 cycle: &salsa::Cycle,
112 use_id: &UseId,
113) -> Maybe<UseData> {
114 let module_file_id = use_id.module_file_id(db.upcast());
115 let mut diagnostics = SemanticDiagnostics::default();
116 let use_ast = db.module_use_by_id(*use_id)?.to_maybe()?;
117 let err = Err(diagnostics.report(
118 &use_ast,
119 if cycle.participant_keys().count() == 1 {
120 PathNotFound(NotFoundItemType::Identifier)
122 } else {
123 UseCycle
124 },
125 ));
126 let inference_id =
127 InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(ModuleItemId::Use(*use_id)));
128 Ok(UseData {
129 diagnostics: diagnostics.build(),
130 resolved_item: err,
131 resolver_data: Arc::new(ResolverData::new(module_file_id, inference_id)),
132 })
133}
134
135pub fn use_semantic_diagnostics(
137 db: &dyn SemanticGroup,
138 use_id: UseId,
139) -> Diagnostics<SemanticDiagnostic> {
140 db.priv_use_semantic_data(use_id).map(|data| data.diagnostics).unwrap_or_default()
141}
142
143pub fn use_resolver_data(db: &dyn SemanticGroup, use_id: UseId) -> Maybe<Arc<ResolverData>> {
145 Ok(db.priv_use_semantic_data(use_id)?.resolver_data)
146}
147
148pub fn use_resolver_data_cycle(
150 db: &dyn SemanticGroup,
151 _cycle: &salsa::Cycle,
152 use_id: &UseId,
153) -> Maybe<Arc<ResolverData>> {
154 use_resolver_data(db, *use_id)
156}
157
158pub trait SemanticUseEx<'a>: Upcast<dyn SemanticGroup + 'a> {
159 fn use_resolved_item(&self, use_id: UseId) -> Maybe<ResolvedGenericItem> {
163 let db = self.upcast();
164 db.priv_use_semantic_data(use_id)?.resolved_item
165 }
166}
167
168impl<'a, T: Upcast<dyn SemanticGroup + 'a> + ?Sized> SemanticUseEx<'a> for T {}
169
170#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
171#[debug_db(dyn SemanticGroup + 'static)]
172pub struct UseGlobalData {
173 diagnostics: Diagnostics<SemanticDiagnostic>,
174 imported_module: Maybe<ModuleId>,
175}
176
177pub fn priv_global_use_semantic_data(
179 db: &dyn SemanticGroup,
180 global_use_id: GlobalUseId,
181) -> Maybe<UseGlobalData> {
182 let module_file_id = global_use_id.module_file_id(db.upcast());
183 let mut diagnostics = SemanticDiagnostics::default();
184 let inference_id = InferenceId::GlobalUseStar(global_use_id);
185 let star_ast = ast::UsePath::Star(db.module_global_use_by_id(global_use_id)?.to_maybe()?);
186 let mut resolver = Resolver::new(db, module_file_id, inference_id);
187 let edition = resolver.settings.edition;
188 if edition.ignore_visibility() {
189 diagnostics.report(&star_ast, GlobalUsesNotSupportedInEdition(edition));
191 }
192
193 let item = star_ast.get_item(db.upcast());
194 let segments = get_use_path_segments(db.upcast(), star_ast.clone())?;
195 if segments.is_empty() {
196 let imported_module = Err(diagnostics.report(star_ast.stable_ptr(), UseStarEmptyPath));
197 return Ok(UseGlobalData { diagnostics: diagnostics.build(), imported_module });
198 }
199 resolver.set_feature_config(&global_use_id, &item, &mut diagnostics);
200 let resolved_item = resolver.resolve_generic_path(
201 &mut diagnostics,
202 segments.clone(),
203 NotFoundItemType::Identifier,
204 None,
205 )?;
206 let last_segment = segments.last().unwrap();
208 let imported_module = match resolved_item {
209 ResolvedGenericItem::Module(module_id) => Ok(module_id),
210 _ => Err(diagnostics.report(last_segment.stable_ptr(), UnexpectedElement {
211 expected: vec![ElementKind::Module],
212 actual: (&resolved_item).into(),
213 })),
214 };
215 Ok(UseGlobalData { diagnostics: diagnostics.build(), imported_module })
216}
217
218pub fn priv_global_use_imported_module(
220 db: &dyn SemanticGroup,
221 global_use_id: GlobalUseId,
222) -> Maybe<ModuleId> {
223 db.priv_global_use_semantic_data(global_use_id)?.imported_module
224}
225
226pub fn global_use_semantic_diagnostics(
228 db: &dyn SemanticGroup,
229 global_use_id: GlobalUseId,
230) -> Diagnostics<SemanticDiagnostic> {
231 db.priv_global_use_semantic_data(global_use_id).map(|data| data.diagnostics).unwrap_or_default()
232}
233
234pub fn priv_global_use_semantic_data_cycle(
236 db: &dyn SemanticGroup,
237 cycle: &salsa::Cycle,
238 global_use_id: &GlobalUseId,
239) -> Maybe<UseGlobalData> {
240 let mut diagnostics = SemanticDiagnostics::default();
241 let global_use_ast = db.module_global_use_by_id(*global_use_id)?.to_maybe()?;
242 let star_ast = ast::UsePath::Star(db.module_global_use_by_id(*global_use_id)?.to_maybe()?);
243 let segments = get_use_path_segments(db.upcast(), star_ast)?;
244 let err = if cycle.participant_keys().count() <= 3 && segments.len() == 1 {
245 diagnostics.report(
248 segments.last().unwrap().stable_ptr(),
249 PathNotFound(NotFoundItemType::Identifier),
250 )
251 } else {
252 diagnostics.report(&global_use_ast, UseCycle)
253 };
254 Ok(UseGlobalData { diagnostics: diagnostics.build(), imported_module: Err(err) })
255}
256
257#[derive(Debug, Clone, PartialEq, Eq)]
259pub struct ImportedModules {
260 pub accessible: OrderedHashSet<(ModuleId, ModuleId)>,
262 pub all: OrderedHashSet<ModuleId>,
265}
266pub fn priv_module_use_star_modules(
269 db: &dyn SemanticGroup,
270 module_id: ModuleId,
271) -> Arc<ImportedModules> {
272 let mut visited = UnorderedHashSet::<_>::default();
273 let mut stack = vec![(module_id, module_id)];
274 let mut accessible_modules = OrderedHashSet::default();
275 while let Some((user_module, containing_module)) = stack.pop() {
278 if !visited.insert((user_module, containing_module)) {
279 continue;
280 }
281 let Ok(glob_uses) = get_module_global_uses(db, containing_module) else {
282 continue;
283 };
284 for (glob_use, item_visibility) in glob_uses.iter() {
285 let Ok(module_id_found) = db.priv_global_use_imported_module(*glob_use) else {
286 continue;
287 };
288 if peek_visible_in(db.upcast(), *item_visibility, containing_module, user_module) {
289 stack.push((containing_module, module_id_found));
290 accessible_modules.insert((containing_module, module_id_found));
291 }
292 }
293 }
294 let mut visited = UnorderedHashSet::<_>::default();
295 let mut stack = vec![module_id];
296 let mut all_modules = OrderedHashSet::default();
297 while let Some(curr_module_id) = stack.pop() {
299 if !visited.insert(curr_module_id) {
300 continue;
301 }
302 all_modules.insert(curr_module_id);
303 let Ok(glob_uses) = get_module_global_uses(db, curr_module_id) else { continue };
304 for glob_use in glob_uses.keys() {
305 let Ok(module_id_found) = db.priv_global_use_imported_module(*glob_use) else {
306 continue;
307 };
308 stack.push(module_id_found);
309 }
310 }
311 Arc::new(ImportedModules { accessible: accessible_modules, all: all_modules })
312}