Skip to content

Commit 6c782a5

Browse files
committed
Power up goto_implementation
by allowing it to be invoked on references of names, showing all (trait) implementations of the given type in all crates including builtin types
1 parent de36027 commit 6c782a5

File tree

3 files changed

+123
-62
lines changed

3 files changed

+123
-62
lines changed

crates/hir/src/lib.rs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -696,8 +696,8 @@ impl Adt {
696696
}
697697
}
698698

699-
pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
700-
Some(self.module(db).krate())
699+
pub fn krate(self, db: &dyn HirDatabase) -> Crate {
700+
self.module(db).krate()
701701
}
702702

703703
pub fn name(self, db: &dyn HirDatabase) -> Name {
@@ -1019,8 +1019,8 @@ impl TypeAlias {
10191019
Module { id: self.id.lookup(db.upcast()).module(db.upcast()) }
10201020
}
10211021

1022-
pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
1023-
Some(self.module(db).krate())
1022+
pub fn krate(self, db: &dyn HirDatabase) -> Crate {
1023+
self.module(db).krate()
10241024
}
10251025

10261026
pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
@@ -1483,9 +1483,42 @@ impl Impl {
14831483

14841484
inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
14851485
}
1486-
pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> {
1487-
let impls = db.trait_impls_in_crate(krate.id);
1488-
impls.for_trait(trait_.id).map(Self::from).collect()
1486+
1487+
pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty }: Type) -> Vec<Impl> {
1488+
let def_crates = match ty.value.def_crates(db, krate) {
1489+
Some(def_crates) => def_crates,
1490+
None => return vec![],
1491+
};
1492+
1493+
let filter = |impl_def: &Impl| {
1494+
let target_ty = impl_def.target_ty(db);
1495+
let rref = target_ty.remove_ref();
1496+
ty.value.equals_ctor(rref.as_ref().map_or(&target_ty.ty.value, |it| &it.ty.value))
1497+
};
1498+
1499+
let mut all = Vec::new();
1500+
def_crates.iter().for_each(|&id| {
1501+
all.extend(db.inherent_impls_in_crate(id).all_impls().map(Self::from).filter(filter))
1502+
});
1503+
for id in def_crates
1504+
.iter()
1505+
.flat_map(|&id| Crate { id }.reverse_dependencies(db))
1506+
.map(|Crate { id }| id)
1507+
.chain(def_crates.iter().copied())
1508+
{
1509+
all.extend(db.trait_impls_in_crate(id).all_impls().map(Self::from).filter(filter));
1510+
}
1511+
all
1512+
}
1513+
1514+
pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> {
1515+
let krate = trait_.module(db).krate();
1516+
let mut all = Vec::new();
1517+
for Crate { id } in krate.reverse_dependencies(db).into_iter().chain(Some(krate)) {
1518+
let impls = db.trait_impls_in_crate(id);
1519+
all.extend(impls.for_trait(trait_.id).map(Self::from))
1520+
}
1521+
all
14891522
}
14901523

14911524
// FIXME: the return type is wrong. This should be a hir version of
@@ -1913,12 +1946,6 @@ impl Type {
19131946
self.ty.value.associated_type_parent_trait(db).map(Into::into)
19141947
}
19151948

