aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers/add_missing_impl_members.rs
diff options
context:
space:
mode:
authorZac Pullar-Strecker <[email protected]>2020-08-24 10:19:53 +0100
committerZac Pullar-Strecker <[email protected]>2020-08-24 10:20:13 +0100
commit7bbca7a1b3f9293d2f5cc5745199bc5f8396f2f0 (patch)
treebdb47765991cb973b2cd5481a088fac636bd326c /crates/assists/src/handlers/add_missing_impl_members.rs
parentca464650eeaca6195891199a93f4f76cf3e7e697 (diff)
parente65d48d1fb3d4d91d9dc1148a7a836ff5c9a3c87 (diff)
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Diffstat (limited to 'crates/assists/src/handlers/add_missing_impl_members.rs')
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs766
1 files changed, 766 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
new file mode 100644
index 000000000..83a2ada9a
--- /dev/null
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -0,0 +1,766 @@
1use hir::HasSource;
2use syntax::{
3 ast::{
4 self,
5 edit::{self, AstNodeEdit, IndentLevel},
6 make, AstNode, NameOwner,
7 },
8 SmolStr,
9};
10
11use crate::{
12 assist_context::{AssistContext, Assists},
13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
15 AssistId, AssistKind,
16};
17
18#[derive(PartialEq)]
19enum AddMissingImplMembersMode {
20 DefaultMethodsOnly,
21 NoDefaultMethods,
22}
23
24// Assist: add_impl_missing_members
25//
26// Adds scaffold for required impl members.
27//
28// ```
29// trait Trait<T> {
30// Type X;
31// fn foo(&self) -> T;
32// fn bar(&self) {}
33// }
34//
35// impl Trait<u32> for () {<|>
36//
37// }
38// ```
39// ->
40// ```
41// trait Trait<T> {
42// Type X;
43// fn foo(&self) -> T;
44// fn bar(&self) {}
45// }
46//
47// impl Trait<u32> for () {
48// fn foo(&self) -> u32 {
49// ${0:todo!()}
50// }
51// }
52// ```
53pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
54 add_missing_impl_members_inner(
55 acc,
56 ctx,
57 AddMissingImplMembersMode::NoDefaultMethods,
58 "add_impl_missing_members",
59 "Implement missing members",
60 )
61}
62
63// Assist: add_impl_default_members
64//
65// Adds scaffold for overriding default impl members.
66//
67// ```
68// trait Trait {
69// Type X;
70// fn foo(&self);
71// fn bar(&self) {}
72// }
73//
74// impl Trait for () {
75// Type X = ();
76// fn foo(&self) {}<|>
77//
78// }
79// ```
80// ->
81// ```
82// trait Trait {
83// Type X;
84// fn foo(&self);
85// fn bar(&self) {}
86// }
87//
88// impl Trait for () {
89// Type X = ();
90// fn foo(&self) {}
91//
92// $0fn bar(&self) {}
93// }
94// ```
95pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
96 add_missing_impl_members_inner(
97 acc,
98 ctx,
99 AddMissingImplMembersMode::DefaultMethodsOnly,
100 "add_impl_default_members",
101 "Implement default members",
102 )
103}
104
105fn add_missing_impl_members_inner(
106 acc: &mut Assists,
107 ctx: &AssistContext,
108 mode: AddMissingImplMembersMode,
109 assist_id: &'static str,
110 label: &'static str,
111) -> Option<()> {
112 let _p = profile::span("add_missing_impl_members_inner");
113 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
114 let impl_item_list = impl_def.assoc_item_list()?;
115
116 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
117
118 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> {
119 match item {
120 ast::AssocItem::Fn(def) => def.name(),
121 ast::AssocItem::TypeAlias(def) => def.name(),
122 ast::AssocItem::Const(def) => def.name(),
123 ast::AssocItem::MacroCall(_) => None,
124 }
125 .map(|it| it.text().clone())
126 };
127
128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
129 .iter()
130 .map(|i| match i {
131 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(ctx.db()).value),
132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(ctx.db()).value),
133 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(ctx.db()).value),
134 })
135 .filter(|t| def_name(&t).is_some())
136 .filter(|t| match t {
137 ast::AssocItem::Fn(def) => match mode {
138 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
139 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
140 },
141 _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
142 })
143 .collect::<Vec<_>>();
144
145 if missing_items.is_empty() {
146 return None;
147 }
148
149 let target = impl_def.syntax().text_range();
150 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
151 let n_existing_items = impl_item_list.assoc_items().count();
152 let source_scope = ctx.sema.scope_for_def(trait_);
153 let target_scope = ctx.sema.scope(impl_item_list.syntax());
154 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
155 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def));
156 let items = missing_items
157 .into_iter()
158 .map(|it| ast_transform::apply(&*ast_transform, it))
159 .map(|it| match it {
160 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
161 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
162 _ => it,
163 })
164 .map(|it| edit::remove_attrs_and_docs(&it));
165 let new_impl_item_list = impl_item_list.append_items(items);
166 let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
167
168 let original_range = impl_item_list.syntax().text_range();
169 match ctx.config.snippet_cap {
170 None => builder.replace(original_range, new_impl_item_list.to_string()),
171 Some(cap) => {
172 let mut cursor = Cursor::Before(first_new_item.syntax());
173 let placeholder;
174 if let ast::AssocItem::Fn(func) = &first_new_item {
175 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) {
176 if m.syntax().text() == "todo!()" {
177 placeholder = m;
178 cursor = Cursor::Replace(placeholder.syntax());
179 }
180 }
181 }
182 builder.replace_snippet(
183 cap,
184 original_range,
185 render_snippet(cap, new_impl_item_list.syntax(), cursor),
186 )
187 }
188 };
189 })
190}
191
192fn add_body(fn_def: ast::Fn) -> ast::Fn {
193 if fn_def.body().is_some() {
194 return fn_def;
195 }
196 let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1));
197 fn_def.with_body(body)
198}
199
200#[cfg(test)]
201mod tests {
202 use crate::tests::{check_assist, check_assist_not_applicable};
203
204 use super::*;
205
206 #[test]
207 fn test_add_missing_impl_members() {
208 check_assist(
209 add_missing_impl_members,
210 r#"
211trait Foo {
212 type Output;
213
214 const CONST: usize = 42;
215
216 fn foo(&self);
217 fn bar(&self);
218 fn baz(&self);
219}
220
221struct S;
222
223impl Foo for S {
224 fn bar(&self) {}
225<|>
226}"#,
227 r#"
228trait Foo {
229 type Output;
230
231 const CONST: usize = 42;
232
233 fn foo(&self);
234 fn bar(&self);
235 fn baz(&self);
236}
237
238struct S;
239
240impl Foo for S {
241 fn bar(&self) {}
242
243 $0type Output;
244
245 const CONST: usize = 42;
246
247 fn foo(&self) {
248 todo!()
249 }
250
251 fn baz(&self) {
252 todo!()
253 }
254}"#,
255 );
256 }
257
258 #[test]
259 fn test_copied_overriden_members() {
260 check_assist(
261 add_missing_impl_members,
262 r#"
263trait Foo {
264 fn foo(&self);
265 fn bar(&self) -> bool { true }
266 fn baz(&self) -> u32 { 42 }
267}
268
269struct S;
270
271impl Foo for S {
272 fn bar(&self) {}
273<|>
274}"#,
275 r#"
276trait Foo {
277 fn foo(&self);
278 fn bar(&self) -> bool { true }
279 fn baz(&self) -> u32 { 42 }
280}
281
282struct S;
283
284impl Foo for S {
285 fn bar(&self) {}
286
287 fn foo(&self) {
288 ${0:todo!()}
289 }
290}"#,
291 );
292 }
293
294 #[test]
295 fn test_empty_impl_def() {
296 check_assist(
297 add_missing_impl_members,
298 r#"
299trait Foo { fn foo(&self); }
300struct S;
301impl Foo for S { <|> }"#,
302 r#"
303trait Foo { fn foo(&self); }
304struct S;
305impl Foo for S {
306 fn foo(&self) {
307 ${0:todo!()}
308 }
309}"#,
310 );
311 }
312
313 #[test]
314 fn fill_in_type_params_1() {
315 check_assist(
316 add_missing_impl_members,
317 r#"
318trait Foo<T> { fn foo(&self, t: T) -> &T; }
319struct S;
320impl Foo<u32> for S { <|> }"#,
321 r#"
322trait Foo<T> { fn foo(&self, t: T) -> &T; }
323struct S;
324impl Foo<u32> for S {
325 fn foo(&self, t: u32) -> &u32 {
326 ${0:todo!()}
327 }
328}"#,
329 );
330 }
331
332 #[test]
333 fn fill_in_type_params_2() {
334 check_assist(
335 add_missing_impl_members,
336 r#"
337trait Foo<T> { fn foo(&self, t: T) -> &T; }
338struct S;
339impl<U> Foo<U> for S { <|> }"#,
340 r#"
341trait Foo<T> { fn foo(&self, t: T) -> &T; }
342struct S;
343impl<U> Foo<U> for S {
344 fn foo(&self, t: U) -> &U {
345 ${0:todo!()}
346 }
347}"#,
348 );
349 }
350
351 #[test]
352 fn test_cursor_after_empty_impl_def() {
353 check_assist(
354 add_missing_impl_members,
355 r#"
356trait Foo { fn foo(&self); }
357struct S;
358impl Foo for S {}<|>"#,
359 r#"
360trait Foo { fn foo(&self); }
361struct S;
362impl Foo for S {
363 fn foo(&self) {
364 ${0:todo!()}
365 }
366}"#,
367 )
368 }
369
370 #[test]
371 fn test_qualify_path_1() {
372 check_assist(
373 add_missing_impl_members,
374 r#"
375mod foo {
376 pub struct Bar;
377 trait Foo { fn foo(&self, bar: Bar); }
378}
379struct S;
380impl foo::Foo for S { <|> }"#,
381 r#"
382mod foo {
383 pub struct Bar;
384 trait Foo { fn foo(&self, bar: Bar); }
385}
386struct S;
387impl foo::Foo for S {
388 fn foo(&self, bar: foo::Bar) {
389 ${0:todo!()}
390 }
391}"#,
392 );
393 }
394
395 #[test]
396 fn test_qualify_path_generic() {
397 check_assist(
398 add_missing_impl_members,
399 r#"
400mod foo {
401 pub struct Bar<T>;
402 trait Foo { fn foo(&self, bar: Bar<u32>); }
403}
404struct S;
405impl foo::Foo for S { <|> }"#,
406 r#"
407mod foo {
408 pub struct Bar<T>;
409 trait Foo { fn foo(&self, bar: Bar<u32>); }
410}
411struct S;
412impl foo::Foo for S {
413 fn foo(&self, bar: foo::Bar<u32>) {
414 ${0:todo!()}
415 }
416}"#,
417 );
418 }
419
420 #[test]
421 fn test_qualify_path_and_substitute_param() {
422 check_assist(
423 add_missing_impl_members,
424 r#"
425mod foo {
426 pub struct Bar<T>;
427 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
428}
429struct S;
430impl foo::Foo<u32> for S { <|> }"#,
431 r#"
432mod foo {
433 pub struct Bar<T>;
434 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
435}
436struct S;
437impl foo::Foo<u32> for S {
438 fn foo(&self, bar: foo::Bar<u32>) {
439 ${0:todo!()}
440 }
441}"#,
442 );
443 }
444
445 #[test]
446 fn test_substitute_param_no_qualify() {
447 // when substituting params, the substituted param should not be qualified!
448 check_assist(
449 add_missing_impl_members,
450 r#"
451mod foo {
452 trait Foo<T> { fn foo(&self, bar: T); }
453 pub struct Param;
454}
455struct Param;
456struct S;
457impl foo::Foo<Param> for S { <|> }"#,
458 r#"
459mod foo {
460 trait Foo<T> { fn foo(&self, bar: T); }
461 pub struct Param;
462}
463struct Param;
464struct S;
465impl foo::Foo<Param> for S {
466 fn foo(&self, bar: Param) {
467 ${0:todo!()}
468 }
469}"#,
470 );
471 }
472
473 #[test]
474 fn test_qualify_path_associated_item() {
475 check_assist(
476 add_missing_impl_members,
477 r#"
478mod foo {
479 pub struct Bar<T>;
480 impl Bar<T> { type Assoc = u32; }
481 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
482}
483struct S;
484impl foo::Foo for S { <|> }"#,
485 r#"
486mod foo {
487 pub struct Bar<T>;
488 impl Bar<T> { type Assoc = u32; }
489 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
490}
491struct S;
492impl foo::Foo for S {
493 fn foo(&self, bar: foo::Bar<u32>::Assoc) {
494 ${0:todo!()}
495 }
496}"#,
497 );
498 }
499
500 #[test]
501 fn test_qualify_path_nested() {
502 check_assist(
503 add_missing_impl_members,
504 r#"
505mod foo {
506 pub struct Bar<T>;
507 pub struct Baz;
508 trait Foo { fn foo(&self, bar: Bar<Baz>); }
509}
510struct S;
511impl foo::Foo for S { <|> }"#,
512 r#"
513mod foo {
514 pub struct Bar<T>;
515 pub struct Baz;
516 trait Foo { fn foo(&self, bar: Bar<Baz>); }
517}
518struct S;
519impl foo::Foo for S {
520 fn foo(&self, bar: foo::Bar<foo::Baz>) {
521 ${0:todo!()}
522 }
523}"#,
524 );
525 }
526
527 #[test]
528 fn test_qualify_path_fn_trait_notation() {
529 check_assist(
530 add_missing_impl_members,
531 r#"
532mod foo {
533 pub trait Fn<Args> { type Output; }
534 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
535}
536struct S;
537impl foo::Foo for S { <|> }"#,
538 r#"
539mod foo {
540 pub trait Fn<Args> { type Output; }
541 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
542}
543struct S;
544impl foo::Foo for S {
545 fn foo(&self, bar: dyn Fn(u32) -> i32) {
546 ${0:todo!()}
547 }
548}"#,
549 );
550 }
551
552 #[test]
553 fn test_empty_trait() {
554 check_assist_not_applicable(
555 add_missing_impl_members,
556 r#"
557trait Foo;
558struct S;
559impl Foo for S { <|> }"#,
560 )
561 }
562
563 #[test]
564 fn test_ignore_unnamed_trait_members_and_default_methods() {
565 check_assist_not_applicable(
566 add_missing_impl_members,
567 r#"
568trait Foo {
569 fn (arg: u32);
570 fn valid(some: u32) -> bool { false }
571}
572struct S;
573impl Foo for S { <|> }"#,
574 )
575 }
576
577 #[test]
578 fn test_with_docstring_and_attrs() {
579 check_assist(
580 add_missing_impl_members,
581 r#"
582#[doc(alias = "test alias")]
583trait Foo {
584 /// doc string
585 type Output;
586
587 #[must_use]
588 fn foo(&self);
589}
590struct S;
591impl Foo for S {}<|>"#,
592 r#"
593#[doc(alias = "test alias")]
594trait Foo {
595 /// doc string
596 type Output;
597
598 #[must_use]
599 fn foo(&self);
600}
601struct S;
602impl Foo for S {
603 $0type Output;
604
605 fn foo(&self) {
606 todo!()
607 }
608}"#,
609 )
610 }
611
612 #[test]
613 fn test_default_methods() {
614 check_assist(
615 add_missing_default_members,
616 r#"
617trait Foo {
618 type Output;
619
620 const CONST: usize = 42;
621
622 fn valid(some: u32) -> bool { false }
623 fn foo(some: u32) -> bool;
624}
625struct S;
626impl Foo for S { <|> }"#,
627 r#"
628trait Foo {
629 type Output;
630
631 const CONST: usize = 42;
632
633 fn valid(some: u32) -> bool { false }
634 fn foo(some: u32) -> bool;
635}
636struct S;
637impl Foo for S {
638 $0fn valid(some: u32) -> bool { false }
639}"#,
640 )
641 }
642
643 #[test]
644 fn test_generic_single_default_parameter() {
645 check_assist(
646 add_missing_impl_members,
647 r#"
648trait Foo<T = Self> {
649 fn bar(&self, other: &T);
650}
651
652struct S;
653impl Foo for S { <|> }"#,
654 r#"
655trait Foo<T = Self> {
656 fn bar(&self, other: &T);
657}
658
659struct S;
660impl Foo for S {
661 fn bar(&self, other: &Self) {
662 ${0:todo!()}
663 }
664}"#,
665 )
666 }
667
668 #[test]
669 fn test_generic_default_parameter_is_second() {
670 check_assist(
671 add_missing_impl_members,
672 r#"
673trait Foo<T1, T2 = Self> {
674 fn bar(&self, this: &T1, that: &T2);
675}
676
677struct S<T>;
678impl Foo<T> for S<T> { <|> }"#,
679 r#"
680trait Foo<T1, T2 = Self> {
681 fn bar(&self, this: &T1, that: &T2);
682}
683
684struct S<T>;
685impl Foo<T> for S<T> {
686 fn bar(&self, this: &T, that: &Self) {
687 ${0:todo!()}
688 }
689}"#,
690 )
691 }
692
693 #[test]
694 fn test_assoc_type_bounds_are_removed() {
695 check_assist(
696 add_missing_impl_members,
697 r#"
698trait Tr {
699 type Ty: Copy + 'static;
700}
701
702impl Tr for ()<|> {
703}"#,
704 r#"
705trait Tr {
706 type Ty: Copy + 'static;
707}
708
709impl Tr for () {
710 $0type Ty;
711}"#,
712 )
713 }
714
715 #[test]
716 fn test_whitespace_fixup_preserves_bad_tokens() {
717 check_assist(
718 add_missing_impl_members,
719 r#"
720trait Tr {
721 fn foo();
722}
723
724impl Tr for ()<|> {
725 +++
726}"#,
727 r#"
728trait Tr {
729 fn foo();
730}
731
732impl Tr for () {
733 fn foo() {
734 ${0:todo!()}
735 }
736 +++
737}"#,
738 )
739 }
740
741 #[test]
742 fn test_whitespace_fixup_preserves_comments() {
743 check_assist(
744 add_missing_impl_members,
745 r#"
746trait Tr {
747 fn foo();
748}
749
750impl Tr for ()<|> {
751 // very important
752}"#,
753 r#"
754trait Tr {
755 fn foo();
756}
757
758impl Tr for () {
759 fn foo() {
760 ${0:todo!()}
761 }
762 // very important
763}"#,
764 )
765 }
766}