]> git.lizzy.rs Git - rust.git/blob - crates/ra_assists/src/handlers/add_missing_impl_members.rs
Merge assits::test_helpers and tests
[rust.git] / crates / ra_assists / src / handlers / add_missing_impl_members.rs
1 use hir::HasSource;
2 use ra_syntax::{
3     ast::{
4         self,
5         edit::{self, IndentLevel},
6         make, AstNode, NameOwner,
7     },
8     SmolStr,
9 };
10
11 use crate::{
12     ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
13     utils::{get_missing_assoc_items, resolve_target_trait},
14     Assist, AssistCtx, AssistId,
15 };
16
17 #[derive(PartialEq)]
18 enum AddMissingImplMembersMode {
19     DefaultMethodsOnly,
20     NoDefaultMethods,
21 }
22
23 // Assist: add_impl_missing_members
24 //
25 // Adds scaffold for required impl members.
26 //
27 // ```
28 // trait Trait<T> {
29 //     Type X;
30 //     fn foo(&self) -> T;
31 //     fn bar(&self) {}
32 // }
33 //
34 // impl Trait<u32> for () {<|>
35 //
36 // }
37 // ```
38 // ->
39 // ```
40 // trait Trait<T> {
41 //     Type X;
42 //     fn foo(&self) -> T;
43 //     fn bar(&self) {}
44 // }
45 //
46 // impl Trait<u32> for () {
47 //     fn foo(&self) -> u32 {
48 //         todo!()
49 //     }
50 //
51 // }
52 // ```
53 pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> {
54     add_missing_impl_members_inner(
55         ctx,
56         AddMissingImplMembersMode::NoDefaultMethods,
57         "add_impl_missing_members",
58         "Implement missing members",
59     )
60 }
61
62 // Assist: add_impl_default_members
63 //
64 // Adds scaffold for overriding default impl members.
65 //
66 // ```
67 // trait Trait {
68 //     Type X;
69 //     fn foo(&self);
70 //     fn bar(&self) {}
71 // }
72 //
73 // impl Trait for () {
74 //     Type X = ();
75 //     fn foo(&self) {}<|>
76 //
77 // }
78 // ```
79 // ->
80 // ```
81 // trait Trait {
82 //     Type X;
83 //     fn foo(&self);
84 //     fn bar(&self) {}
85 // }
86 //
87 // impl Trait for () {
88 //     Type X = ();
89 //     fn foo(&self) {}
90 //     fn bar(&self) {}
91 //
92 // }
93 // ```
94 pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> {
95     add_missing_impl_members_inner(
96         ctx,
97         AddMissingImplMembersMode::DefaultMethodsOnly,
98         "add_impl_default_members",
99         "Implement default members",
100     )
101 }
102
103 fn add_missing_impl_members_inner(
104     ctx: AssistCtx,
105     mode: AddMissingImplMembersMode,
106     assist_id: &'static str,
107     label: &'static str,
108 ) -> Option<Assist> {
109     let _p = ra_prof::profile("add_missing_impl_members_inner");
110     let impl_node = ctx.find_node_at_offset::<ast::ImplDef>()?;
111     let impl_item_list = impl_node.item_list()?;
112
113     let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?;
114
115     let def_name = |item: &ast::AssocItem| -> Option<SmolStr> {
116         match item {
117             ast::AssocItem::FnDef(def) => def.name(),
118             ast::AssocItem::TypeAliasDef(def) => def.name(),
119             ast::AssocItem::ConstDef(def) => def.name(),
120         }
121         .map(|it| it.text().clone())
122     };
123
124     let missing_items = get_missing_assoc_items(&ctx.sema, &impl_node)
125         .iter()
126         .map(|i| match i {
127             hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value),
128             hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value),
129             hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value),
130         })
131         .filter(|t| def_name(&t).is_some())
132         .filter(|t| match t {
133             ast::AssocItem::FnDef(def) => match mode {
134                 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
135                 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
136             },
137             _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
138         })
139         .collect::<Vec<_>>();
140
141     if missing_items.is_empty() {
142         return None;
143     }
144
145     let sema = ctx.sema;
146
147     ctx.add_assist(AssistId(assist_id), label, |edit| {
148         let n_existing_items = impl_item_list.assoc_items().count();
149         let source_scope = sema.scope_for_def(trait_);
150         let target_scope = sema.scope(impl_item_list.syntax());
151         let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
152             .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_node));
153         let items = missing_items
154             .into_iter()
155             .map(|it| ast_transform::apply(&*ast_transform, it))
156             .map(|it| match it {
157                 ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)),
158                 _ => it,
159             })
160             .map(|it| edit::remove_attrs_and_docs(&it));
161         let new_impl_item_list = impl_item_list.append_items(items);
162         let cursor_position = {
163             let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
164             first_new_item.syntax().text_range().start()
165         };
166
167         edit.replace_ast(impl_item_list, new_impl_item_list);
168         edit.set_cursor(cursor_position);
169     })
170 }
171
172 fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
173     if fn_def.body().is_none() {
174         let body = make::block_expr(None, Some(make::expr_todo()));
175         let body = IndentLevel(1).increase_indent(body);
176         fn_def.with_body(body)
177     } else {
178         fn_def
179     }
180 }
181
182 #[cfg(test)]
183 mod tests {
184     use crate::tests::{check_assist, check_assist_not_applicable};
185
186     use super::*;
187
188     #[test]
189     fn test_add_missing_impl_members() {
190         check_assist(
191             add_missing_impl_members,
192             r#"
193 trait Foo {
194     type Output;
195
196     const CONST: usize = 42;
197
198     fn foo(&self);
199     fn bar(&self);
200     fn baz(&self);
201 }
202
203 struct S;
204
205 impl Foo for S {
206     fn bar(&self) {}
207 <|>
208 }"#,
209             r#"
210 trait Foo {
211     type Output;
212
213     const CONST: usize = 42;
214
215     fn foo(&self);
216     fn bar(&self);
217     fn baz(&self);
218 }
219
220 struct S;
221
222 impl Foo for S {
223     fn bar(&self) {}
224     <|>type Output;
225     const CONST: usize = 42;
226     fn foo(&self) {
227         todo!()
228     }
229     fn baz(&self) {
230         todo!()
231     }
232
233 }"#,
234         );
235     }
236
237     #[test]
238     fn test_copied_overriden_members() {
239         check_assist(
240             add_missing_impl_members,
241             r#"
242 trait Foo {
243     fn foo(&self);
244     fn bar(&self) -> bool { true }
245     fn baz(&self) -> u32 { 42 }
246 }
247
248 struct S;
249
250 impl Foo for S {
251     fn bar(&self) {}
252 <|>
253 }"#,
254             r#"
255 trait Foo {
256     fn foo(&self);
257     fn bar(&self) -> bool { true }
258     fn baz(&self) -> u32 { 42 }
259 }
260
261 struct S;
262
263 impl Foo for S {
264     fn bar(&self) {}
265     <|>fn foo(&self) {
266         todo!()
267     }
268
269 }"#,
270         );
271     }
272
273     #[test]
274     fn test_empty_impl_def() {
275         check_assist(
276             add_missing_impl_members,
277             r#"
278 trait Foo { fn foo(&self); }
279 struct S;
280 impl Foo for S { <|> }"#,
281             r#"
282 trait Foo { fn foo(&self); }
283 struct S;
284 impl Foo for S {
285     <|>fn foo(&self) {
286         todo!()
287     }
288 }"#,
289         );
290     }
291
292     #[test]
293     fn fill_in_type_params_1() {
294         check_assist(
295             add_missing_impl_members,
296             r#"
297 trait Foo<T> { fn foo(&self, t: T) -> &T; }
298 struct S;
299 impl Foo<u32> for S { <|> }"#,
300             r#"
301 trait Foo<T> { fn foo(&self, t: T) -> &T; }
302 struct S;
303 impl Foo<u32> for S {
304     <|>fn foo(&self, t: u32) -> &u32 {
305         todo!()
306     }
307 }"#,
308         );
309     }
310
311     #[test]
312     fn fill_in_type_params_2() {
313         check_assist(
314             add_missing_impl_members,
315             r#"
316 trait Foo<T> { fn foo(&self, t: T) -> &T; }
317 struct S;
318 impl<U> Foo<U> for S { <|> }"#,
319             r#"
320 trait Foo<T> { fn foo(&self, t: T) -> &T; }
321 struct S;
322 impl<U> Foo<U> for S {
323     <|>fn foo(&self, t: U) -> &U {
324         todo!()
325     }
326 }"#,
327         );
328     }
329
330     #[test]
331     fn test_cursor_after_empty_impl_def() {
332         check_assist(
333             add_missing_impl_members,
334             r#"
335 trait Foo { fn foo(&self); }
336 struct S;
337 impl Foo for S {}<|>"#,
338             r#"
339 trait Foo { fn foo(&self); }
340 struct S;
341 impl Foo for S {
342     <|>fn foo(&self) {
343         todo!()
344     }
345 }"#,
346         )
347     }
348
349     #[test]
350     fn test_qualify_path_1() {
351         check_assist(
352             add_missing_impl_members,
353             r#"
354 mod foo {
355     pub struct Bar;
356     trait Foo { fn foo(&self, bar: Bar); }
357 }
358 struct S;
359 impl foo::Foo for S { <|> }"#,
360             r#"
361 mod foo {
362     pub struct Bar;
363     trait Foo { fn foo(&self, bar: Bar); }
364 }
365 struct S;
366 impl foo::Foo for S {
367     <|>fn foo(&self, bar: foo::Bar) {
368         todo!()
369     }
370 }"#,
371         );
372     }
373
374     #[test]
375     fn test_qualify_path_generic() {
376         check_assist(
377             add_missing_impl_members,
378             r#"
379 mod foo {
380     pub struct Bar<T>;
381     trait Foo { fn foo(&self, bar: Bar<u32>); }
382 }
383 struct S;
384 impl foo::Foo for S { <|> }"#,
385             r#"
386 mod foo {
387     pub struct Bar<T>;
388     trait Foo { fn foo(&self, bar: Bar<u32>); }
389 }
390 struct S;
391 impl foo::Foo for S {
392     <|>fn foo(&self, bar: foo::Bar<u32>) {
393         todo!()
394     }
395 }"#,
396         );
397     }
398
399     #[test]
400     fn test_qualify_path_and_substitute_param() {
401         check_assist(
402             add_missing_impl_members,
403             r#"
404 mod foo {
405     pub struct Bar<T>;
406     trait Foo<T> { fn foo(&self, bar: Bar<T>); }
407 }
408 struct S;
409 impl foo::Foo<u32> for S { <|> }"#,
410             r#"
411 mod foo {
412     pub struct Bar<T>;
413     trait Foo<T> { fn foo(&self, bar: Bar<T>); }
414 }
415 struct S;
416 impl foo::Foo<u32> for S {
417     <|>fn foo(&self, bar: foo::Bar<u32>) {
418         todo!()
419     }
420 }"#,
421         );
422     }
423
424     #[test]
425     fn test_substitute_param_no_qualify() {
426         // when substituting params, the substituted param should not be qualified!
427         check_assist(
428             add_missing_impl_members,
429             r#"
430 mod foo {
431     trait Foo<T> { fn foo(&self, bar: T); }
432     pub struct Param;
433 }
434 struct Param;
435 struct S;
436 impl foo::Foo<Param> for S { <|> }"#,
437             r#"
438 mod foo {
439     trait Foo<T> { fn foo(&self, bar: T); }
440     pub struct Param;
441 }
442 struct Param;
443 struct S;
444 impl foo::Foo<Param> for S {
445     <|>fn foo(&self, bar: Param) {
446         todo!()
447     }
448 }"#,
449         );
450     }
451
452     #[test]
453     fn test_qualify_path_associated_item() {
454         check_assist(
455             add_missing_impl_members,
456             r#"
457 mod foo {
458     pub struct Bar<T>;
459     impl Bar<T> { type Assoc = u32; }
460     trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
461 }
462 struct S;
463 impl foo::Foo for S { <|> }"#,
464             r#"
465 mod foo {
466     pub struct Bar<T>;
467     impl Bar<T> { type Assoc = u32; }
468     trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
469 }
470 struct S;
471 impl foo::Foo for S {
472     <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) {
473         todo!()
474     }
475 }"#,
476         );
477     }
478
479     #[test]
480     fn test_qualify_path_nested() {
481         check_assist(
482             add_missing_impl_members,
483             r#"
484 mod foo {
485     pub struct Bar<T>;
486     pub struct Baz;
487     trait Foo { fn foo(&self, bar: Bar<Baz>); }
488 }
489 struct S;
490 impl foo::Foo for S { <|> }"#,
491             r#"
492 mod foo {
493     pub struct Bar<T>;
494     pub struct Baz;
495     trait Foo { fn foo(&self, bar: Bar<Baz>); }
496 }
497 struct S;
498 impl foo::Foo for S {
499     <|>fn foo(&self, bar: foo::Bar<foo::Baz>) {
500         todo!()
501     }
502 }"#,
503         );
504     }
505
506     #[test]
507     fn test_qualify_path_fn_trait_notation() {
508         check_assist(
509             add_missing_impl_members,
510             r#"
511 mod foo {
512     pub trait Fn<Args> { type Output; }
513     trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
514 }
515 struct S;
516 impl foo::Foo for S { <|> }"#,
517             r#"
518 mod foo {
519     pub trait Fn<Args> { type Output; }
520     trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
521 }
522 struct S;
523 impl foo::Foo for S {
524     <|>fn foo(&self, bar: dyn Fn(u32) -> i32) {
525         todo!()
526     }
527 }"#,
528         );
529     }
530
531     #[test]
532     fn test_empty_trait() {
533         check_assist_not_applicable(
534             add_missing_impl_members,
535             r#"
536 trait Foo;
537 struct S;
538 impl Foo for S { <|> }"#,
539         )
540     }
541
542     #[test]
543     fn test_ignore_unnamed_trait_members_and_default_methods() {
544         check_assist_not_applicable(
545             add_missing_impl_members,
546             r#"
547 trait Foo {
548     fn (arg: u32);
549     fn valid(some: u32) -> bool { false }
550 }
551 struct S;
552 impl Foo for S { <|> }"#,
553         )
554     }
555
556     #[test]
557     fn test_with_docstring_and_attrs() {
558         check_assist(
559             add_missing_impl_members,
560             r#"
561 #[doc(alias = "test alias")]
562 trait Foo {
563     /// doc string
564     type Output;
565
566     #[must_use]
567     fn foo(&self);
568 }
569 struct S;
570 impl Foo for S {}<|>"#,
571             r#"
572 #[doc(alias = "test alias")]
573 trait Foo {
574     /// doc string
575     type Output;
576
577     #[must_use]
578     fn foo(&self);
579 }
580 struct S;
581 impl Foo for S {
582     <|>type Output;
583     fn foo(&self) {
584         todo!()
585     }
586 }"#,
587         )
588     }
589
590     #[test]
591     fn test_default_methods() {
592         check_assist(
593             add_missing_default_members,
594             r#"
595 trait Foo {
596     type Output;
597
598     const CONST: usize = 42;
599
600     fn valid(some: u32) -> bool { false }
601     fn foo(some: u32) -> bool;
602 }
603 struct S;
604 impl Foo for S { <|> }"#,
605             r#"
606 trait Foo {
607     type Output;
608
609     const CONST: usize = 42;
610
611     fn valid(some: u32) -> bool { false }
612     fn foo(some: u32) -> bool;
613 }
614 struct S;
615 impl Foo for S {
616     <|>fn valid(some: u32) -> bool { false }
617 }"#,
618         )
619     }
620 }