diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/add_missing_impl_members.rs | 131 | ||||
-rw-r--r-- | crates/ra_assists/src/doc_tests/generated.rs | 22 |
3 files changed, 129 insertions, 26 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 993aebc47..28152f724 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -46,7 +46,7 @@ pub(crate) enum Assist { | |||
46 | /// | 46 | /// |
47 | /// Note, however, that we don't actually use such two-phase logic at the | 47 | /// Note, however, that we don't actually use such two-phase logic at the |
48 | /// moment, because the LSP API is pretty awkward in this place, and it's much | 48 | /// moment, because the LSP API is pretty awkward in this place, and it's much |
49 | /// easier to just compute the edit eagerly :-)#[derive(Debug, Clone)] | 49 | /// easier to just compute the edit eagerly :-) |
50 | #[derive(Debug)] | 50 | #[derive(Debug)] |
51 | pub(crate) struct AssistCtx<'a, DB> { | 51 | pub(crate) struct AssistCtx<'a, DB> { |
52 | pub(crate) db: &'a DB, | 52 | pub(crate) db: &'a DB, |
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index cef669cb5..bc49e71fe 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
1 | use hir::{db::HirDatabase, HasSource}; | 3 | use hir::{db::HirDatabase, HasSource}; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, edit, make, AstNode, NameOwner}, | 5 | ast::{self, edit, make, AstNode, NameOwner}, |
@@ -17,26 +19,26 @@ enum AddMissingImplMembersMode { | |||
17 | // Adds scaffold for required impl members. | 19 | // Adds scaffold for required impl members. |
18 | // | 20 | // |
19 | // ``` | 21 | // ``` |
20 | // trait T { | 22 | // trait Trait<T> { |
21 | // Type X; | 23 | // Type X; |
22 | // fn foo(&self); | 24 | // fn foo(&self) -> T; |
23 | // fn bar(&self) {} | 25 | // fn bar(&self) {} |
24 | // } | 26 | // } |
25 | // | 27 | // |
26 | // impl T for () {<|> | 28 | // impl Trait<u32> for () {<|> |
27 | // | 29 | // |
28 | // } | 30 | // } |
29 | // ``` | 31 | // ``` |
30 | // -> | 32 | // -> |
31 | // ``` | 33 | // ``` |
32 | // trait T { | 34 | // trait Trait<T> { |
33 | // Type X; | 35 | // Type X; |
34 | // fn foo(&self); | 36 | // fn foo(&self) -> T; |
35 | // fn bar(&self) {} | 37 | // fn bar(&self) {} |
36 | // } | 38 | // } |
37 | // | 39 | // |
38 | // impl T for () { | 40 | // impl Trait<u32> for () { |
39 | // fn foo(&self) { unimplemented!() } | 41 | // fn foo(&self) -> u32 { unimplemented!() } |
40 | // | 42 | // |
41 | // } | 43 | // } |
42 | // ``` | 44 | // ``` |
@@ -54,13 +56,13 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti | |||
54 | // Adds scaffold for overriding default impl members. | 56 | // Adds scaffold for overriding default impl members. |
55 | // | 57 | // |
56 | // ``` | 58 | // ``` |
57 | // trait T { | 59 | // trait Trait { |
58 | // Type X; | 60 | // Type X; |
59 | // fn foo(&self); | 61 | // fn foo(&self); |
60 | // fn bar(&self) {} | 62 | // fn bar(&self) {} |
61 | // } | 63 | // } |
62 | // | 64 | // |
63 | // impl T for () { | 65 | // impl Trait for () { |
64 | // Type X = (); | 66 | // Type X = (); |
65 | // fn foo(&self) {}<|> | 67 | // fn foo(&self) {}<|> |
66 | // | 68 | // |
@@ -68,13 +70,13 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti | |||
68 | // ``` | 70 | // ``` |
69 | // -> | 71 | // -> |
70 | // ``` | 72 | // ``` |
71 | // trait T { | 73 | // trait Trait { |
72 | // Type X; | 74 | // Type X; |
73 | // fn foo(&self); | 75 | // fn foo(&self); |
74 | // fn bar(&self) {} | 76 | // fn bar(&self) {} |
75 | // } | 77 | // } |
76 | // | 78 | // |
77 | // impl T for () { | 79 | // impl Trait for () { |
78 | // Type X = (); | 80 | // Type X = (); |
79 | // fn foo(&self) {} | 81 | // fn foo(&self) {} |
80 | // fn bar(&self) {} | 82 | // fn bar(&self) {} |
@@ -99,7 +101,7 @@ fn add_missing_impl_members_inner( | |||
99 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; | 101 | let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; |
100 | let impl_item_list = impl_node.item_list()?; | 102 | let impl_item_list = impl_node.item_list()?; |
101 | 103 | ||
102 | let trait_def = { | 104 | let (trait_, trait_def) = { |
103 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); | 105 | let analyzer = ctx.source_analyzer(impl_node.syntax(), None); |
104 | 106 | ||
105 | resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? | 107 | resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? |
@@ -132,10 +134,25 @@ fn add_missing_impl_members_inner( | |||
132 | return None; | 134 | return None; |
133 | } | 135 | } |
134 | 136 | ||
137 | let file_id = ctx.frange.file_id; | ||
138 | let db = ctx.db; | ||
139 | |||
135 | ctx.add_assist(AssistId(assist_id), label, |edit| { | 140 | ctx.add_assist(AssistId(assist_id), label, |edit| { |
136 | let n_existing_items = impl_item_list.impl_items().count(); | 141 | let n_existing_items = impl_item_list.impl_items().count(); |
142 | let substs = get_syntactic_substs(impl_node).unwrap_or_default(); | ||
143 | let generic_def: hir::GenericDef = trait_.into(); | ||
144 | let substs_by_param: HashMap<_, _> = generic_def | ||
145 | .params(db) | ||
146 | .into_iter() | ||
147 | // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky | ||
148 | .skip(1) | ||
149 | .zip(substs.into_iter()) | ||
150 | .collect(); | ||
137 | let items = missing_items | 151 | let items = missing_items |
138 | .into_iter() | 152 | .into_iter() |
153 | .map(|it| { | ||
154 | substitute_type_params(db, hir::InFile::new(file_id.into(), it), &substs_by_param) | ||
155 | }) | ||
139 | .map(|it| match it { | 156 | .map(|it| match it { |
140 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), | 157 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), |
141 | _ => it, | 158 | _ => it, |
@@ -160,13 +177,63 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
160 | } | 177 | } |
161 | } | 178 | } |
162 | 179 | ||
180 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the | ||
181 | // trait ref, and then go from the types in the substs back to the syntax) | ||
182 | // FIXME: This should be a general utility (not even just for assists) | ||
183 | fn get_syntactic_substs(impl_block: ast::ImplBlock) -> Option<Vec<ast::TypeRef>> { | ||
184 | let target_trait = impl_block.target_trait()?; | ||
185 | let path_type = match target_trait { | ||
186 | ast::TypeRef::PathType(path) => path, | ||
187 | _ => return None, | ||
188 | }; | ||
189 | let type_arg_list = path_type.path()?.segment()?.type_arg_list()?; | ||
190 | let mut result = Vec::new(); | ||
191 | for type_arg in type_arg_list.type_args() { | ||
192 | let type_arg: ast::TypeArg = type_arg; | ||
193 | result.push(type_arg.type_ref()?); | ||
194 | } | ||
195 | Some(result) | ||
196 | } | ||
197 | |||
198 | // FIXME: This should be a general utility (not even just for assists) | ||
199 | fn substitute_type_params<N: AstNode>( | ||
200 | db: &impl HirDatabase, | ||
201 | node: hir::InFile<N>, | ||
202 | substs: &HashMap<hir::TypeParam, ast::TypeRef>, | ||
203 | ) -> N { | ||
204 | let type_param_replacements = node | ||
205 | .value | ||
206 | .syntax() | ||
207 | .descendants() | ||
208 | .filter_map(ast::TypeRef::cast) | ||
209 | .filter_map(|n| { | ||
210 | let path = match &n { | ||
211 | ast::TypeRef::PathType(path_type) => path_type.path()?, | ||
212 | _ => return None, | ||
213 | }; | ||
214 | let analyzer = hir::SourceAnalyzer::new(db, node.with_value(n.syntax()), None); | ||
215 | let resolution = analyzer.resolve_path(db, &path)?; | ||
216 | match resolution { | ||
217 | hir::PathResolution::TypeParam(tp) => Some((n, substs.get(&tp)?.clone())), | ||
218 | _ => None, | ||
219 | } | ||
220 | }) | ||
221 | .collect::<Vec<_>>(); | ||
222 | |||
223 | if type_param_replacements.is_empty() { | ||
224 | node.value | ||
225 | } else { | ||
226 | edit::replace_descendants(&node.value, type_param_replacements.into_iter()) | ||
227 | } | ||
228 | } | ||
229 | |||
163 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being | 230 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being |
164 | /// implemented) to a `ast::TraitDef`. | 231 | /// implemented) to a `ast::TraitDef`. |
165 | fn resolve_target_trait_def( | 232 | fn resolve_target_trait_def( |
166 | db: &impl HirDatabase, | 233 | db: &impl HirDatabase, |
167 | analyzer: &hir::SourceAnalyzer, | 234 | analyzer: &hir::SourceAnalyzer, |
168 | impl_block: &ast::ImplBlock, | 235 | impl_block: &ast::ImplBlock, |
169 | ) -> Option<ast::TraitDef> { | 236 | ) -> Option<(hir::Trait, ast::TraitDef)> { |
170 | let ast_path = impl_block | 237 | let ast_path = impl_block |
171 | .target_trait() | 238 | .target_trait() |
172 | .map(|it| it.syntax().clone()) | 239 | .map(|it| it.syntax().clone()) |
@@ -174,7 +241,9 @@ fn resolve_target_trait_def( | |||
174 | .path()?; | 241 | .path()?; |
175 | 242 | ||
176 | match analyzer.resolve_path(db, &ast_path) { | 243 | match analyzer.resolve_path(db, &ast_path) { |
177 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).value), | 244 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => { |
245 | Some((def, def.source(db).value)) | ||
246 | } | ||
178 | _ => None, | 247 | _ => None, |
179 | } | 248 | } |
180 | } | 249 | } |
@@ -281,6 +350,40 @@ impl Foo for S { | |||
281 | } | 350 | } |
282 | 351 | ||
283 | #[test] | 352 | #[test] |
353 | fn fill_in_type_params_1() { | ||
354 | check_assist( | ||
355 | add_missing_impl_members, | ||
356 | " | ||
357 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
358 | struct S; | ||
359 | impl Foo<u32> for S { <|> }", | ||
360 | " | ||
361 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
362 | struct S; | ||
363 | impl Foo<u32> for S { | ||
364 | <|>fn foo(&self, t: u32) -> &u32 { unimplemented!() } | ||
365 | }", | ||
366 | ); | ||
367 | } | ||
368 | |||
369 | #[test] | ||
370 | fn fill_in_type_params_2() { | ||
371 | check_assist( | ||
372 | add_missing_impl_members, | ||
373 | " | ||
374 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
375 | struct S; | ||
376 | impl<U> Foo<U> for S { <|> }", | ||
377 | " | ||
378 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | ||
379 | struct S; | ||
380 | impl<U> Foo<U> for S { | ||
381 | <|>fn foo(&self, t: U) -> &U { unimplemented!() } | ||
382 | }", | ||
383 | ); | ||
384 | } | ||
385 | |||
386 | #[test] | ||
284 | fn test_cursor_after_empty_impl_block() { | 387 | fn test_cursor_after_empty_impl_block() { |
285 | check_assist( | 388 | check_assist( |
286 | add_missing_impl_members, | 389 | add_missing_impl_members, |
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 4586eeb59..7d84dc8fb 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -101,26 +101,26 @@ fn doctest_add_impl_default_members() { | |||
101 | check( | 101 | check( |
102 | "add_impl_default_members", | 102 | "add_impl_default_members", |
103 | r#####" | 103 | r#####" |
104 | trait T { | 104 | trait Trait { |
105 | Type X; | 105 | Type X; |
106 | fn foo(&self); | 106 | fn foo(&self); |
107 | fn bar(&self) {} | 107 | fn bar(&self) {} |
108 | } | 108 | } |
109 | 109 | ||
110 | impl T for () { | 110 | impl Trait for () { |
111 | Type X = (); | 111 | Type X = (); |
112 | fn foo(&self) {}<|> | 112 | fn foo(&self) {}<|> |
113 | 113 | ||
114 | } | 114 | } |
115 | "#####, | 115 | "#####, |
116 | r#####" | 116 | r#####" |
117 | trait T { | 117 | trait Trait { |
118 | Type X; | 118 | Type X; |
119 | fn foo(&self); | 119 | fn foo(&self); |
120 | fn bar(&self) {} | 120 | fn bar(&self) {} |
121 | } | 121 | } |
122 | 122 | ||
123 | impl T for () { | 123 | impl Trait for () { |
124 | Type X = (); | 124 | Type X = (); |
125 | fn foo(&self) {} | 125 | fn foo(&self) {} |
126 | fn bar(&self) {} | 126 | fn bar(&self) {} |
@@ -135,25 +135,25 @@ fn doctest_add_impl_missing_members() { | |||
135 | check( | 135 | check( |
136 | "add_impl_missing_members", | 136 | "add_impl_missing_members", |
137 | r#####" | 137 | r#####" |
138 | trait T { | 138 | trait Trait<T> { |
139 | Type X; | 139 | Type X; |
140 | fn foo(&self); | 140 | fn foo(&self) -> T; |
141 | fn bar(&self) {} | 141 | fn bar(&self) {} |
142 | } | 142 | } |
143 | 143 | ||
144 | impl T for () {<|> | 144 | impl Trait<u32> for () {<|> |
145 | 145 | ||
146 | } | 146 | } |
147 | "#####, | 147 | "#####, |
148 | r#####" | 148 | r#####" |
149 | trait T { | 149 | trait Trait<T> { |
150 | Type X; | 150 | Type X; |
151 | fn foo(&self); | 151 | fn foo(&self) -> T; |
152 | fn bar(&self) {} | 152 | fn bar(&self) {} |
153 | } | 153 | } |
154 | 154 | ||
155 | impl T for () { | 155 | impl Trait<u32> for () { |
156 | fn foo(&self) { unimplemented!() } | 156 | fn foo(&self) -> u32 { unimplemented!() } |
157 | 157 | ||
158 | } | 158 | } |
159 | "#####, | 159 | "#####, |