diff options
author | Aleksey Kladov <[email protected]> | 2019-09-25 12:29:41 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-09-25 12:29:41 +0100 |
commit | f32081fa185b3a9df021f277c2c27fbd123d0951 (patch) | |
tree | 0032bc2f09dc0f323ae5b77bbc2b7d891173f398 /crates/ra_assists/src/assists/add_missing_impl_members.rs | |
parent | d9b4835625ac96c7628e2ef66ef6e26cc48d231f (diff) |
move assists to subdir
Diffstat (limited to 'crates/ra_assists/src/assists/add_missing_impl_members.rs')
-rw-r--r-- | crates/ra_assists/src/assists/add_missing_impl_members.rs | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs new file mode 100644 index 000000000..cbeb7054f --- /dev/null +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs | |||
@@ -0,0 +1,340 @@ | |||
1 | use hir::{db::HirDatabase, HasSource}; | ||
2 | use ra_syntax::{ | ||
3 | ast::{self, AstNode, NameOwner}, | ||
4 | SmolStr, | ||
5 | }; | ||
6 | |||
7 | use crate::{ | ||
8 | ast_editor::{AstBuilder, AstEditor}, | ||
9 | Assist, AssistCtx, AssistId, | ||
10 | }; | ||
11 | |||
12 | #[derive(PartialEq)] | ||
13 | enum AddMissingImplMembersMode { | ||
14 | DefaultMethodsOnly, | ||
15 | NoDefaultMethods, | ||
16 | } | ||
17 | |||
18 | pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
19 | add_missing_impl_members_inner( | ||
20 | ctx, | ||
21 | AddMissingImplMembersMode::NoDefaultMethods, | ||
22 | "add_impl_missing_members", | ||
23 | "add missing impl members", | ||
24 | ) | ||
25 | } | ||
26 | |||
27 | pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
28 | add_missing_impl_members_inner( | ||
29 | ctx, | ||
30 | AddMissingImplMembersMode::DefaultMethodsOnly, | ||
31 | "add_impl_default_members", | ||
32 | "add impl default members", | ||
33 | ) | ||
34 | } | ||
35 | |||
36 | fn add_missing_impl_members_inner( | ||
37 | mut ctx: AssistCtx<impl HirDatabase>, | ||
38 | mode: AddMissingImplMembersMode, | ||
39 | assist_id: &'static str, | ||
40 | label: &'static str, | ||
41 | ) -> Option<Assist> { | ||
42 | let impl_node = ctx.node_at_offset::<ast::ImplBlock>()?; | ||
43 | let impl_item_list = impl_node.item_list()?; | ||
44 | |||
45 | let trait_def = { | ||
46 | let file_id = ctx.frange.file_id; | ||
47 | let analyzer = hir::SourceAnalyzer::new(ctx.db, file_id, impl_node.syntax(), None); | ||
48 | |||
49 | resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? | ||
50 | }; | ||
51 | |||
52 | let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { | ||
53 | match item { | ||
54 | ast::ImplItem::FnDef(def) => def.name(), | ||
55 | ast::ImplItem::TypeAliasDef(def) => def.name(), | ||
56 | ast::ImplItem::ConstDef(def) => def.name(), | ||
57 | } | ||
58 | .map(|it| it.text().clone()) | ||
59 | }; | ||
60 | |||
61 | let trait_items = trait_def.item_list()?.impl_items(); | ||
62 | let impl_items = impl_item_list.impl_items().collect::<Vec<_>>(); | ||
63 | |||
64 | let missing_items: Vec<_> = trait_items | ||
65 | .filter(|t| def_name(t).is_some()) | ||
66 | .filter(|t| match t { | ||
67 | ast::ImplItem::FnDef(def) => match mode { | ||
68 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), | ||
69 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), | ||
70 | }, | ||
71 | _ => mode == AddMissingImplMembersMode::NoDefaultMethods, | ||
72 | }) | ||
73 | .filter(|t| impl_items.iter().all(|i| def_name(i) != def_name(t))) | ||
74 | .collect(); | ||
75 | if missing_items.is_empty() { | ||
76 | return None; | ||
77 | } | ||
78 | |||
79 | ctx.add_action(AssistId(assist_id), label, |edit| { | ||
80 | let n_existing_items = impl_item_list.impl_items().count(); | ||
81 | let items = missing_items.into_iter().map(|it| match it { | ||
82 | ast::ImplItem::FnDef(def) => strip_docstring(add_body(def).into()), | ||
83 | _ => strip_docstring(it), | ||
84 | }); | ||
85 | let mut ast_editor = AstEditor::new(impl_item_list); | ||
86 | |||
87 | ast_editor.append_items(items); | ||
88 | |||
89 | let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); | ||
90 | let cursor_position = first_new_item.syntax().text_range().start(); | ||
91 | ast_editor.into_text_edit(edit.text_edit_builder()); | ||
92 | |||
93 | edit.set_cursor(cursor_position); | ||
94 | }); | ||
95 | |||
96 | ctx.build() | ||
97 | } | ||
98 | |||
99 | fn strip_docstring(item: ast::ImplItem) -> ast::ImplItem { | ||
100 | let mut ast_editor = AstEditor::new(item); | ||
101 | ast_editor.strip_attrs_and_docs(); | ||
102 | ast_editor.ast().to_owned() | ||
103 | } | ||
104 | |||
105 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | ||
106 | let mut ast_editor = AstEditor::new(fn_def.clone()); | ||
107 | if fn_def.body().is_none() { | ||
108 | ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr( | ||
109 | &AstBuilder::<ast::Expr>::unimplemented(), | ||
110 | )); | ||
111 | } | ||
112 | ast_editor.ast().to_owned() | ||
113 | } | ||
114 | |||
115 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being | ||
116 | /// implemented) to a `ast::TraitDef`. | ||
117 | fn resolve_target_trait_def( | ||
118 | db: &impl HirDatabase, | ||
119 | analyzer: &hir::SourceAnalyzer, | ||
120 | impl_block: &ast::ImplBlock, | ||
121 | ) -> Option<ast::TraitDef> { | ||
122 | let ast_path = impl_block | ||
123 | .target_trait() | ||
124 | .map(|it| it.syntax().clone()) | ||
125 | .and_then(ast::PathType::cast)? | ||
126 | .path()?; | ||
127 | |||
128 | match analyzer.resolve_path(db, &ast_path) { | ||
129 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).ast), | ||
130 | _ => None, | ||
131 | } | ||
132 | } | ||
133 | |||
134 | #[cfg(test)] | ||
135 | mod tests { | ||
136 | use super::*; | ||
137 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
138 | |||
139 | #[test] | ||
140 | fn test_add_missing_impl_members() { | ||
141 | check_assist( | ||
142 | add_missing_impl_members, | ||
143 | " | ||
144 | trait Foo { | ||
145 | type Output; | ||
146 | |||
147 | const CONST: usize = 42; | ||
148 | |||
149 | fn foo(&self); | ||
150 | fn bar(&self); | ||
151 | fn baz(&self); | ||
152 | } | ||
153 | |||
154 | struct S; | ||
155 | |||
156 | impl Foo for S { | ||
157 | fn bar(&self) {} | ||
158 | <|> | ||
159 | }", | ||
160 | " | ||
161 | trait Foo { | ||
162 | type Output; | ||
163 | |||
164 | const CONST: usize = 42; | ||
165 | |||
166 | fn foo(&self); | ||
167 | fn bar(&self); | ||
168 | fn baz(&self); | ||
169 | } | ||
170 | |||
171 | struct S; | ||
172 | |||
173 | impl Foo for S { | ||
174 | fn bar(&self) {} | ||
175 | <|>type Output; | ||
176 | const CONST: usize = 42; | ||
177 | fn foo(&self) { unimplemented!() } | ||
178 | fn baz(&self) { unimplemented!() } | ||
179 | |||
180 | }", | ||
181 | ); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn test_copied_overriden_members() { | ||
186 | check_assist( | ||
187 | add_missing_impl_members, | ||
188 | " | ||
189 | trait Foo { | ||
190 | fn foo(&self); | ||
191 | fn bar(&self) -> bool { true } | ||
192 | fn baz(&self) -> u32 { 42 } | ||
193 | } | ||
194 | |||
195 | struct S; | ||
196 | |||
197 | impl Foo for S { | ||
198 | fn bar(&self) {} | ||
199 | <|> | ||
200 | }", | ||
201 | " | ||
202 | trait Foo { | ||
203 | fn foo(&self); | ||
204 | fn bar(&self) -> bool { true } | ||
205 | fn baz(&self) -> u32 { 42 } | ||
206 | } | ||
207 | |||
208 | struct S; | ||
209 | |||
210 | impl Foo for S { | ||
211 | fn bar(&self) {} | ||
212 | <|>fn foo(&self) { unimplemented!() } | ||
213 | |||
214 | }", | ||
215 | ); | ||
216 | } | ||
217 | |||
218 | #[test] | ||
219 | fn test_empty_impl_block() { | ||
220 | check_assist( | ||
221 | add_missing_impl_members, | ||
222 | " | ||
223 | trait Foo { fn foo(&self); } | ||
224 | struct S; | ||
225 | impl Foo for S { <|> }", | ||
226 | " | ||
227 | trait Foo { fn foo(&self); } | ||
228 | struct S; | ||
229 | impl Foo for S { | ||
230 | <|>fn foo(&self) { unimplemented!() } | ||
231 | }", | ||
232 | ); | ||
233 | } | ||
234 | |||
235 | #[test] | ||
236 | fn test_cursor_after_empty_impl_block() { | ||
237 | check_assist( | ||
238 | add_missing_impl_members, | ||
239 | " | ||
240 | trait Foo { fn foo(&self); } | ||
241 | struct S; | ||
242 | impl Foo for S {}<|>", | ||
243 | " | ||
244 | trait Foo { fn foo(&self); } | ||
245 | struct S; | ||
246 | impl Foo for S { | ||
247 | <|>fn foo(&self) { unimplemented!() } | ||
248 | }", | ||
249 | ) | ||
250 | } | ||
251 | |||
252 | #[test] | ||
253 | fn test_empty_trait() { | ||
254 | check_assist_not_applicable( | ||
255 | add_missing_impl_members, | ||
256 | " | ||
257 | trait Foo; | ||
258 | struct S; | ||
259 | impl Foo for S { <|> }", | ||
260 | ) | ||
261 | } | ||
262 | |||
263 | #[test] | ||
264 | fn test_ignore_unnamed_trait_members_and_default_methods() { | ||
265 | check_assist_not_applicable( | ||
266 | add_missing_impl_members, | ||
267 | " | ||
268 | trait Foo { | ||
269 | fn (arg: u32); | ||
270 | fn valid(some: u32) -> bool { false } | ||
271 | } | ||
272 | struct S; | ||
273 | impl Foo for S { <|> }", | ||
274 | ) | ||
275 | } | ||
276 | |||
277 | #[test] | ||
278 | fn test_with_docstring_and_attrs() { | ||
279 | check_assist( | ||
280 | add_missing_impl_members, | ||
281 | r#" | ||
282 | #[doc(alias = "test alias")] | ||
283 | trait Foo { | ||
284 | /// doc string | ||
285 | type Output; | ||
286 | |||
287 | #[must_use] | ||
288 | fn foo(&self); | ||
289 | } | ||
290 | struct S; | ||
291 | impl Foo for S {}<|>"#, | ||
292 | r#" | ||
293 | #[doc(alias = "test alias")] | ||
294 | trait Foo { | ||
295 | /// doc string | ||
296 | type Output; | ||
297 | |||
298 | #[must_use] | ||
299 | fn foo(&self); | ||
300 | } | ||
301 | struct S; | ||
302 | impl Foo for S { | ||
303 | <|>type Output; | ||
304 | fn foo(&self) { unimplemented!() } | ||
305 | }"#, | ||
306 | ) | ||
307 | } | ||
308 | |||
309 | #[test] | ||
310 | fn test_default_methods() { | ||
311 | check_assist( | ||
312 | add_missing_default_members, | ||
313 | " | ||
314 | trait Foo { | ||
315 | type Output; | ||
316 | |||
317 | const CONST: usize = 42; | ||
318 | |||
319 | fn valid(some: u32) -> bool { false } | ||
320 | fn foo(some: u32) -> bool; | ||
321 | } | ||
322 | struct S; | ||
323 | impl Foo for S { <|> }", | ||
324 | " | ||
325 | trait Foo { | ||
326 | type Output; | ||
327 | |||
328 | const CONST: usize = 42; | ||
329 | |||
330 | fn valid(some: u32) -> bool { false } | ||
331 | fn foo(some: u32) -> bool; | ||
332 | } | ||
333 | struct S; | ||
334 | impl Foo for S { | ||
335 | <|>fn valid(some: u32) -> bool { false } | ||
336 | }", | ||
337 | ) | ||
338 | } | ||
339 | |||
340 | } | ||