11393: fix: Complete functions and methods from block level impls r=Veykril a=Veykril
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/11372
Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
db: &dyn HirDatabase,
krate: Crate,
traits_in_scope: &FxHashSet<TraitId>,
+ with_local_impls: Option<Module>,
name: Option<&Name>,
mut callback: impl FnMut(Type, Function) -> Option<T>,
) -> Option<T> {
db,
krate,
traits_in_scope,
+ with_local_impls,
name,
&mut |ty, assoc_item_id| {
if let AssocItemId::FunctionId(func) = assoc_item_id {
db: &dyn HirDatabase,
krate: Crate,
traits_in_scope: &FxHashSet<TraitId>,
+ with_local_impls: Option<Module>,
name: Option<&Name>,
callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
) {
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),
db: &dyn HirDatabase,
krate: Crate,
traits_in_scope: &FxHashSet<TraitId>,
+ with_local_impls: Option<Module>,
name: Option<&Name>,
mut callback: impl FnMut(Type, AssocItem) -> Option<T>,
) -> Option<T> {
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()) {
db: &dyn HirDatabase,
krate: Crate,
traits_in_scope: &FxHashSet<TraitId>,
+ with_local_impls: Option<Module>,
name: Option<&Name>,
callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>,
) {
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),
self.trait_env.clone(),
krate,
&traits_in_scope,
- self.resolver.module(),
+ self.resolver.module().into(),
method_name,
)
});
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| {
env: Arc<TraitEnvironment>,
krate: CrateId,
traits_in_scope: &FxHashSet<TraitId>,
- visible_from_module: Option<ModuleId>,
+ visible_from_module: VisibleFromModule,
name: &Name,
) -> Option<(Canonical<Ty>, FunctionId)> {
iterate_method_candidates(
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<Option<ModuleId>> for VisibleFromModule {
+ fn from(module: Option<ModuleId>) -> Self {
+ match module {
+ Some(module) => Self::Filter(module),
+ None => Self::None,
+ }
+ }
+}
+
+impl From<Option<BlockId>> for VisibleFromModule {
+ fn from(block: Option<BlockId>) -> 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?
env: Arc<TraitEnvironment>,
krate: CrateId,
traits_in_scope: &FxHashSet<TraitId>,
- visible_from_module: Option<ModuleId>,
+ visible_from_module: VisibleFromModule,
name: Option<&Name>,
mode: LookupMode,
mut callback: impl FnMut(&Canonical<Ty>, AssocItemId) -> Option<T>,
env: Arc<TraitEnvironment>,
krate: CrateId,
traits_in_scope: &FxHashSet<TraitId>,
- visible_from_module: Option<ModuleId>,
+ visible_from_module: VisibleFromModule,
name: Option<&Name>,
mode: LookupMode,
callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
env: Arc<TraitEnvironment>,
krate: CrateId,
traits_in_scope: &FxHashSet<TraitId>,
- visible_from_module: Option<ModuleId>,
+ visible_from_module: VisibleFromModule,
name: Option<&Name>,
mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
) -> ControlFlow<()> {
env: Arc<TraitEnvironment>,
krate: CrateId,
traits_in_scope: &FxHashSet<TraitId>,
- visible_from_module: Option<ModuleId>,
+ visible_from_module: VisibleFromModule,
name: Option<&Name>,
mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
) -> ControlFlow<()> {
env: Arc<TraitEnvironment>,
krate: CrateId,
traits_in_scope: &FxHashSet<TraitId>,
- visible_from_module: Option<ModuleId>,
+ visible_from_module: VisibleFromModule,
name: Option<&Name>,
mut callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
) -> ControlFlow<()> {
name: Option<&Name>,
receiver_ty: Option<&Canonical<Ty>>,
krate: CrateId,
- visible_from_module: Option<ModuleId>,
+ visible_from_module: VisibleFromModule,
callback: &mut dyn FnMut(&Canonical<Ty>, AssocItemId) -> ControlFlow<()>,
) -> ControlFlow<()> {
let def_crates = match def_crates(db, &self_ty.value, krate) {
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(());
sema.db,
krate,
&traits_in_scope,
+ None,
Some(&wanted_method),
|_, func| {
if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
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)]
) {
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 {
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(
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::<()>
+ },
+ );
}
}
);
}
+ #[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(
}
fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> {
- 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,
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);
None => return,
};
- let context_module = ctx.scope.module();
+ let context_module = ctx.module;
match ctx.completion_location {
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
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()
ty
}
hir::ModuleDef::BuiltinType(builtin) => {
- let module = match ctx.scope.module() {
+ let module = match ctx.module {
Some(it) => it,
None => return,
};
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| {
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),
}
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));
pub(super) token: SyntaxToken,
/// The crate of the current file.
pub(super) krate: Option<hir::Crate>,
+ /// The crate of the `scope`.
+ pub(super) module: Option<hir::Module>,
pub(super) expected_name: Option<NameOrNameRef>,
pub(super) expected_type: Option<Type>,
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,
};
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 {
original_token,
token,
krate,
+ module,
expected_name: None,
expected_type: None,
function_def: None,
fields: &[hir::Field],
item: impl HasAttrs,
) -> Option<(Vec<hir::Field>, bool)> {
- let module = ctx.completion.scope.module()?;
+ let module = ctx.completion.module?;
let n_fields = fields.len();
let fields = fields
.iter()
fields: &[hir::Field],
item: impl HasAttrs,
) -> Option<(Vec<hir::Field>, bool)> {
- let module = ctx.completion.scope.module()?;
+ let module = ctx.completion.module?;
let n_fields = fields.len();
let fields = fields
.iter()
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(),
current_crate,
&trait_candidates,
None,
+ None,
|_, assoc| {
if required_assoc_items.contains(&assoc) {
if let AssocItem::Function(f) = assoc {
current_crate,
&trait_candidates,
None,
+ None,
|_, function| {
let assoc = function.as_assoc_item(db)?;
if required_assoc_items.contains(&assoc) {
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)?;