]> git.lizzy.rs Git - rust.git/commitdiff
Go To Implementation for Trait
authorkjeremy <kjeremy@gmail.com>
Thu, 31 Jan 2019 23:34:52 +0000 (18:34 -0500)
committerkjeremy <kjeremy@gmail.com>
Thu, 31 Jan 2019 23:34:52 +0000 (18:34 -0500)
crates/ra_hir/src/source_binder.rs
crates/ra_hir/src/ty/method_resolution.rs
crates/ra_ide_api/src/impls.rs

index d1eaccf230190761a91270fd40fd4f207f880567..a1b94ed9c9cfed493c299531dffa4bd212b44e9d 100644 (file)
@@ -14,7 +14,7 @@
 
 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,
index 37bc3f38ce49c16327ef2bf812519079f22e5be3..e857d6856eb2e11bc07717ea61b1c4701803c144 100644 (file)
@@ -7,17 +7,18 @@
 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<TyFingerprint, Vec<(ModuleId, ImplId)>>,
+    impls_by_trait: FxHashMap<TraitId, Vec<(ModuleId, ImplId)>>,
 }
 
 impl CrateImplBlocks {
@@ -60,27 +62,53 @@ pub fn lookup_impl_blocks<'a>(
             })
     }
 
+    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));
                 }
@@ -99,6 +127,7 @@ pub(crate) fn impls_in_crate_query(
         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);
index 469d56d63acd091143ba13960a5b4e7f376daa13..91fa41f1f4ba0eef543335d9633afb898f93165a 100644 (file)
@@ -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::<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)
@@ -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<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)]
@@ -117,4 +155,38 @@ impl crate::Foo {}
             ],
         );
     }
+
+    #[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)",
+            ],
+        );
+    }
 }