about summary refs log tree commit diff
path: root/crates/ra_ide_api/src/impls.rs
diff options
context:
space:
mode:
authorkjeremy <kjeremy@gmail.com>2019-01-31 18:34:52 -0500
committerkjeremy <kjeremy@gmail.com>2019-01-31 18:34:52 -0500
commitf0fdc9d5c0b5c8712bbd94da20289fda4259d793 (patch)
tree0015874d918f8be2ec848ab2b110d4478890388b /crates/ra_ide_api/src/impls.rs
parent4c0ab7db85d2084870db4a2f92d92a3ae67a3bb1 (diff)
downloadrust-f0fdc9d5c0b5c8712bbd94da20289fda4259d793.tar.gz
rust-f0fdc9d5c0b5c8712bbd94da20289fda4259d793.zip
Go To Implementation for Trait
Diffstat (limited to 'crates/ra_ide_api/src/impls.rs')
-rw-r--r--crates/ra_ide_api/src/impls.rs84
1 files changed, 78 insertions, 6 deletions
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs
index 469d56d63ac..91fa41f1f4b 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::<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 @@ 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)",
+            ],
+        );
+    }
 }