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 d322e7dc886..a3edca2d00d 100644 --- a/crates/cairo-lang-semantic/src/expr/semantic_test_data/use +++ b/crates/cairo-lang-semantic/src/expr/semantic_test_data/use @@ -1202,3 +1202,171 @@ Constant( ) //! > expected_diagnostics + +//! > ========================================================================== + +//! > Testing use star import impl by trait. + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > crate_settings +edition = "2024_07" + +//! > module_code +mod a { + pub trait MyTrait { + fn foo(self: T) -> T; + } +} +mod b { + pub struct V {} + impl VMyTrait of super::a::MyTrait { + fn foo(self: V) -> V { + self + } + } +} + +use a::*; + +//! > function_body + +//! > expr_code +b::V {}.foo() + +//! > expected_semantics +FunctionCall( + ExprFunctionCall { + function: test::b::VMyTrait::foo, + args: [ + Value( + StructCtor( + ExprStructCtor { + concrete_struct_id: test::b::V, + members: [], + base_struct: None, + ty: test::b::V, + }, + ), + ), + ], + coupon_arg: None, + ty: test::b::V, + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Testing use star import trait by impl. + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > crate_settings +edition = "2024_07" + +//! > module_code +mod a { + pub trait MyTrait { + fn foo(self: T) -> T; + } +} +mod b { + pub impl VMyTrait of super::a::MyTrait { + fn foo(self: super::c::V) -> super::c::V { + self + } + } +} + +mod c { + pub struct V {} +} + +use b::*; + +//! > function_body + +//! > expr_code +c::V {}.foo() + +//! > expected_semantics +FunctionCall( + ExprFunctionCall { + function: test::b::VMyTrait::foo, + args: [ + Value( + StructCtor( + ExprStructCtor { + concrete_struct_id: test::c::V, + members: [], + base_struct: None, + ty: test::c::V, + }, + ), + ), + ], + coupon_arg: None, + ty: test::c::V, + }, +) + +//! > expected_diagnostics + +//! > ========================================================================== + +//! > Testing use star struct import trait. + +//! > test_runner_name +test_expr_semantics(expect_diagnostics: false) + +//! > crate_settings +edition = "2024_07" + +//! > module_code +pub trait MyTrait { + fn foo(t: T) -> T; +} +mod b { + pub impl VMyTrait of super::MyTrait { + fn foo(t: super::c::V) -> super::c::V { + t + } + } +} + +mod c { + use super::b::*; + pub struct V {} +} + +//! > function_body + +//! > expr_code +MyTrait::foo(c::V {}) + +//! > expected_semantics +FunctionCall( + ExprFunctionCall { + function: test::b::VMyTrait::foo, + args: [ + Value( + StructCtor( + ExprStructCtor { + concrete_struct_id: test::c::V, + members: [], + base_struct: None, + ty: test::c::V, + }, + ), + ), + ], + coupon_arg: None, + ty: test::c::V, + }, +) + +//! > expected_diagnostics diff --git a/crates/cairo-lang-semantic/src/items/imp.rs b/crates/cairo-lang-semantic/src/items/imp.rs index 909be0e3cc9..562e5909b69 100644 --- a/crates/cairo-lang-semantic/src/items/imp.rs +++ b/crates/cairo-lang-semantic/src/items/imp.rs @@ -59,6 +59,7 @@ use super::type_aliases::{ TypeAliasData, type_alias_generic_params_data_helper, type_alias_semantic_data_cycle_helper, type_alias_semantic_data_helper, }; +use super::visibility::peek_visible_in; use super::{TraitOrImplContext, resolve_trait_path}; use crate::corelib::{ CoreTraitContext, concrete_destruct_trait, concrete_drop_trait, copy_trait, core_crate, @@ -1354,24 +1355,12 @@ pub fn module_impl_ids_for_trait_filter( module_id: ModuleId, trait_filter: TraitFilter, ) -> Maybe> { - let mut uninferred_impls = Vec::new(); - if let Ok(impl_ids) = db.module_impls_ids(module_id) { - uninferred_impls.extend(impl_ids.iter().copied().map(UninferredImpl::Def)); - } - if let Ok(impl_aliases_ids) = db.module_impl_aliases_ids(module_id) { - uninferred_impls.extend(impl_aliases_ids.iter().copied().map(UninferredImpl::ImplAlias)); - } - if let Ok(uses_ids) = db.module_uses_ids(module_id) { - for use_id in uses_ids.iter().copied() { - match db.use_resolved_item(use_id) { - Ok(ResolvedGenericItem::Impl(impl_def_id)) => { - uninferred_impls.push(UninferredImpl::Def(impl_def_id)); - } - Ok(ResolvedGenericItem::GenericImplAlias(impl_alias_id)) => { - uninferred_impls.push(UninferredImpl::ImplAlias(impl_alias_id)); - } - _ => {} - } + let mut uninferred_impls: OrderedHashSet = + OrderedHashSet::from_iter(module_impl_ids(db, module_id, module_id)?); + for (user_module, containing_module) in &db.priv_module_use_star_modules(module_id).accessible { + let local_uninferred_impls = module_impl_ids(db, *user_module, *containing_module)?; + for curr_uniferred_impl in local_uninferred_impls { + uninferred_impls.insert(curr_uniferred_impl); } } let mut res = Vec::new(); @@ -1387,10 +1376,49 @@ pub fn module_impl_ids_for_trait_filter( res.push(uninferred_impl); } } - Ok(res) } +/// Returns the uninferred impls in a module. +fn module_impl_ids( + db: &dyn SemanticGroup, + user_module: ModuleId, + containing_module: ModuleId, +) -> Maybe> { + let defs_db = db.upcast(); + let mut uninferred_impls = Vec::new(); + for item in db.priv_module_semantic_data(containing_module)?.items.values() { + if !matches!( + item.item_id, + ModuleItemId::Impl(_) | ModuleItemId::ImplAlias(_) | ModuleItemId::Use(_) + ) { + continue; + } + if !peek_visible_in(defs_db, item.visibility, containing_module, user_module) { + continue; + } + match item.item_id { + ModuleItemId::Impl(impl_def_id) => { + uninferred_impls.push(UninferredImpl::Def(impl_def_id)); + } + ModuleItemId::ImplAlias(impl_alias_id) => { + uninferred_impls.push(UninferredImpl::ImplAlias(impl_alias_id)); + } + ModuleItemId::Use(use_id) => match db.use_resolved_item(use_id) { + Ok(ResolvedGenericItem::Impl(impl_def_id)) => { + uninferred_impls.push(UninferredImpl::Def(impl_def_id)); + } + Ok(ResolvedGenericItem::GenericImplAlias(impl_alias_id)) => { + uninferred_impls.push(UninferredImpl::ImplAlias(impl_alias_id)); + } + _ => {} + }, + _ => {} + } + } + Ok(uninferred_impls) +} + /// Cycle handling for [crate::db::SemanticGroup::module_impl_ids_for_trait_filter]. pub fn module_impl_ids_for_trait_filter_cycle( _db: &dyn SemanticGroup, @@ -1551,7 +1579,7 @@ impl ImplLookupContext { } } -/// An candidate impl for later inference. +/// A candidate impl for later inference. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)] pub enum UninferredImpl { Def(ImplDefId), diff --git a/crates/cairo-lang-semantic/src/items/module.rs b/crates/cairo-lang-semantic/src/items/module.rs index cb2c5696e1d..88bb7c94ca4 100644 --- a/crates/cairo-lang-semantic/src/items/module.rs +++ b/crates/cairo-lang-semantic/src/items/module.rs @@ -15,7 +15,7 @@ use smol_str::SmolStr; use super::feature_kind::FeatureKind; use super::us::SemanticUseEx; -use super::visibility::Visibility; +use super::visibility::{Visibility, peek_visible_in}; use crate::SemanticDiagnostic; use crate::db::{SemanticGroup, get_resolver_data_options}; use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnosticsBuilder}; @@ -192,55 +192,94 @@ pub fn module_attributes(db: &dyn SemanticGroup, module_id: ModuleId) -> Maybe Maybe>> { - let mut module_traits = OrderedHashMap::from_iter( - db.module_traits_ids(module_id)? - .iter() - .map(|traid_id| (*traid_id, LookupItemId::ModuleItem(ModuleItemId::Trait(*traid_id)))), - ); - // Add traits from impls in the module. - for imp in db.module_impls_ids(module_id)?.iter().copied() { - let Ok(trait_id) = db.impl_def_trait(imp) else { - continue; - }; - module_traits.entry(trait_id).or_insert(LookupItemId::ModuleItem(ModuleItemId::Impl(imp))); + // Get the traits first from the module, do not change this order. + let mut module_traits = specific_module_usable_trait_ids(db, module_id, module_id)?; + for (user_module, containing_module) in &db.priv_module_use_star_modules(module_id).accessible { + let local_module_traits = + specific_module_usable_trait_ids(db, *user_module, *containing_module)?; + for (trait_id, local_item_id) in local_module_traits { + module_traits.entry(trait_id).or_insert(local_item_id); + } } - // Add traits from impl aliases in the module. - for alias in db.module_impl_aliases_ids(module_id)?.iter().copied() { - let Ok(impl_id) = db.impl_alias_impl_def(alias) else { - continue; - }; - let Ok(trait_id) = db.impl_def_trait(impl_id) else { + Ok(module_traits.into()) +} + +/// Finds all the trait ids usable in the current context, not using `global use` imports. +fn specific_module_usable_trait_ids( + db: &dyn SemanticGroup, + user_module: ModuleId, + containing_module: ModuleId, +) -> Maybe> { + let def_db = db.upcast(); + let mut module_traits: OrderedHashMap = OrderedHashMap::default(); + for item in db.priv_module_semantic_data(containing_module)?.items.values() { + if !matches!( + item.item_id, + ModuleItemId::Trait(_) + | ModuleItemId::Impl(_) + | ModuleItemId::ImplAlias(_) + | ModuleItemId::Use(_) + ) { continue; - }; - module_traits - .entry(trait_id) - .or_insert(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(alias))); - } - // Add traits from uses in the module. - for use_id in db.module_uses_ids(module_id)?.iter().copied() { - let Ok(resolved_item) = db.use_resolved_item(use_id) else { + } + if !peek_visible_in(def_db, item.visibility, containing_module, user_module) { continue; - }; - match resolved_item { - // use of a trait. - ResolvedGenericItem::Trait(trait_id) => { - module_traits.insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Use(use_id))); - } - // use of an impl from which we get the trait. - ResolvedGenericItem::Impl(impl_def_id) => { - if let Ok(trait_id) = db.impl_def_trait(impl_def_id) { - module_traits - .entry(trait_id) - .or_insert(LookupItemId::ModuleItem(ModuleItemId::Use(use_id))); + } + match item.item_id { + ModuleItemId::Trait(trait_id) => { + module_traits + .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Trait(trait_id))); + } + ModuleItemId::Impl(impl_def_id) => { + // Add traits from impls in the module. + let Ok(trait_id) = db.impl_def_trait(impl_def_id) else { + continue; + }; + module_traits + .entry(trait_id) + .or_insert(LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id))); + } + ModuleItemId::ImplAlias(impl_alias_id) => { + // Add traits from impl aliases in the module. + let Ok(impl_id) = db.impl_alias_impl_def(impl_alias_id) else { + continue; + }; + let Ok(trait_id) = db.impl_def_trait(impl_id) else { + continue; }; + module_traits + .entry(trait_id) + .or_insert(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id))); + } + ModuleItemId::Use(use_id) => { + // Add traits from uses in the module. + let Ok(resolved_item) = db.use_resolved_item(use_id) else { + continue; + }; + match resolved_item { + // use of a trait. + ResolvedGenericItem::Trait(trait_id) => { + module_traits + .insert(trait_id, LookupItemId::ModuleItem(ModuleItemId::Use(use_id))); + } + // use of an impl from which we get the trait. + ResolvedGenericItem::Impl(impl_def_id) => { + if let Ok(trait_id) = db.impl_def_trait(impl_def_id) { + module_traits + .entry(trait_id) + .or_insert(LookupItemId::ModuleItem(ModuleItemId::Use(use_id))); + }; + } + _ => {} + } } _ => {} } } - Ok(module_traits.into()) + Ok(module_traits) }