]> 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 a88854d05c44fe9e35ada5d0839b52e22c6606b8..d1101230452f7b5a3f25d57446c87a61997cf223 100644 (file)
@@ -1,8 +1,11 @@
-use hir::{Crate, ImplDef, 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::ToNav, FilePosition, NavigationTarget, RangeInfo};
+use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
 
 // Feature: Go to Implementation
 //
@@ -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,65 +26,82 @@ 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 = ImplDef::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)))
-            .map(|imp| imp.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 = ImplDef::for_trait(sema.db, krate, tr);
-
-    Some(impls.into_iter().map(|imp| imp.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)]
 mod tests {
-    use base_db::FileRange;
+    use ide_db::base_db::FileRange;
 
-    use crate::mock_analysis::analysis_and_annotations;
+    use crate::fixture;
 
     fn check(ra_fixture: &str) {
-        let (analysis, position, annotations) = analysis_and_annotations(ra_fixture);
+        let (analysis, position, annotations) = fixture::annotations(ra_fixture);
 
         let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
 
@@ -107,7 +129,7 @@ fn check(ra_fixture: &str) {
     fn goto_implementation_works() {
         check(
             r#"
-struct Foo<|>;
+struct Foo$0;
 impl Foo {}
    //^^^
 "#,
@@ -118,7 +140,7 @@ impl Foo {}
     fn goto_implementation_works_multiple_blocks() {
         check(
             r#"
-struct Foo<|>;
+struct Foo$0;
 impl Foo {}
    //^^^
 impl Foo {}
@@ -131,7 +153,7 @@ impl Foo {}
     fn goto_implementation_works_multiple_mods() {
         check(
             r#"
-struct Foo<|>;
+struct Foo$0;
 mod a {
     impl super::Foo {}
        //^^^^^^^^^^
@@ -149,7 +171,7 @@ fn goto_implementation_works_multiple_files() {
         check(
             r#"
 //- /lib.rs
-struct Foo<|>;
+struct Foo$0;
 mod a;
 mod b;
 //- /a.rs
@@ -166,7 +188,7 @@ impl crate::Foo {}
     fn goto_implementation_for_trait() {
         check(
             r#"
-trait T<|> {}
+trait T$0 {}
 struct Foo;
 impl T for Foo {}
          //^^^
@@ -179,7 +201,7 @@ fn goto_implementation_for_trait_multiple_files() {
         check(
             r#"
 //- /lib.rs
-trait T<|> {};
+trait T$0 {};
 struct Foo;
 mod a;
 mod b;
@@ -199,7 +221,7 @@ fn goto_implementation_all_impls() {
             r#"
 //- /lib.rs
 trait T {}
-struct Foo<|>;
+struct Foo$0;
 impl Foo {}
    //^^^
 impl T for Foo {}
@@ -214,12 +236,91 @@ impl T for &Foo {}
     fn goto_implementation_to_builtin_derive() {
         check(
             r#"
+//- minicore: copy, derive
   #[derive(Copy)]
 //^^^^^^^^^^^^^^^
-struct Foo<|>;
+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;
+        //^
 }
 "#,
         );