-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
//
//
// | 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,
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)]
fn goto_implementation_works() {
check(
r#"
-struct Foo<|>;
+struct Foo$0;
impl Foo {}
//^^^
"#,
fn goto_implementation_works_multiple_blocks() {
check(
r#"
-struct Foo<|>;
+struct Foo$0;
impl Foo {}
//^^^
impl Foo {}
fn goto_implementation_works_multiple_mods() {
check(
r#"
-struct Foo<|>;
+struct Foo$0;
mod a {
impl super::Foo {}
//^^^^^^^^^^
check(
r#"
//- /lib.rs
-struct Foo<|>;
+struct Foo$0;
mod a;
mod b;
//- /a.rs
fn goto_implementation_for_trait() {
check(
r#"
-trait T<|> {}
+trait T$0 {}
struct Foo;
impl T for Foo {}
//^^^
check(
r#"
//- /lib.rs
-trait T<|> {};
+trait T$0 {};
struct Foo;
mod a;
mod b;
r#"
//- /lib.rs
trait T {}
-struct Foo<|>;
+struct Foo$0;
impl Foo {}
//^^^
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;
+ //^
}
"#,
);