aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-12-24 15:44:32 +0000
committerFlorian Diebold <[email protected]>2019-12-24 16:29:27 +0000
commitc39352fa43f4ff4df835d511b4415c8e770438c4 (patch)
treecb7daa51f218a87aa94874aef28130c1ca2b0245 /crates/ra_assists/src
parentaa49b79bda5b7cafbaa33c302a9974133d34c52b (diff)
Fill in type params in 'add missing impl members' assist
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r--crates/ra_assists/src/assist_ctx.rs2
-rw-r--r--crates/ra_assists/src/assists/add_missing_impl_members.rs131
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs22
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)]
51pub(crate) struct AssistCtx<'a, DB> { 51pub(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 @@
1use std::collections::HashMap;
2
1use hir::{db::HirDatabase, HasSource}; 3use hir::{db::HirDatabase, HasSource};
2use ra_syntax::{ 4use 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)
183fn 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)
199fn 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`.
165fn resolve_target_trait_def( 232fn 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 "
357trait Foo<T> { fn foo(&self, t: T) -> &T; }
358struct S;
359impl Foo<u32> for S { <|> }",
360 "
361trait Foo<T> { fn foo(&self, t: T) -> &T; }
362struct S;
363impl 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 "
374trait Foo<T> { fn foo(&self, t: T) -> &T; }
375struct S;
376impl<U> Foo<U> for S { <|> }",
377 "
378trait Foo<T> { fn foo(&self, t: T) -> &T; }
379struct S;
380impl<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#####"
104trait T { 104trait 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
110impl T for () { 110impl 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#####"
117trait T { 117trait 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
123impl T for () { 123impl 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#####"
138trait T { 138trait 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
144impl T for () {<|> 144impl Trait<u32> for () {<|>
145 145
146} 146}
147"#####, 147"#####,
148 r#####" 148 r#####"
149trait T { 149trait 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
155impl T for () { 155impl Trait<u32> for () {
156 fn foo(&self) { unimplemented!() } 156 fn foo(&self) -> u32 { unimplemented!() }
157 157
158} 158}
159"#####, 159"#####,