diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-01 06:00:47 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-01 06:00:47 +0000 |
commit | 9d3f4624e1a83eb945f4df6427fc650356ea77fa (patch) | |
tree | 0015874d918f8be2ec848ab2b110d4478890388b /crates | |
parent | 4c0ab7db85d2084870db4a2f92d92a3ae67a3bb1 (diff) | |
parent | f0fdc9d5c0b5c8712bbd94da20289fda4259d793 (diff) |
Merge #721
721: Go To Implementation for Trait r=matklad a=kjeremy
If on a trait def you can now go to all the impls of that trait in the crate.
This is more of #620.
Co-authored-by: kjeremy <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 15 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 67 | ||||
-rw-r--r-- | crates/ra_ide_api/src/impls.rs | 84 |
3 files changed, 140 insertions, 26 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index d1eaccf23..a1b94ed9c 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -14,7 +14,7 @@ use ra_syntax::{ | |||
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | HirDatabase, Function, ModuleDef, Struct, Enum, | 16 | HirDatabase, Function, ModuleDef, Struct, Enum, |
17 | AsName, Module, HirFileId, Crate, | 17 | AsName, Module, HirFileId, Crate, Trait, |
18 | ids::{LocationCtx, SourceFileItemId}, | 18 | ids::{LocationCtx, SourceFileItemId}, |
19 | }; | 19 | }; |
20 | 20 | ||
@@ -151,6 +151,19 @@ pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::E | |||
151 | } | 151 | } |
152 | } | 152 | } |
153 | 153 | ||
154 | pub fn trait_from_module( | ||
155 | db: &impl HirDatabase, | ||
156 | module: Module, | ||
157 | trait_def: &ast::TraitDef, | ||
158 | ) -> Trait { | ||
159 | let (file_id, _) = module.definition_source(db); | ||
160 | let file_id = file_id.into(); | ||
161 | let ctx = LocationCtx::new(db, module, file_id); | ||
162 | Trait { | ||
163 | id: ctx.to_def(trait_def), | ||
164 | } | ||
165 | } | ||
166 | |||
154 | pub fn macro_symbols(db: &impl HirDatabase, file_id: FileId) -> Vec<(SmolStr, TextRange)> { | 167 | pub fn macro_symbols(db: &impl HirDatabase, file_id: FileId) -> Vec<(SmolStr, TextRange)> { |
155 | let module = match module_from_file_id(db, file_id) { | 168 | let module = match module_from_file_id(db, file_id) { |
156 | Some(it) => it, | 169 | Some(it) => it, |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 37bc3f38c..e857d6856 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -7,17 +7,18 @@ use std::sync::Arc; | |||
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | HirDatabase, module_tree::ModuleId, Module, Crate, Name, Function, | 10 | HirDatabase, module_tree::ModuleId, Module, ModuleDef, Crate, Name, Function, Trait, |
11 | ids::TraitId, | ||
11 | impl_block::{ImplId, ImplBlock, ImplItem}, | 12 | impl_block::{ImplId, ImplBlock, ImplItem}, |
12 | generics::GenericParams, | 13 | generics::GenericParams, |
13 | ty::{AdtDef, Ty} | 14 | ty::{AdtDef, Ty}, |
15 | type_ref::TypeRef, | ||
14 | }; | 16 | }; |
15 | 17 | ||
16 | /// This is used as a key for indexing impls. | 18 | /// This is used as a key for indexing impls. |
17 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | 19 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
18 | pub enum TyFingerprint { | 20 | pub enum TyFingerprint { |
19 | Adt(AdtDef), | 21 | Adt(AdtDef), // we'll also want to index impls for primitive types etc. |
20 | // we'll also want to index impls for primitive types etc. | ||
21 | } | 22 | } |
22 | 23 | ||
23 | impl TyFingerprint { | 24 | impl TyFingerprint { |
@@ -37,6 +38,7 @@ pub struct CrateImplBlocks { | |||
37 | /// To make sense of the ModuleIds, we need the source root. | 38 | /// To make sense of the ModuleIds, we need the source root. |
38 | krate: Crate, | 39 | krate: Crate, |
39 | impls: FxHashMap<TyFingerprint, Vec<(ModuleId, ImplId)>>, | 40 | impls: FxHashMap<TyFingerprint, Vec<(ModuleId, ImplId)>>, |
41 | impls_by_trait: FxHashMap<TraitId, Vec<(ModuleId, ImplId)>>, | ||
40 | } | 42 | } |
41 | 43 | ||
42 | impl CrateImplBlocks { | 44 | impl CrateImplBlocks { |
@@ -60,27 +62,53 @@ impl CrateImplBlocks { | |||
60 | }) | 62 | }) |
61 | } | 63 | } |
62 | 64 | ||
65 | pub fn lookup_impl_blocks_for_trait<'a>( | ||
66 | &'a self, | ||
67 | db: &'a impl HirDatabase, | ||
68 | tr: &Trait, | ||
69 | ) -> impl Iterator<Item = (Module, ImplBlock)> + 'a { | ||
70 | let id = tr.id; | ||
71 | self.impls_by_trait | ||
72 | .get(&id) | ||
73 | .into_iter() | ||
74 | .flat_map(|i| i.iter()) | ||
75 | .map(move |(module_id, impl_id)| { | ||
76 | let module = Module { | ||
77 | krate: self.krate, | ||
78 | module_id: *module_id, | ||
79 | }; | ||
80 | let module_impl_blocks = db.impls_in_module(module); | ||
81 | (module, ImplBlock::from_id(module_impl_blocks, *impl_id)) | ||
82 | }) | ||
83 | } | ||
84 | |||
63 | fn collect_recursive(&mut self, db: &impl HirDatabase, module: &Module) { | 85 | fn collect_recursive(&mut self, db: &impl HirDatabase, module: &Module) { |
64 | let module_impl_blocks = db.impls_in_module(module.clone()); | 86 | let module_impl_blocks = db.impls_in_module(module.clone()); |
65 | 87 | ||
66 | for (impl_id, impl_data) in module_impl_blocks.impls.iter() { | 88 | for (impl_id, impl_data) in module_impl_blocks.impls.iter() { |
67 | let impl_block = ImplBlock::from_id(Arc::clone(&module_impl_blocks), impl_id); | 89 | let impl_block = ImplBlock::from_id(Arc::clone(&module_impl_blocks), impl_id); |
90 | // TODO provide generics of impl | ||
91 | let generics = GenericParams::default(); | ||
92 | let target_ty = Ty::from_hir( | ||
93 | db, | ||
94 | &module, | ||
95 | Some(&impl_block), | ||
96 | &generics, | ||
97 | impl_data.target_type(), | ||
98 | ); | ||
99 | |||
100 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | ||
101 | self.impls | ||
102 | .entry(target_ty_fp) | ||
103 | .or_insert_with(Vec::new) | ||
104 | .push((module.module_id, impl_id)); | ||
105 | } | ||
68 | 106 | ||
69 | if let Some(_target_trait) = impl_data.target_trait() { | 107 | if let Some(TypeRef::Path(path)) = impl_data.target_trait() { |
70 | // ignore for now | 108 | let perns = module.resolve_path(db, path); |
71 | } else { | 109 | if let Some(ModuleDef::Trait(tr)) = perns.take_types() { |
72 | // TODO provide generics of impl | 110 | self.impls_by_trait |
73 | let generics = GenericParams::default(); | 111 | .entry(tr.id) |
74 | let target_ty = Ty::from_hir( | ||
75 | db, | ||
76 | &module, | ||
77 | Some(&impl_block), | ||
78 | &generics, | ||
79 | impl_data.target_type(), | ||
80 | ); | ||
81 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | ||
82 | self.impls | ||
83 | .entry(target_ty_fp) | ||
84 | .or_insert_with(Vec::new) | 112 | .or_insert_with(Vec::new) |
85 | .push((module.module_id, impl_id)); | 113 | .push((module.module_id, impl_id)); |
86 | } | 114 | } |
@@ -99,6 +127,7 @@ impl CrateImplBlocks { | |||
99 | let mut crate_impl_blocks = CrateImplBlocks { | 127 | let mut crate_impl_blocks = CrateImplBlocks { |
100 | krate: krate.clone(), | 128 | krate: krate.clone(), |
101 | impls: FxHashMap::default(), | 129 | impls: FxHashMap::default(), |
130 | impls_by_trait: FxHashMap::default(), | ||
102 | }; | 131 | }; |
103 | if let Some(module) = krate.root_module(db) { | 132 | if let Some(module) = krate.root_module(db) { |
104 | crate_impl_blocks.collect_recursive(db, &module); | 133 | crate_impl_blocks.collect_recursive(db, &module); |
diff --git a/crates/ra_ide_api/src/impls.rs b/crates/ra_ide_api/src/impls.rs index 469d56d63..91fa41f1f 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( | |||
15 | let syntax = file.syntax(); | 15 | let syntax = file.syntax(); |
16 | 16 | ||
17 | let module = source_binder::module_from_position(db, position)?; | 17 | let module = source_binder::module_from_position(db, position)?; |
18 | let krate = module.krate(db)?; | ||
19 | 18 | ||
20 | let node = find_node_at_offset::<ast::NominalDef>(syntax, position.offset)?; | 19 | if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(syntax, position.offset) { |
20 | return Some(RangeInfo::new( | ||
21 | nominal_def.syntax().range(), | ||
22 | impls_for_def(db, nominal_def, module)?, | ||
23 | )); | ||
24 | } else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(syntax, position.offset) { | ||
25 | return Some(RangeInfo::new( | ||
26 | trait_def.syntax().range(), | ||
27 | impls_for_trait(db, trait_def, module)?, | ||
28 | )); | ||
29 | } | ||
30 | |||
31 | None | ||
32 | } | ||
33 | |||
34 | fn impls_for_def( | ||
35 | db: &RootDatabase, | ||
36 | node: &ast::NominalDef, | ||
37 | module: hir::Module, | ||
38 | ) -> Option<Vec<NavigationTarget>> { | ||
21 | let ty = match node.kind() { | 39 | let ty = match node.kind() { |
22 | ast::NominalDefKind::StructDef(def) => { | 40 | ast::NominalDefKind::StructDef(def) => { |
23 | source_binder::struct_from_module(db, module, &def).ty(db) | 41 | source_binder::struct_from_module(db, module, &def).ty(db) |
@@ -27,13 +45,33 @@ pub(crate) fn goto_implementation( | |||
27 | } | 45 | } |
28 | }; | 46 | }; |
29 | 47 | ||
48 | let krate = module.krate(db)?; | ||
30 | let impls = db.impls_in_crate(krate); | 49 | let impls = db.impls_in_crate(krate); |
31 | 50 | ||
32 | let navs = impls | 51 | Some( |
33 | .lookup_impl_blocks(db, &ty) | 52 | impls |
34 | .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp)); | 53 | .lookup_impl_blocks(db, &ty) |
54 | .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp)) | ||
55 | .collect(), | ||
56 | ) | ||
57 | } | ||
58 | |||
59 | fn impls_for_trait( | ||
60 | db: &RootDatabase, | ||
61 | node: &ast::TraitDef, | ||
62 | module: hir::Module, | ||
63 | ) -> Option<Vec<NavigationTarget>> { | ||
64 | let tr = source_binder::trait_from_module(db, module, node); | ||
35 | 65 | ||
36 | Some(RangeInfo::new(node.syntax().range(), navs.collect())) | 66 | let krate = module.krate(db)?; |
67 | let impls = db.impls_in_crate(krate); | ||
68 | |||
69 | Some( | ||
70 | impls | ||
71 | .lookup_impl_blocks_for_trait(db, &tr) | ||
72 | .map(|(module, imp)| NavigationTarget::from_impl_block(db, module, &imp)) | ||
73 | .collect(), | ||
74 | ) | ||
37 | } | 75 | } |
38 | 76 | ||
39 | #[cfg(test)] | 77 | #[cfg(test)] |
@@ -117,4 +155,38 @@ mod tests { | |||
117 | ], | 155 | ], |
118 | ); | 156 | ); |
119 | } | 157 | } |
158 | |||
159 | #[test] | ||
160 | fn goto_implementation_for_trait() { | ||
161 | check_goto( | ||
162 | " | ||
163 | //- /lib.rs | ||
164 | trait T<|> {} | ||
165 | struct Foo; | ||
166 | impl T for Foo {} | ||
167 | ", | ||
168 | &["impl IMPL_BLOCK FileId(1) [23; 40)"], | ||
169 | ); | ||
170 | } | ||
171 | |||
172 | #[test] | ||
173 | fn goto_implementation_for_trait_multiple_files() { | ||
174 | check_goto( | ||
175 | " | ||
176 | //- /lib.rs | ||
177 | trait T<|> {}; | ||
178 | struct Foo; | ||
179 | mod a; | ||
180 | mod b; | ||
181 | //- /a.rs | ||
182 | impl crate::T for crate::Foo {} | ||
183 | //- /b.rs | ||
184 | impl crate::T for crate::Foo {} | ||
185 | ", | ||
186 | &[ | ||
187 | "impl IMPL_BLOCK FileId(2) [0; 31)", | ||
188 | "impl IMPL_BLOCK FileId(3) [0; 31)", | ||
189 | ], | ||
190 | ); | ||
191 | } | ||
120 | } | 192 | } |