diff options
Diffstat (limited to 'crates/ra_assists/src/add_missing_impl_members.rs')
-rw-r--r-- | crates/ra_assists/src/add_missing_impl_members.rs | 96 |
1 files changed, 68 insertions, 28 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index 0e49b0401..0c903a563 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs | |||
@@ -2,9 +2,10 @@ use crate::{Assist, AssistId, AssistCtx, ast_editor::{AstEditor, AstBuilder}}; | |||
2 | 2 | ||
3 | use hir::{HasSource, db::HirDatabase}; | 3 | use hir::{HasSource, db::HirDatabase}; |
4 | use ra_syntax::{SmolStr, TreeArc}; | 4 | use ra_syntax::{SmolStr, TreeArc}; |
5 | use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; | 5 | use ra_syntax::ast::{self, AstNode, ImplItem, ImplItemKind, NameOwner}; |
6 | use ra_db::FilePosition; | 6 | use ra_db::FilePosition; |
7 | 7 | ||
8 | #[derive(PartialEq)] | ||
8 | enum AddMissingImplMembersMode { | 9 | enum AddMissingImplMembersMode { |
9 | DefaultMethodsOnly, | 10 | DefaultMethodsOnly, |
10 | NoDefaultMethods, | 11 | NoDefaultMethods, |
@@ -45,39 +46,50 @@ fn add_missing_impl_members_inner( | |||
45 | resolve_target_trait_def(ctx.db, &analyzer, impl_node)? | 46 | resolve_target_trait_def(ctx.db, &analyzer, impl_node)? |
46 | }; | 47 | }; |
47 | 48 | ||
48 | let missing_fns: Vec<_> = { | 49 | let def_name = |kind| -> Option<&SmolStr> { |
49 | let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None }; | 50 | match kind { |
50 | let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) }; | 51 | ImplItemKind::FnDef(def) => def.name(), |
51 | 52 | ImplItemKind::TypeAliasDef(def) => def.name(), | |
52 | let trait_items = | 53 | ImplItemKind::ConstDef(def) => def.name(), |
53 | trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items(); | 54 | } |
54 | let impl_items = impl_item_list.impl_items(); | 55 | .map(ast::Name::text) |
55 | |||
56 | let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt); | ||
57 | let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>(); | ||
58 | |||
59 | trait_fns | ||
60 | .filter(|t| def_name(t).is_some()) | ||
61 | .filter(|t| match mode { | ||
62 | AddMissingImplMembersMode::DefaultMethodsOnly => t.body().is_some(), | ||
63 | AddMissingImplMembersMode::NoDefaultMethods => t.body().is_none(), | ||
64 | }) | ||
65 | .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t))) | ||
66 | .collect() | ||
67 | }; | 56 | }; |
68 | if missing_fns.is_empty() { | 57 | |
58 | let trait_items = trait_def.item_list()?.impl_items(); | ||
59 | let impl_items = impl_item_list.impl_items().collect::<Vec<_>>(); | ||
60 | |||
61 | let missing_items: Vec<_> = trait_items | ||
62 | .filter(|t| def_name(t.kind()).is_some()) | ||
63 | .filter(|t| match t.kind() { | ||
64 | ImplItemKind::FnDef(def) => match mode { | ||
65 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), | ||
66 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), | ||
67 | }, | ||
68 | _ => mode == AddMissingImplMembersMode::NoDefaultMethods, | ||
69 | }) | ||
70 | .filter(|t| impl_items.iter().all(|i| def_name(i.kind()) != def_name(t.kind()))) | ||
71 | .collect(); | ||
72 | if missing_items.is_empty() { | ||
69 | return None; | 73 | return None; |
70 | } | 74 | } |
71 | 75 | ||
72 | ctx.add_action(AssistId(assist_id), label, |edit| { | 76 | ctx.add_action(AssistId(assist_id), label, |edit| { |
73 | let n_existing_items = impl_item_list.impl_items().count(); | 77 | let n_existing_items = impl_item_list.impl_items().count(); |
74 | let fns = missing_fns.into_iter().map(add_body_and_strip_docstring).collect::<Vec<_>>(); | ||
75 | |||
76 | let mut ast_editor = AstEditor::new(impl_item_list); | 78 | let mut ast_editor = AstEditor::new(impl_item_list); |
77 | if n_existing_items == 0 { | 79 | if n_existing_items == 0 { |
78 | ast_editor.make_multiline(); | 80 | ast_editor.make_multiline(); |
79 | } | 81 | } |
80 | ast_editor.append_functions(fns.iter().map(|it| &**it)); | 82 | |
83 | for item in missing_items { | ||
84 | let it = match item.kind() { | ||
85 | ImplItemKind::FnDef(def) => { | ||
86 | strip_docstring(ImplItem::cast(add_body(def).syntax()).unwrap()) | ||
87 | } | ||
88 | _ => strip_docstring(item), | ||
89 | }; | ||
90 | ast_editor.append_item(&it) | ||
91 | } | ||
92 | |||
81 | let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); | 93 | let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); |
82 | let cursor_poisition = first_new_item.syntax().range().start(); | 94 | let cursor_poisition = first_new_item.syntax().range().start(); |
83 | ast_editor.into_text_edit(edit.text_edit_builder()); | 95 | ast_editor.into_text_edit(edit.text_edit_builder()); |
@@ -88,14 +100,19 @@ fn add_missing_impl_members_inner( | |||
88 | ctx.build() | 100 | ctx.build() |
89 | } | 101 | } |
90 | 102 | ||
91 | fn add_body_and_strip_docstring(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> { | 103 | fn strip_docstring(item: &ast::ImplItem) -> TreeArc<ast::ImplItem> { |
104 | let mut ast_editor = AstEditor::new(item); | ||
105 | ast_editor.strip_attrs_and_docs(); | ||
106 | ast_editor.ast().to_owned() | ||
107 | } | ||
108 | |||
109 | fn add_body(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> { | ||
92 | let mut ast_editor = AstEditor::new(fn_def); | 110 | let mut ast_editor = AstEditor::new(fn_def); |
93 | if fn_def.body().is_none() { | 111 | if fn_def.body().is_none() { |
94 | ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr( | 112 | ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr( |
95 | &AstBuilder::<ast::Expr>::unimplemented(), | 113 | &AstBuilder::<ast::Expr>::unimplemented(), |
96 | )); | 114 | )); |
97 | } | 115 | } |
98 | ast_editor.strip_attrs_and_docs(); | ||
99 | ast_editor.ast().to_owned() | 116 | ast_editor.ast().to_owned() |
100 | } | 117 | } |
101 | 118 | ||
@@ -126,6 +143,10 @@ mod tests { | |||
126 | add_missing_impl_members, | 143 | add_missing_impl_members, |
127 | " | 144 | " |
128 | trait Foo { | 145 | trait Foo { |
146 | type Output; | ||
147 | |||
148 | const CONST: usize = 42; | ||
149 | |||
129 | fn foo(&self); | 150 | fn foo(&self); |
130 | fn bar(&self); | 151 | fn bar(&self); |
131 | fn baz(&self); | 152 | fn baz(&self); |
@@ -139,6 +160,10 @@ impl Foo for S { | |||
139 | }", | 160 | }", |
140 | " | 161 | " |
141 | trait Foo { | 162 | trait Foo { |
163 | type Output; | ||
164 | |||
165 | const CONST: usize = 42; | ||
166 | |||
142 | fn foo(&self); | 167 | fn foo(&self); |
143 | fn bar(&self); | 168 | fn bar(&self); |
144 | fn baz(&self); | 169 | fn baz(&self); |
@@ -148,7 +173,9 @@ struct S; | |||
148 | 173 | ||
149 | impl Foo for S { | 174 | impl Foo for S { |
150 | fn bar(&self) {} | 175 | fn bar(&self) {} |
151 | <|>fn foo(&self) { unimplemented!() } | 176 | <|>type Output; |
177 | const CONST: usize = 42; | ||
178 | fn foo(&self) { unimplemented!() } | ||
152 | fn baz(&self) { unimplemented!() } | 179 | fn baz(&self) { unimplemented!() } |
153 | 180 | ||
154 | }", | 181 | }", |
@@ -256,6 +283,8 @@ impl Foo for S { <|> }", | |||
256 | #[doc(alias = "test alias")] | 283 | #[doc(alias = "test alias")] |
257 | trait Foo { | 284 | trait Foo { |
258 | /// doc string | 285 | /// doc string |
286 | type Output; | ||
287 | |||
259 | #[must_use] | 288 | #[must_use] |
260 | fn foo(&self); | 289 | fn foo(&self); |
261 | } | 290 | } |
@@ -265,12 +294,15 @@ impl Foo for S {}<|>"#, | |||
265 | #[doc(alias = "test alias")] | 294 | #[doc(alias = "test alias")] |
266 | trait Foo { | 295 | trait Foo { |
267 | /// doc string | 296 | /// doc string |
297 | type Output; | ||
298 | |||
268 | #[must_use] | 299 | #[must_use] |
269 | fn foo(&self); | 300 | fn foo(&self); |
270 | } | 301 | } |
271 | struct S; | 302 | struct S; |
272 | impl Foo for S { | 303 | impl Foo for S { |
273 | <|>fn foo(&self) { unimplemented!() } | 304 | <|>type Output; |
305 | fn foo(&self) { unimplemented!() } | ||
274 | }"#, | 306 | }"#, |
275 | ) | 307 | ) |
276 | } | 308 | } |
@@ -281,6 +313,10 @@ impl Foo for S { | |||
281 | add_missing_default_members, | 313 | add_missing_default_members, |
282 | " | 314 | " |
283 | trait Foo { | 315 | trait Foo { |
316 | type Output; | ||
317 | |||
318 | const CONST: usize = 42; | ||
319 | |||
284 | fn valid(some: u32) -> bool { false } | 320 | fn valid(some: u32) -> bool { false } |
285 | fn foo(some: u32) -> bool; | 321 | fn foo(some: u32) -> bool; |
286 | } | 322 | } |
@@ -288,6 +324,10 @@ struct S; | |||
288 | impl Foo for S { <|> }", | 324 | impl Foo for S { <|> }", |
289 | " | 325 | " |
290 | trait Foo { | 326 | trait Foo { |
327 | type Output; | ||
328 | |||
329 | const CONST: usize = 42; | ||
330 | |||
291 | fn valid(some: u32) -> bool { false } | 331 | fn valid(some: u32) -> bool { false } |
292 | fn foo(some: u32) -> bool; | 332 | fn foo(some: u32) -> bool; |
293 | } | 333 | } |