]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide/src/goto_implementation.rs
Explicitly check for reference locals or fields in Name classification
[rust.git] / crates / ide / src / goto_implementation.rs
index 761a98b2cabfeeacf135f2c8690c554a54cecfed..d1101230452f7b5a3f25d57446c87a61997cf223 100644 (file)
@@ -1,6 +1,9 @@
-use hir::{Crate, Impl, Semantics};
-use ide_db::RootDatabase;
-use syntax::{algo::find_node_at_offset, ast, AstNode};
+use hir::{AsAssocItem, Impl, Semantics};
+use ide_db::{
+    defs::{Definition, NameClass, NameRefClass},
+    RootDatabase,
+};
+use syntax::{ast, AstNode};
 
 use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
 
@@ -13,6 +16,8 @@
 //
 // | VS Code | kbd:[Ctrl+F12]
 // |===
+//
+// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
 pub(crate) fn goto_implementation(
     db: &RootDatabase,
     position: FilePosition,
@@ -21,55 +26,72 @@ pub(crate) fn goto_implementation(
     let source_file = sema.parse(position.file_id);
     let syntax = source_file.syntax().clone();
 
-    let krate = sema.to_module_def(position.file_id)?.krate();
-
-    if let Some(nominal_def) = find_node_at_offset::<ast::AdtDef>(&syntax, position.offset) {
-        return Some(RangeInfo::new(
-            nominal_def.syntax().text_range(),
-            impls_for_def(&sema, &nominal_def, krate)?,
-        ));
-    } else if let Some(trait_def) = find_node_at_offset::<ast::Trait>(&syntax, position.offset) {
-        return Some(RangeInfo::new(
-            trait_def.syntax().text_range(),
-            impls_for_trait(&sema, &trait_def, krate)?,
-        ));
-    }
-
-    None
-}
+    let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?;
+    let def = match &node {
+        ast::NameLike::Name(name) => {
+            NameClass::classify(&sema, name).map(|class| class.defined_or_referenced_local())
+        }
+        ast::NameLike::NameRef(name_ref) => {
+            NameRefClass::classify(&sema, name_ref).map(|class| class.referenced_local())
+        }
+        ast::NameLike::Lifetime(_) => None,
+    }?;
 
-fn impls_for_def(
-    sema: &Semantics<RootDatabase>,
-    node: &ast::AdtDef,
-    krate: Crate,
-) -> Option<Vec<NavigationTarget>> {
-    let ty = match node {
-        ast::AdtDef::Struct(def) => sema.to_def(def)?.ty(sema.db),
-        ast::AdtDef::Enum(def) => sema.to_def(def)?.ty(sema.db),
-        ast::AdtDef::Union(def) => sema.to_def(def)?.ty(sema.db),
+    let def = match def {
+        Definition::ModuleDef(def) => def,
+        _ => return None,
+    };
+    let navs = match def {
+        hir::ModuleDef::Trait(trait_) => impls_for_trait(&sema, trait_),
+        hir::ModuleDef::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
+        hir::ModuleDef::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
+        hir::ModuleDef::BuiltinType(builtin) => {
+            let module = sema.to_module_def(position.file_id)?;
+            impls_for_ty(&sema, builtin.ty(sema.db, module))
+        }
+        hir::ModuleDef::Function(f) => {
+            let assoc = f.as_assoc_item(sema.db)?;
+            let name = assoc.name(sema.db)?;
+            let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
+            impls_for_trait_item(&sema, trait_, name)
+        }
+        hir::ModuleDef::Const(c) => {
+            let assoc = c.as_assoc_item(sema.db)?;
+            let name = assoc.name(sema.db)?;
+            let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
+            impls_for_trait_item(&sema, trait_, name)
+        }
+        _ => return None,
     };
+    Some(RangeInfo { range: node.syntax().text_range(), info: navs })
+}
 
-    let impls = Impl::all_in_crate(sema.db, krate);
+fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> {
+    Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()
+}
 
-    Some(
-        impls
-            .into_iter()
-            .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db)))
-            .filter_map(|imp| imp.try_to_nav(sema.db))
-            .collect(),
-    )
+fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> {
+    Impl::all_for_trait(sema.db, trait_)
+        .into_iter()
+        .filter_map(|imp| imp.try_to_nav(sema.db))
+        .collect()
 }
 
-fn impls_for_trait(
+fn impls_for_trait_item(
     sema: &Semantics<RootDatabase>,
-    node: &ast::Trait,
-    krate: Crate,
-) -> Option<Vec<NavigationTarget>> {
-    let tr = sema.to_def(node)?;
-
-    let impls = Impl::for_trait(sema.db, krate, tr);
-
-    Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect())
+    trait_: hir::Trait,
+    fun_name: hir::Name,
+) -> Vec<NavigationTarget> {
+    Impl::all_for_trait(sema.db, trait_)
+        .into_iter()
+        .filter_map(|imp| {
+            let item = imp.items(sema.db).iter().find_map(|itm| {
+                let itm_name = itm.name(sema.db)?;
+                (itm_name == fun_name).then(|| *itm)
+            })?;
+            item.try_to_nav(sema.db)
+        })
+        .collect()
 }
 
 #[cfg(test)]
@@ -214,15 +236,92 @@ impl T for &Foo {}
     fn goto_implementation_to_builtin_derive() {
         check(
             r#"
+//- minicore: copy, derive
   #[derive(Copy)]
 //^^^^^^^^^^^^^^^
 struct Foo$0;
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_implementation_type_alias() {
+        check(
+            r#"
+struct Foo;
+
+type Bar$0 = Foo;
+
+impl Foo {}
+   //^^^
+impl Bar {}
+   //^^^
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_implementation_adt_generic() {
+        check(
+            r#"
+struct Foo$0<T>;
+
+impl<T> Foo<T> {}
+      //^^^^^^
+impl Foo<str> {}
+   //^^^^^^^^
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_implementation_builtin() {
+        check(
+            r#"
+//- /lib.rs crate:main deps:core
+fn foo(_: bool$0) {{}}
+//- /libcore.rs crate:core
+#[lang = "bool"]
+impl bool {}
+   //^^^^
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_implementation_trait_functions() {
+        check(
+            r#"
+trait Tr {
+    fn f$0();
+}
+
+struct S;
+
+impl Tr for S {
+    fn f() {
+     //^
+        println!("Hello, world!");
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_implementation_trait_assoc_const() {
+        check(
+            r#"
+trait Tr {
+    const C$0: usize;
+}
+
+struct S;
 
-mod marker {
-    trait Copy {}
+impl Tr for S {
+    const C: usize = 4;
+        //^
 }
-#[rustc_builtin_macro]
-macro Copy {}
 "#,
         );
     }