1916-
// FIXME: provide required accessors such that it becomes implementable from outside.
1917-
pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1918-
let rref = other.remove_ref();
1919-
self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value))
1920-
}
1921-
19221949
fn derived(&self, ty: Ty) -> Type {
19231950
Type {
19241951
krate: self.krate,

crates/ide/src/goto_implementation.rs

Lines changed: 82 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use hir::{Crate, Impl, Semantics};
2-
use ide_db::RootDatabase;
3-
use syntax::{algo::find_node_at_offset, ast, AstNode};
1+
use hir::{Impl, Semantics};
2+
use ide_db::{
3+
defs::{Definition, NameClass, NameRefClass},
4+
RootDatabase,
5+
};
6+
use syntax::{ast, AstNode};
47

58
use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
69

@@ -21,55 +24,42 @@ pub(crate) fn goto_implementation(
2124
let source_file = sema.parse(position.file_id);
2225
let syntax = source_file.syntax().clone();
2326

24-
let krate = sema.to_module_def(position.file_id)?.krate();
25-
26-
if let Some(nominal_def) = find_node_at_offset::<ast::Adt>(&syntax, position.offset) {
27-
return Some(RangeInfo::new(
28-
nominal_def.syntax().text_range(),
29-
impls_for_def(&sema, &nominal_def, krate)?,
30-
));
31-
} else if let Some(trait_def) = find_node_at_offset::<ast::Trait>(&syntax, position.offset) {
32-
return Some(RangeInfo::new(
33-
trait_def.syntax().text_range(),
34-
impls_for_trait(&sema, &trait_def, krate)?,
35-
));
36-
}
37-
38-
None
39-
}
40-
41-
fn impls_for_def(
42-
sema: &Semantics<RootDatabase>,
43-
node: &ast::Adt,
44-
krate: Crate,
45-
) -> Option<Vec<NavigationTarget>> {
46-
let ty = match node {
47-
ast::Adt::Struct(def) => sema.to_def(def)?.ty(sema.db),
48-
ast::Adt::Enum(def) => sema.to_def(def)?.ty(sema.db),
49-
ast::Adt::Union(def) => sema.to_def(def)?.ty(sema.db),
27+
let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?;
28+
let def = match &node {
29+
ast::NameLike::Name(name) => {
30+
NameClass::classify(&sema, name).map(|class| class.referenced_or_defined(sema.db))
31+
}
32+
ast::NameLike::NameRef(name_ref) => {
33+
NameRefClass::classify(&sema, name_ref).map(|class| class.referenced(sema.db))
34+
}
35+
ast::NameLike::Lifetime(_) => None,
36+
}?;
37+
let def = match def {
38+
Definition::ModuleDef(def) => def,
39+
_ => return None,
5040
};
51-
52-
let impls = Impl::all_in_crate(sema.db, krate);
53-
54-
Some(
55-
impls
56-
.into_iter()
57-
.filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db)))
58-
.filter_map(|imp| imp.try_to_nav(sema.db))
59-
.collect(),
60-
)
41+
let navs = match def {
42+
hir::ModuleDef::Trait(trait_) => impls_for_trait(&sema, trait_),
43+
hir::ModuleDef::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
44+
hir::ModuleDef::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
45+
hir::ModuleDef::BuiltinType(builtin) => {
46+
let module = sema.to_module_def(position.file_id)?;
47+
impls_for_ty(&sema, builtin.ty(sema.db, module))
48+
}
49+
_ => return None,
50+
};
51+
Some(RangeInfo { range: node.syntax().text_range(), info: navs })
6152
}
6253

63-
fn impls_for_trait(
64-
sema: &Semantics<RootDatabase>,
65-
node: &ast::Trait,
66-
krate: Crate,
67-
) -> Option<Vec<NavigationTarget>> {
68-
let tr = sema.to_def(node)?;
69-
70-
let impls = Impl::for_trait(sema.db, krate, tr);
54+
fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> {
55+
Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()
56+
}
7157

72-
Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect())
58+
fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> {
59+
Impl::all_for_trait(sema.db, trait_)
60+
.into_iter()
61+
.filter_map(|imp| imp.try_to_nav(sema.db))
62+
.collect()
7363
}
7464

7565
#[cfg(test)]
@@ -223,6 +213,50 @@ mod marker {
223213
}
224214
#[rustc_builtin_macro]
225215
macro Copy {}
216+
"#,
217+
);
218+
}
219+
220+
#[test]
221+
fn goto_implementation_type_alias() {
222+
check(
223+
r#"
224+
struct Foo;
225+
226+
type Bar$0 = Foo;
227+
228+
impl Foo {}
229+
//^^^
230+
impl Bar {}
231+
//^^^
232+
"#,
233+
);
234+
}
235+
236+
#[test]
237+
fn goto_implementation_adt_generic() {
238+
check(
239+
r#"
240+
struct Foo$0<T>;
241+
242+
impl<T> Foo<T> {}
243+
//^^^^^^
244+
impl Foo<str> {}
245+
//^^^^^^^^
246+
"#,
247+
);
248+
}
249+
250+
#[test]
251+
fn goto_implementation_builtin() {
252+
check(
253+
r#"
254+
//- /lib.rs crate:main deps:core
255+
fn foo(_: bool$0) {{}}
256+
//- /libcore.rs crate:core
257+
#[lang = "bool"]
258+
impl bool {}
259+
//^^^^
226260
"#,
227261
);
228262
}

crates/ide/src/inlay_hints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ fn hint_iterator(
219219
let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref())
220220
.last()
221221
.and_then(|strukt| strukt.as_adt())?;
222-
let krate = strukt.krate(db)?;
222+
let krate = strukt.krate(db);
223223
if krate != famous_defs.core()? {
224224
return None;
225225
}

0 commit comments

Comments
 (0)