diff --git a/crates/cairo-lang-defs/src/db.rs b/crates/cairo-lang-defs/src/db.rs index a8b314f8621..da2cbe5b009 100644 --- a/crates/cairo-lang-defs/src/db.rs +++ b/crates/cairo-lang-defs/src/db.rs @@ -41,6 +41,8 @@ pub trait DefsGroup: #[salsa::interned] fn intern_use(&self, id: UseLongId) -> UseId; #[salsa::interned] + fn intern_global_use(&self, id: GlobalUseLongId) -> GlobalUseId; + #[salsa::interned] fn intern_free_function(&self, id: FreeFunctionLongId) -> FreeFunctionId; #[salsa::interned] fn intern_impl_type_def(&self, id: ImplTypeDefLongId) -> ImplTypeDefId; @@ -162,6 +164,10 @@ pub trait DefsGroup: free_function_id: FreeFunctionId, ) -> Maybe>; fn module_items(&self, module_id: ModuleId) -> Maybe>; + fn module_global_uses( + &self, + module_id: ModuleId, + ) -> Maybe>>; /// Returns the stable ptr of the name of a module item. fn module_item_name_stable_ptr( &self, @@ -174,6 +180,10 @@ pub trait DefsGroup: ) -> Maybe>>; fn module_uses_ids(&self, module_id: ModuleId) -> Maybe>; fn module_use_by_id(&self, use_id: UseId) -> Maybe>; + fn module_global_use_by_id( + &self, + global_use_id: GlobalUseId, + ) -> Maybe>; fn module_structs( &self, module_id: ModuleId, @@ -394,6 +404,7 @@ pub struct ModuleData { impls: Arc>, extern_types: Arc>, extern_functions: Arc>, + global_uses: Arc>, files: Vec, /// Generation info for each file. Virtual files have Some. Other files have None. @@ -452,6 +463,7 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe Maybe { let item_id = @@ -568,6 +584,7 @@ fn priv_module_data(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe Vec ast::UsePath::Multi(use_path) => { stack.extend(use_path.use_paths(db).elements(db).into_iter().rev()) } - ast::UsePath::Star(_) => todo!("Change function to collect `star` use as well."), + ast::UsePath::Star(_) => {} + } + } + res +} + +/// Returns all the path stars under a given use item. +pub fn get_all_path_stars(db: &dyn SyntaxGroup, use_item: &ast::ItemUse) -> Vec { + let mut res = vec![]; + let mut stack = vec![use_item.use_path(db)]; + while let Some(use_path) = stack.pop() { + match use_path { + ast::UsePath::Leaf(_) => {} + ast::UsePath::Single(use_path) => stack.push(use_path.use_path(db)), + ast::UsePath::Multi(use_path) => { + stack.extend(use_path.use_paths(db).elements(db).into_iter().rev()) + } + ast::UsePath::Star(use_path) => res.push(use_path), } } res @@ -853,6 +887,15 @@ pub fn module_use_by_id(db: &dyn DefsGroup, use_id: UseId) -> Maybe Maybe> { + let module_global_uses = db.module_global_uses(global_use_id.module_file_id(db.upcast()).0)?; + Ok(module_global_uses.get(&global_use_id).cloned()) +} + /// Returns all the structs of the given module. pub fn module_structs( db: &dyn DefsGroup, @@ -1038,6 +1081,13 @@ fn module_items(db: &dyn DefsGroup, module_id: ModuleId) -> Maybe Maybe>> { + Ok(db.priv_module_data(module_id)?.global_uses) +} + fn module_item_name_stable_ptr( db: &dyn DefsGroup, module_id: ModuleId, diff --git a/crates/cairo-lang-defs/src/ids.rs b/crates/cairo-lang-defs/src/ids.rs index 53b15379269..b98af99c767 100644 --- a/crates/cairo-lang-defs/src/ids.rs +++ b/crates/cairo-lang-defs/src/ids.rs @@ -359,6 +359,13 @@ define_top_level_language_element_id!( lookup_intern_constant, intern_constant ); +define_language_element_id_basic!( + GlobalUseId, + GlobalUseLongId, + ast::UsePathStar, + lookup_intern_global_use, + intern_global_use +); define_top_level_language_element_id!( UseId, UseLongId, diff --git a/crates/cairo-lang-semantic/src/db.rs b/crates/cairo-lang-semantic/src/db.rs index d1fcfa94982..9c3b2d86af4 100644 --- a/crates/cairo-lang-semantic/src/db.rs +++ b/crates/cairo-lang-semantic/src/db.rs @@ -4,10 +4,10 @@ use cairo_lang_defs::db::DefsGroup; use cairo_lang_defs::diagnostic_utils::StableLocation; use cairo_lang_defs::ids::{ ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, FunctionTitleId, - FunctionWithBodyId, GenericParamId, GenericTypeId, ImplAliasId, ImplConstantDefId, ImplDefId, - ImplFunctionId, ImplImplDefId, ImplItemId, ImplTypeDefId, LanguageElementId, LookupItemId, - ModuleId, ModuleItemId, ModuleTypeAliasId, StructId, TraitConstantId, TraitFunctionId, TraitId, - TraitImplId, TraitItemId, TraitTypeId, UseId, VariantId, + FunctionWithBodyId, GenericParamId, GenericTypeId, GlobalUseId, ImplAliasId, ImplConstantDefId, + ImplDefId, ImplFunctionId, ImplImplDefId, ImplItemId, ImplTypeDefId, LanguageElementId, + LookupItemId, ModuleId, ModuleItemId, ModuleTypeAliasId, StructId, TraitConstantId, + TraitFunctionId, TraitId, TraitImplId, TraitItemId, TraitTypeId, UseId, VariantId, }; use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe}; use cairo_lang_filesystem::db::{AsFilesGroupMut, FilesGroup}; @@ -175,6 +175,13 @@ pub trait SemanticGroup: #[salsa::cycle(items::us::use_resolver_data_cycle)] fn use_resolver_data(&self, use_id: UseId) -> Maybe>; + // Global Use. + // ==== + /// Private query to compute data about a global use. + #[salsa::invoke(items::us::priv_global_use_semantic_data)] + fn priv_global_use_semantic_data(&self, use_id: GlobalUseId) + -> Maybe; + // Module. // ==== diff --git a/crates/cairo-lang-semantic/src/diagnostic.rs b/crates/cairo-lang-semantic/src/diagnostic.rs index 290e343897b..878b66de3d0 100644 --- a/crates/cairo-lang-semantic/src/diagnostic.rs +++ b/crates/cairo-lang-semantic/src/diagnostic.rs @@ -20,7 +20,7 @@ use crate::corelib::LiteralError; use crate::db::SemanticGroup; use crate::expr::inference::InferenceError; use crate::items::feature_kind::FeatureMarkerDiagnostic; -use crate::resolve::ResolvedConcreteItem; +use crate::resolve::{ResolvedConcreteItem, ResolvedGenericItem}; use crate::types::peel_snapshots; use crate::{ConcreteTraitId, semantic}; @@ -1302,6 +1302,25 @@ impl From<&ResolvedConcreteItem> for ElementKind { } } } +impl From<&ResolvedGenericItem> for ElementKind { + fn from(val: &ResolvedGenericItem) -> Self { + match val { + ResolvedGenericItem::GenericConstant(_) => ElementKind::Constant, + ResolvedGenericItem::Module(_) => ElementKind::Module, + ResolvedGenericItem::GenericFunction(_) => ElementKind::Function, + ResolvedGenericItem::TraitFunction(_) => ElementKind::TraitFunction, + ResolvedGenericItem::GenericType(_) | ResolvedGenericItem::GenericTypeAlias(_) => { + ElementKind::Type + } + ResolvedGenericItem::Variant(_) => ElementKind::Variant, + ResolvedGenericItem::Trait(_) => ElementKind::Trait, + ResolvedGenericItem::Impl(_) | ResolvedGenericItem::GenericImplAlias(_) => { + ElementKind::Impl + } + ResolvedGenericItem::Variable(_) => ElementKind::Variable, + } + } +} impl Display for ElementKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let res = match self { diff --git a/crates/cairo-lang-semantic/src/expr/inference.rs b/crates/cairo-lang-semantic/src/expr/inference.rs index b61a76de731..00b721eccd9 100644 --- a/crates/cairo-lang-semantic/src/expr/inference.rs +++ b/crates/cairo-lang-semantic/src/expr/inference.rs @@ -8,9 +8,9 @@ use std::ops::{Deref, DerefMut}; use cairo_lang_debug::DebugWithDb; use cairo_lang_defs::ids::{ ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, GenericParamId, - ImplAliasId, ImplDefId, ImplFunctionId, ImplImplDefId, LanguageElementId, LocalVarId, - LookupItemId, MemberId, ParamId, StructId, TraitConstantId, TraitFunctionId, TraitId, - TraitImplId, TraitTypeId, VarId, VariantId, + GlobalUseId, ImplAliasId, ImplDefId, ImplFunctionId, ImplImplDefId, LanguageElementId, + LocalVarId, LookupItemId, MemberId, ParamId, StructId, TraitConstantId, TraitFunctionId, + TraitId, TraitImplId, TraitTypeId, VarId, VariantId, }; use cairo_lang_diagnostics::{DiagnosticAdded, Maybe, skip_diagnostic}; use cairo_lang_proc_macros::{DebugWithDb, SemanticObject}; @@ -86,6 +86,7 @@ pub enum InferenceId { ImplAliasImplDef(ImplAliasId), GenericParam(GenericParamId), GenericImplParamTrait(GenericParamId), + GlobalUseStar(GlobalUseId), Canonical, /// For resolving that will not be used anywhere in the semantic model. NoContext, diff --git a/crates/cairo-lang-semantic/src/expr/semantic_test_data/use b/crates/cairo-lang-semantic/src/expr/semantic_test_data/use index 114c3ad3690..98d29269bc2 100644 --- a/crates/cairo-lang-semantic/src/expr/semantic_test_data/use +++ b/crates/cairo-lang-semantic/src/expr/semantic_test_data/use @@ -1022,3 +1022,54 @@ Block( ) //! > expected_diagnostics + +//! > ========================================================================== + +//! > Testing use star without implementation in Semantic + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: true) + +//! > module_code +/// TODO(Tomer-StarkWare): Implement use star in Semantic +pub mod a { + const C: u8 = 2; +} +pub mod b { + use super::a::*; +} + +//! > function_body + +//! > expr_code +{ + let _s = b::C; +} + +//! > expected_semantics +Block( + ExprBlock { + statements: [ + Let( + StatementLet { + pattern: Variable( + _s, + ), + expr: Missing( + ExprMissing { + ty: , + }, + ), + }, + ), + ], + tail: None, + ty: (), + }, +) + +//! > expected_diagnostics +error: Identifier not found. + --> lib.cairo:10:17 + let _s = b::C; + ^ diff --git a/crates/cairo-lang-semantic/src/items/module.rs b/crates/cairo-lang-semantic/src/items/module.rs index 1b6f3e6d1ec..b6cc10eb520 100644 --- a/crates/cairo-lang-semantic/src/items/module.rs +++ b/crates/cairo-lang-semantic/src/items/module.rs @@ -2,7 +2,8 @@ use std::sync::Arc; use cairo_lang_defs::db::DefsGroup; use cairo_lang_defs::ids::{ - LanguageElementId, LookupItemId, ModuleId, ModuleItemId, NamedLanguageElementId, TraitId, + GlobalUseId, LanguageElementId, LookupItemId, ModuleId, ModuleItemId, NamedLanguageElementId, + TraitId, }; use cairo_lang_diagnostics::{Diagnostics, DiagnosticsBuilder, Maybe}; use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize}; @@ -32,6 +33,7 @@ pub struct ModuleItemInfo { pub struct ModuleSemanticData { /// The items in the module without duplicates. pub items: OrderedHashMap, + pub global_uses: OrderedHashMap, pub diagnostics: Diagnostics, } @@ -110,7 +112,19 @@ pub fn priv_module_semantic_data( ); } } - Ok(Arc::new(ModuleSemanticData { items, diagnostics: diagnostics.build() })) + + let global_uses = db + .module_global_uses(module_id)? + .iter() + .map(|(global_use_id, use_path_star)| { + let item = ast::UsePath::Star(use_path_star.clone()).get_item(syntax_db); + ( + *global_use_id, + Visibility::from_ast(db.upcast(), &mut diagnostics, &item.visibility(syntax_db)), + ) + }) + .collect(); + Ok(Arc::new(ModuleSemanticData { items, global_uses, diagnostics: diagnostics.build() })) } pub fn module_item_by_name( diff --git a/crates/cairo-lang-semantic/src/items/us.rs b/crates/cairo-lang-semantic/src/items/us.rs index 33866edadb6..e03bba6e0f0 100644 --- a/crates/cairo-lang-semantic/src/items/us.rs +++ b/crates/cairo-lang-semantic/src/items/us.rs @@ -1,6 +1,8 @@ use std::sync::Arc; -use cairo_lang_defs::ids::{LanguageElementId, LookupItemId, ModuleItemId, UseId}; +use cairo_lang_defs::ids::{ + GlobalUseId, LanguageElementId, LookupItemId, ModuleId, ModuleItemId, UseId, +}; use cairo_lang_diagnostics::{Diagnostics, Maybe, ToMaybe}; use cairo_lang_proc_macros::DebugWithDb; use cairo_lang_syntax::node::db::SyntaxGroup; @@ -12,7 +14,9 @@ use cairo_lang_utils::Upcast; use crate::SemanticDiagnostic; use crate::db::SemanticGroup; use crate::diagnostic::SemanticDiagnosticKind::*; -use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder}; +use crate::diagnostic::{ + ElementKind, NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder, +}; use crate::expr::inference::InferenceId; use crate::resolve::{ResolvedGenericItem, Resolver, ResolverData}; @@ -49,6 +53,49 @@ pub fn priv_use_semantic_data(db: &dyn SemanticGroup, use_id: UseId) -> Maybe Maybe { + let module_file_id = global_use_id.module_file_id(db.upcast()); + let mut diagnostics = SemanticDiagnostics::default(); + let inference_id = InferenceId::GlobalUseStar(global_use_id); + let star_ast = ast::UsePath::Star(db.module_global_use_by_id(global_use_id)?.to_maybe()?); + let mut resolver = Resolver::new(db, module_file_id, inference_id); + + let item = star_ast.get_item(db.upcast()); + let segments = get_use_path_segments(db.upcast(), star_ast.clone())?; + resolver.set_feature_config(&global_use_id, &item, &mut diagnostics); + let resolved_item = resolver.resolve_generic_path( + &mut diagnostics, + segments, + NotFoundItemType::Identifier, + None, + ); + let resolver_data = Arc::new(resolver.data); + let imported_module = resolved_item.and_then(|item| { + if let ResolvedGenericItem::Module(module_id) = item { + Ok(module_id) + } else { + Err(diagnostics.report(&star_ast, UnexpectedElement { + expected: vec![ElementKind::Module], + actual: (&item).into(), + })) + } + }); + + Ok(UseGlobalData { diagnostics: diagnostics.build(), imported_module, resolver_data }) +} + +#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)] +#[debug_db(dyn SemanticGroup + 'static)] +pub struct UseGlobalData { + diagnostics: Diagnostics, + imported_module: Maybe, + resolver_data: Arc, +} + /// Returns the segments that are the parts of the use path. /// /// The segments are returned in the order they appear in the use path. @@ -61,13 +108,15 @@ pub fn get_use_path_segments( db: &dyn SyntaxGroup, use_path: ast::UsePath, ) -> Maybe> { - let mut rev_segments = vec![match &use_path { - ast::UsePath::Leaf(use_ast) => use_ast.ident(db), - ast::UsePath::Single(use_ast) => use_ast.ident(db), - ast::UsePath::Multi(_) | ast::UsePath::Star(_) => { + let mut rev_segments = vec![]; + match &use_path { + ast::UsePath::Leaf(use_ast) => rev_segments.push(use_ast.ident(db)), + ast::UsePath::Single(use_ast) => rev_segments.push(use_ast.ident(db)), + ast::UsePath::Star(_) => {} + ast::UsePath::Multi(_) => { panic!("Only `UsePathLeaf` and `UsePathSingle` are supported.") } - }]; + } let mut current_use_path = use_path; while let Some(parent_use_path) = get_parent_single_use_path(db, ¤t_use_path) { rev_segments.push(parent_use_path.ident(db));