diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index fe518c86429..e091af4bf8c 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2787,6 +2787,7 @@ pub fn iterate_method_candidates( db: &dyn HirDatabase, krate: Crate, traits_in_scope: &FxHashSet, + with_local_impls: Option, name: Option<&Name>, mut callback: impl FnMut(Type, Function) -> Option, ) -> Option { @@ -2797,6 +2798,7 @@ pub fn iterate_method_candidates( db, krate, traits_in_scope, + with_local_impls, name, &mut |ty, assoc_item_id| { if let AssocItemId::FunctionId(func) = assoc_item_id { @@ -2816,6 +2818,7 @@ fn iterate_method_candidates_dyn( db: &dyn HirDatabase, krate: Crate, traits_in_scope: &FxHashSet, + with_local_impls: Option, name: Option<&Name>, callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>, ) { @@ -2831,7 +2834,7 @@ fn iterate_method_candidates_dyn( env, krate, traits_in_scope, - None, + with_local_impls.and_then(|b| b.id.containing_block()).into(), name, method_resolution::LookupMode::MethodCall, &mut |ty, id| callback(&ty.value, id), @@ -2843,6 +2846,7 @@ pub fn iterate_path_candidates( db: &dyn HirDatabase, krate: Crate, traits_in_scope: &FxHashSet, + with_local_impls: Option, name: Option<&Name>, mut callback: impl FnMut(Type, AssocItem) -> Option, ) -> Option { @@ -2852,6 +2856,7 @@ pub fn iterate_path_candidates( db, krate, traits_in_scope, + with_local_impls, name, &mut |ty, assoc_item_id| { if let Some(res) = callback(self.derived(ty.clone()), assoc_item_id.into()) { @@ -2869,6 +2874,7 @@ fn iterate_path_candidates_dyn( db: &dyn HirDatabase, krate: Crate, traits_in_scope: &FxHashSet, + with_local_impls: Option, name: Option<&Name>, callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>, ) { @@ -2883,7 +2889,7 @@ fn iterate_path_candidates_dyn( env, krate, traits_in_scope, - None, + with_local_impls.and_then(|b| b.id.containing_block()).into(), name, method_resolution::LookupMode::Path, &mut |ty, id| callback(&ty.value, id), diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 54b1680214e..4f1bdee705d 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -996,7 +996,7 @@ fn infer_method_call( self.trait_env.clone(), krate, &traits_in_scope, - self.resolver.module(), + self.resolver.module().into(), method_name, ) }); diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index c33a697f05a..b63ef2ffdc8 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -227,7 +227,7 @@ fn resolve_ty_assoc_item( self.table.trait_env.clone(), krate, &traits_in_scope, - self.resolver.module(), + self.resolver.module().into(), Some(name), method_resolution::LookupMode::Path, move |_ty, item| { diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 1a451ae79f5..c91b6f2e82d 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -437,7 +437,7 @@ pub(crate) fn lookup_method( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, - visible_from_module: Option, + visible_from_module: VisibleFromModule, name: &Name, ) -> Option<(Canonical, FunctionId)> { iterate_method_candidates( @@ -468,6 +468,34 @@ pub enum LookupMode { Path, } +#[derive(Clone, Copy)] +pub enum VisibleFromModule { + /// Filter for results that are visible from the given module + Filter(ModuleId), + /// Include impls from the given block. + IncludeBlock(BlockId), + /// Do nothing special in regards visibility + None, +} + +impl From> for VisibleFromModule { + fn from(module: Option) -> Self { + match module { + Some(module) => Self::Filter(module), + None => Self::None, + } + } +} + +impl From> for VisibleFromModule { + fn from(block: Option) -> Self { + match block { + Some(block) => Self::IncludeBlock(block), + None => Self::None, + } + } +} + // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? @@ -477,7 +505,7 @@ pub fn iterate_method_candidates( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, - visible_from_module: Option, + visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, mut callback: impl FnMut(&Canonical, AssocItemId) -> Option, @@ -510,7 +538,7 @@ pub fn iterate_method_candidates_dyn( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, - visible_from_module: Option, + visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, @@ -578,7 +606,7 @@ fn iterate_method_candidates_with_autoref( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, - visible_from_module: Option, + visible_from_module: VisibleFromModule, name: Option<&Name>, mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { @@ -644,7 +672,7 @@ fn iterate_method_candidates_by_receiver( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, - visible_from_module: Option, + visible_from_module: VisibleFromModule, name: Option<&Name>, mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { @@ -686,7 +714,7 @@ fn iterate_method_candidates_for_self_ty( env: Arc, krate: CrateId, traits_in_scope: &FxHashSet, - visible_from_module: Option, + visible_from_module: VisibleFromModule, name: Option<&Name>, mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { @@ -797,7 +825,7 @@ fn iterate_inherent_methods( name: Option<&Name>, receiver_ty: Option<&Canonical>, krate: CrateId, - visible_from_module: Option, + visible_from_module: VisibleFromModule, callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { let def_crates = match def_crates(db, &self_ty.value, krate) { @@ -805,35 +833,30 @@ fn iterate_inherent_methods( None => return ControlFlow::Continue(()), }; - if let Some(module_id) = visible_from_module { - if let Some(block_id) = module_id.containing_block() { - if let Some(impls) = db.inherent_impls_in_block(block_id) { - impls_for_self_ty( - &impls, - self_ty, - db, - env.clone(), - name, - receiver_ty, - visible_from_module, - callback, - )?; - } + let (module, block) = match visible_from_module { + VisibleFromModule::Filter(module) => (Some(module), module.containing_block()), + VisibleFromModule::IncludeBlock(block) => (None, Some(block)), + VisibleFromModule::None => (None, None), + }; + + if let Some(block_id) = block { + if let Some(impls) = db.inherent_impls_in_block(block_id) { + impls_for_self_ty( + &impls, + self_ty, + db, + env.clone(), + name, + receiver_ty, + module, + callback, + )?; } } for krate in def_crates { let impls = db.inherent_impls_in_crate(krate); - impls_for_self_ty( - &impls, - self_ty, - db, - env.clone(), - name, - receiver_ty, - visible_from_module, - callback, - )?; + impls_for_self_ty(&impls, self_ty, db, env.clone(), name, receiver_ty, module, callback)?; } return ControlFlow::Continue(()); diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs index 7fbbdb4f5eb..0d2daa8dc30 100644 --- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs @@ -156,6 +156,7 @@ fn is_ref_and_impls_iter_method( sema.db, krate, &traits_in_scope, + None, Some(&wanted_method), |_, func| { if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) { diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs index 15025cf0d0e..cb3fbed2199 100644 --- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs +++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs @@ -93,7 +93,9 @@ fn get_impl_method( let krate = impl_def.module(db).krate(); let ty = impl_def.self_ty(db); let traits_in_scope = scope.visible_traits(); - ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func)) + ty.iterate_method_candidates(db, krate, &traits_in_scope, None, Some(fn_name), |_, func| { + Some(func) + }) } #[cfg(test)] diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index e399213731d..a072d223e0d 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs @@ -253,7 +253,7 @@ fn enum_variants_with_paths( ) { let variants = enum_.variants(ctx.db); - let module = if let Some(module) = ctx.scope.module() { + let module = if let Some(module) = ctx.module { // Compute path from the completion site if available. module } else { diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs index 5b22408a2cb..29fe096e135 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs @@ -80,7 +80,7 @@ fn flyimport_derive(acc: &mut Completions, ctx: &CompletionContext) -> Option<() return None; }; let potential_import_name = ctx.token.to_string(); - let module = ctx.scope.module()?; + let module = ctx.module?; let parent = ctx.token.parent()?; let user_input_lowercased = potential_import_name.to_lowercase(); let import_assets = ImportAssets::for_fuzzy_path( diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 539b423cb30..b8bab941706 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs @@ -84,12 +84,19 @@ fn complete_methods( traits_in_scope.remove(&drop_trait.into()); } - receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { - if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { - f(func); - } - None::<()> - }); + receiver.iterate_method_candidates( + ctx.db, + krate, + &traits_in_scope, + ctx.module, + None, + |_ty, func| { + if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { + f(func); + } + None::<()> + }, + ); } } @@ -265,6 +272,37 @@ fn foo(a: lib::A) { a.$0 } ); } + #[test] + fn test_local_impls() { + check( + r#" +//- /lib.rs crate:lib +pub struct A {} +mod m { + impl super::A { + pub fn pub_module_method(&self) {} + } + fn f() { + impl super::A { + pub fn pub_foreign_local_method(&self) {} + } + } +} +//- /main.rs crate:main deps:lib +fn foo(a: lib::A) { + impl lib::A { + fn local_method(&self) {} + } + a.$0 +} +"#, + expect![[r#" + me local_method() fn(&self) + me pub_module_method() fn(&self) + "#]], + ); + } + #[test] fn test_doc_hidden_filtering() { check( diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 8994c25475b..782a119c9e7 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs @@ -217,7 +217,7 @@ pub(crate) fn position_for_import( } fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option { - let current_module = ctx.scope.module()?; + let current_module = ctx.module?; if let Some(dot_receiver) = ctx.dot_receiver() { ImportAssets::for_fuzzy_method_call( current_module, diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs index beef834581b..64e992c2e6b 100644 --- a/crates/ide_completion/src/completions/mod_.rs +++ b/crates/ide_completion/src/completions/mod_.rs @@ -22,7 +22,7 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op let _p = profile::span("completion::complete_mod"); - let current_module = ctx.scope.module()?; + let current_module = ctx.module?; let module_definition_file = current_module.definition_source(ctx.db).file_id.original_file(ctx.db); diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 499791abc9a..85df19f1dd4 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -51,7 +51,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon None => return, }; - let context_module = ctx.scope.module(); + let context_module = ctx.module; match ctx.completion_location { Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => { @@ -75,7 +75,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon match kind { Some(PathKind::Vis { .. }) => { if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { - if let Some(current_module) = ctx.scope.module() { + if let Some(current_module) = ctx.module { if let Some(next) = current_module .path_to_root(ctx.db) .into_iter() @@ -189,7 +189,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon ty } hir::ModuleDef::BuiltinType(builtin) => { - let module = match ctx.scope.module() { + let module = match ctx.module { Some(it) => it, None => return, }; @@ -205,10 +205,17 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon let krate = ctx.krate; if let Some(krate) = krate { let traits_in_scope = ctx.scope.visible_traits(); - ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { - add_assoc_item(acc, ctx, item); - None::<()> - }); + ty.iterate_path_candidates( + ctx.db, + krate, + &traits_in_scope, + ctx.module, + None, + |_ty, item| { + add_assoc_item(acc, ctx, item); + None::<()> + }, + ); // Iterate assoc types separately ty.iterate_assoc_items(ctx.db, krate, |item| { @@ -239,14 +246,21 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon let traits_in_scope = ctx.scope.visible_traits(); let mut seen = FxHashSet::default(); - ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { - // We might iterate candidates of a trait multiple times here, so deduplicate - // them. - if seen.insert(item) { - add_assoc_item(acc, ctx, item); - } - None::<()> - }); + ty.iterate_path_candidates( + ctx.db, + krate, + &traits_in_scope, + ctx.module, + None, + |_ty, item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if seen.insert(item) { + add_assoc_item(acc, ctx, item); + } + None::<()> + }, + ); } } hir::PathResolution::Macro(mac) => acc.add_macro(ctx, None, mac), diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index b066a46065d..ec1ee292be4 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs @@ -59,8 +59,7 @@ pub(crate) fn complete_record_literal( } if let hir::Adt::Struct(strukt) = ctx.expected_type.as_ref()?.as_adt()? { - let module = - if let Some(module) = ctx.scope.module() { module } else { strukt.module(ctx.db) }; + let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) }; let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt)); diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 59c16c08d6c..a31a552dadb 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -103,6 +103,8 @@ pub(crate) struct CompletionContext<'a> { pub(super) token: SyntaxToken, /// The crate of the current file. pub(super) krate: Option, + /// The crate of the `scope`. + pub(super) module: Option, pub(super) expected_name: Option, pub(super) expected_type: Option, @@ -338,7 +340,7 @@ fn is_visible_impl( attrs: &hir::Attrs, defining_crate: hir::Crate, ) -> bool { - let module = match self.scope.module() { + let module = match self.module { Some(it) => it, None => return false, }; @@ -394,6 +396,7 @@ pub(super) fn new( let token = sema.descend_into_macros_single(original_token.clone()); let scope = sema.scope_at_offset(&token.parent()?, offset); let krate = scope.krate(); + let module = scope.module(); let mut locals = vec![]; scope.process_all_names(&mut |name, scope| { if let ScopeDef::Local(local) = scope { @@ -410,6 +413,7 @@ pub(super) fn new( original_token, token, krate, + module, expected_name: None, expected_type: None, function_def: None, diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs index e486d9f2b91..641a15aa65d 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs @@ -144,7 +144,7 @@ fn visible_fields( fields: &[hir::Field], item: impl HasAttrs, ) -> Option<(Vec, bool)> { - let module = ctx.completion.scope.module()?; + let module = ctx.completion.module?; let n_fields = fields.len(); let fields = fields .iter() diff --git a/crates/ide_completion/src/render/struct_literal.rs b/crates/ide_completion/src/render/struct_literal.rs index bfbcc263b1d..6be7b9d43bc 100644 --- a/crates/ide_completion/src/render/struct_literal.rs +++ b/crates/ide_completion/src/render/struct_literal.rs @@ -119,7 +119,7 @@ fn visible_fields( fields: &[hir::Field], item: impl HasAttrs, ) -> Option<(Vec, bool)> { - let module = ctx.completion.scope.module()?; + let module = ctx.completion.module?; let n_fields = fields.len(); let fields = fields .iter() diff --git a/crates/ide_completion/src/snippet.rs b/crates/ide_completion/src/snippet.rs index 98cd3f8f337..3a4f713333b 100644 --- a/crates/ide_completion/src/snippet.rs +++ b/crates/ide_completion/src/snippet.rs @@ -185,11 +185,8 @@ fn import_edits( hir::PathResolution::Def(def) => def.into(), _ => return None, }; - let path = ctx.scope.module()?.find_use_path_prefixed( - ctx.db, - item, - ctx.config.insert_use.prefix_kind, - )?; + let path = + ctx.module?.find_use_path_prefixed(ctx.db, item, ctx.config.insert_use.prefix_kind)?; Some((path.len() > 1).then(|| ImportEdit { import: LocatedImport::new(path.clone(), item, item, None), scope: import_scope.clone(), diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 2356750bceb..c037c3e0f87 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -505,6 +505,7 @@ fn trait_applicable_items( current_crate, &trait_candidates, None, + None, |_, assoc| { if required_assoc_items.contains(&assoc) { if let AssocItem::Function(f) = assoc { @@ -531,6 +532,7 @@ fn trait_applicable_items( current_crate, &trait_candidates, None, + None, |_, function| { let assoc = function.as_assoc_item(db)?; if required_assoc_items.contains(&assoc) { diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs index 7902295d290..844b19779a8 100644 --- a/crates/ide_ssr/src/resolving.rs +++ b/crates/ide_ssr/src/resolving.rs @@ -219,10 +219,12 @@ fn resolve_path(&self, path: &ast::Path) -> Option { let resolved_qualifier = self.scope.speculative_resolve(&path.qualifier()?)?; if let hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) = resolved_qualifier { let name = path.segment()?.name_ref()?; + let module = self.scope.module()?; adt.ty(self.scope.db).iterate_path_candidates( self.scope.db, - self.scope.module()?.krate(), + module.krate(), &self.scope.visible_traits(), + Some(module), None, |_ty, assoc_item| { let item_name = assoc_item.name(self.scope.db)?;