From f0fdc9d5c0b5c8712bbd94da20289fda4259d793 Mon Sep 17 00:00:00 2001 From: kjeremy Date: Thu, 31 Jan 2019 18:34:52 -0500 Subject: Go To Implementation for Trait --- crates/ra_hir/src/source_binder.rs | 15 +++++- crates/ra_hir/src/ty/method_resolution.rs | 67 +++++++++++++++++------- crates/ra_ide_api/src/impls.rs | 84 ++++++++++++++++++++++++++++--- 3 files changed, 140 insertions(+), 26 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index d1eaccf23..a1b94ed9c 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -14,7 +14,7 @@ use ra_syntax::{ use crate::{ HirDatabase, Function, ModuleDef, Struct, Enum, - AsName, Module, HirFileId, Crate, + AsName, Module, HirFileId, Crate, Trait, ids::{LocationCtx, SourceFileItemId}, }; @@ -151,6 +151,19 @@ pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::E } } +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, diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 37bc3f38c..e857d6856 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -7,17 +7,18 @@ use std::sync::Arc; 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 { @@ -37,6 +38,7 @@ pub struct CrateImplBlocks { /// To make sense of the ModuleIds, we need the source root. krate: Crate, impls: FxHashMap>, + impls_by_trait: FxHashMap>, } impl CrateImplBlocks { @@ -60,27 +62,53 @@ impl CrateImplBlocks { }) } + pub fn lookup_impl_blocks_for_trait<'a>( + &'a self, + db: &'a impl HirDatabase, + tr: &Trait, + ) -> impl Iterator + '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)); } @@ -99,6 +127,7 @@ impl CrateImplBlocks { 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); diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs index 469d56d63..91fa41f1f 100644 --- a/crates/ra_ide_api/src/impls.rs +++ b/crates/ra_ide_api/src/impls.rs @@ -15,9 +15,27 @@ pub(crate) fn goto_implementation( let syntax = file.syntax(); let module = source_binder::module_from_position(db, position)?; - let krate = module.krate(db)?; - let node = find_node_at_offset::(syntax, position.offset)?; + if let Some(nominal_def) = find_node_at_offset::(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::(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> { let ty = match node.kind() { ast::NominalDefKind::StructDef(def) => { source_binder::struct_from_module(db, module, &def).ty(db) @@ -27,13 +45,33 @@ pub(crate) fn goto_implementation( } }; + 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> { + 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)] @@ -117,4 +155,38 @@ mod tests { ], ); } + + #[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)", + ], + ); + } } -- cgit v1.2.3