use crate::{
HirDatabase, Function, ModuleDef, Struct, Enum,
- AsName, Module, HirFileId, Crate,
+ AsName, Module, HirFileId, Crate, Trait,
ids::{LocationCtx, SourceFileItemId},
};
}
}
+pub fn trait_from_module(
+ db: &impl HirDatabase,
+ module: Module,
+ trait_def: &ast::TraitDef,
+) -> Trait {
+ let (file_id, _) = module.definition_source(db);
+ let file_id = file_id.into();
+ let ctx = LocationCtx::new(db, module, file_id);
+ Trait {
+ id: ctx.to_def(trait_def),
+ }
+}
+
pub fn macro_symbols(db: &impl HirDatabase, file_id: FileId) -> Vec<(SmolStr, TextRange)> {
let module = match module_from_file_id(db, file_id) {
Some(it) => it,
use rustc_hash::FxHashMap;
use crate::{
- HirDatabase, module_tree::ModuleId, Module, Crate, Name, Function,
+ HirDatabase, module_tree::ModuleId, Module, ModuleDef, Crate, Name, Function, Trait,
+ ids::TraitId,
impl_block::{ImplId, ImplBlock, ImplItem},
generics::GenericParams,
- ty::{AdtDef, Ty}
+ ty::{AdtDef, Ty},
+ type_ref::TypeRef,
};
/// This is used as a key for indexing impls.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TyFingerprint {
- Adt(AdtDef),
- // we'll also want to index impls for primitive types etc.
+ Adt(AdtDef), // we'll also want to index impls for primitive types etc.
}
impl TyFingerprint {
/// To make sense of the ModuleIds, we need the source root.
krate: Crate,
impls: FxHashMap<TyFingerprint, Vec<(ModuleId, ImplId)>>,
+ impls_by_trait: FxHashMap<TraitId, Vec<(ModuleId, ImplId)>>,
}
impl CrateImplBlocks {
})
}
+ pub fn lookup_impl_blocks_for_trait<'a>(
+ &'a self,
+ db: &'a impl HirDatabase,
+ tr: &Trait,
+ ) -> impl Iterator<Item = (Module, ImplBlock)> + 'a {
+ let id = tr.id;
+ self.impls_by_trait
+ .get(&id)
+ .into_iter()
+ .flat_map(|i| i.iter())
+ .map(move |(module_id, impl_id)| {
+ let module = Module {
+ krate: self.krate,
+ module_id: *module_id,
+ };
+ let module_impl_blocks = db.impls_in_module(module);
+ (module, ImplBlock::from_id(module_impl_blocks, *impl_id))
+ })
+ }
+
fn collect_recursive(&mut self, db: &impl HirDatabase, module: &Module) {
let module_impl_blocks = db.impls_in_module(module.clone());
for (impl_id, impl_data) in module_impl_blocks.impls.iter() {
let impl_block = ImplBlock::from_id(Arc::clone(&module_impl_blocks), impl_id);
+ // TODO provide generics of impl
+ let generics = GenericParams::default();
+ let target_ty = Ty::from_hir(
+ db,
+ &module,
+ Some(&impl_block),
+ &generics,
+ impl_data.target_type(),
+ );
+
+ if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
+ self.impls
+ .entry(target_ty_fp)
+ .or_insert_with(Vec::new)
+ .push((module.module_id, impl_id));
+ }
- if let Some(_target_trait) = impl_data.target_trait() {
- // ignore for now
- } else {
- // TODO provide generics of impl
- let generics = GenericParams::default();
- let target_ty = Ty::from_hir(
- db,
- &module,
- Some(&impl_block),
- &generics,
- impl_data.target_type(),
- );
- if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
- self.impls
- .entry(target_ty_fp)
+ if let Some(TypeRef::Path(path)) = impl_data.target_trait() {
+ let perns = module.resolve_path(db, path);
+ if let Some(ModuleDef::Trait(tr)) = perns.take_types() {
+ self.impls_by_trait
+ .entry(tr.id)
.or_insert_with(Vec::new)
.push((module.module_id, impl_id));
}
let mut crate_impl_blocks = CrateImplBlocks {
krate: krate.clone(),
impls: FxHashMap::default(),
+ impls_by_trait: FxHashMap::default(),
};
if let Some(module) = krate.root_module(db) {
crate_impl_blocks.collect_recursive(db, &module);
let syntax = file.syntax();
let module = source_binder::module_from_position(db, position)?;
- let krate = module.krate(db)?;
- let node = find_node_at_offset::<ast::NominalDef>(syntax, position.offset)?;
+ if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(syntax, position.offset) {
+ return Some(RangeInfo::new(
+ nominal_def.syntax().range(),
+ impls_for_def(db, nominal_def, module)?,
+ ));
+ } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(syntax, position.offset) {
+ return Some(RangeInfo::new(
+ trait_def.syntax().range(),
+ impls_for_trait(db, trait_def, module)?,
+ ));
+ }
+
+ None
+}
+
+fn impls_for_def(
+ db: &RootDatabase,
+ node: &ast::NominalDef,
+ module: hir::Module,
+) -> Option<Vec<NavigationTarget>> {
let ty = match node.kind() {
ast::NominalDefKind::StructDef(def) => {
source_binder::struct_from_module(db, module, &def).ty(db)
}
};
+ let krate = module.krate(db)?;
let impls = db.impls_in_crate(krate);
- let navs = impls
- .lookup_impl_blocks(db, &ty)
- .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp));
+ Some(
+ impls
+ .lookup_impl_blocks(db, &ty)
+ .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp))
+ .collect(),
+ )
+}
+
+fn impls_for_trait(
+ db: &RootDatabase,
+ node: &ast::TraitDef,
+ module: hir::Module,
+) -> Option<Vec<NavigationTarget>> {
+ let tr = source_binder::trait_from_module(db, module, node);
- Some(RangeInfo::new(node.syntax().range(), navs.collect()))
+ let krate = module.krate(db)?;
+ let impls = db.impls_in_crate(krate);
+
+ Some(
+ impls
+ .lookup_impl_blocks_for_trait(db, &tr)
+ .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp))
+ .collect(),
+ )
}
#[cfg(test)]
],
);
}
+
+ #[test]
+ fn goto_implementation_for_trait() {
+ check_goto(
+ "
+ //- /lib.rs
+ trait T<|> {}
+ struct Foo;
+ impl T for Foo {}
+ ",
+ &["impl IMPL_BLOCK FileId(1) [23; 40)"],
+ );
+ }
+
+ #[test]
+ fn goto_implementation_for_trait_multiple_files() {
+ check_goto(
+ "
+ //- /lib.rs
+ trait T<|> {};
+ struct Foo;
+ mod a;
+ mod b;
+ //- /a.rs
+ impl crate::T for crate::Foo {}
+ //- /b.rs
+ impl crate::T for crate::Foo {}
+ ",
+ &[
+ "impl IMPL_BLOCK FileId(2) [0; 31)",
+ "impl IMPL_BLOCK FileId(3) [0; 31)",
+ ],
+ );
+ }
}