ide_assists/handlers/
generate_impl.rs

1use syntax::{
2    ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make},
3    syntax_editor::{Position, SyntaxEditor},
4};
5
6use crate::{
7    AssistContext, AssistId, Assists,
8    utils::{self, DefaultMethods, IgnoreAssocItems},
9};
10
11fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) {
12    let indent = nominal.indent_level();
13
14    impl_.indent(indent);
15    editor.insert_all(
16        Position::after(nominal.syntax()),
17        vec![
18            // Add a blank line after the ADT, and indentation for the impl to match the ADT
19            make::tokens::whitespace(&format!("\n\n{indent}")).into(),
20            impl_.syntax().clone().into(),
21        ],
22    );
23}
24
25// Assist: generate_impl
26//
27// Adds a new inherent impl for a type.
28//
29// ```
30// struct Ctx$0<T: Clone> {
31//     data: T,
32// }
33// ```
34// ->
35// ```
36// struct Ctx<T: Clone> {
37//     data: T,
38// }
39//
40// impl<T: Clone> Ctx<T> {$0}
41// ```
42pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
43    let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
44    let name = nominal.name()?;
45    let target = nominal.syntax().text_range();
46
47    if ctx.find_node_at_offset::<ast::RecordFieldList>().is_some() {
48        return None;
49    }
50
51    acc.add(
52        AssistId::generate("generate_impl"),
53        format!("Generate impl for `{name}`"),
54        target,
55        |edit| {
56            // Generate the impl
57            let impl_ = utils::generate_impl(&nominal);
58
59            let mut editor = edit.make_editor(nominal.syntax());
60            // Add a tabstop after the left curly brace
61            if let Some(cap) = ctx.config.snippet_cap
62                && let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token())
63            {
64                let tabstop = edit.make_tabstop_after(cap);
65                editor.add_annotation(l_curly, tabstop);
66            }
67
68            insert_impl(&mut editor, &impl_, &nominal);
69            edit.add_file_edits(ctx.vfs_file_id(), editor);
70        },
71    )
72}
73
74// Assist: generate_trait_impl
75//
76// Adds a new trait impl for a type.
77//
78// ```
79// struct $0Ctx<T: Clone> {
80//     data: T,
81// }
82// ```
83// ->
84// ```
85// struct Ctx<T: Clone> {
86//     data: T,
87// }
88//
89// impl<T: Clone> ${1:_} for Ctx<T> {$0}
90// ```
91pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
92    let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
93    let name = nominal.name()?;
94    let target = nominal.syntax().text_range();
95
96    if ctx.find_node_at_offset::<ast::RecordFieldList>().is_some() {
97        return None;
98    }
99
100    acc.add(
101        AssistId::generate("generate_trait_impl"),
102        format!("Generate trait impl for `{name}`"),
103        target,
104        |edit| {
105            // Generate the impl
106            let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder());
107
108            let mut editor = edit.make_editor(nominal.syntax());
109            // Make the trait type a placeholder snippet
110            if let Some(cap) = ctx.config.snippet_cap {
111                if let Some(trait_) = impl_.trait_() {
112                    let placeholder = edit.make_placeholder_snippet(cap);
113                    editor.add_annotation(trait_.syntax(), placeholder);
114                }
115
116                if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
117                    let tabstop = edit.make_tabstop_after(cap);
118                    editor.add_annotation(l_curly, tabstop);
119                }
120            }
121
122            insert_impl(&mut editor, &impl_, &nominal);
123            edit.add_file_edits(ctx.vfs_file_id(), editor);
124        },
125    )
126}
127
128// Assist: generate_impl_trait
129//
130// Adds this trait impl for a type.
131//
132// ```
133// trait $0Foo {
134//     fn foo(&self) -> i32;
135// }
136// ```
137// ->
138// ```
139// trait Foo {
140//     fn foo(&self) -> i32;
141// }
142//
143// impl Foo for ${1:_} {
144//     fn foo(&self) -> i32 {
145//         $0todo!()
146//     }
147// }
148// ```
149pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
150    let name = ctx.find_node_at_offset::<ast::Name>()?;
151    let trait_ = ast::Trait::cast(name.syntax().parent()?)?;
152    let target_scope = ctx.sema.scope(trait_.syntax())?;
153    let hir_trait = ctx.sema.to_def(&trait_)?;
154
155    let target = trait_.syntax().text_range();
156    acc.add(
157        AssistId::generate("generate_impl_trait"),
158        format!("Generate `{name}` impl for type"),
159        target,
160        |edit| {
161            let mut editor = edit.make_editor(trait_.syntax());
162
163            let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder()));
164            let missing_items = utils::filter_assoc_items(
165                &ctx.sema,
166                &hir_trait.items(ctx.db()),
167                DefaultMethods::No,
168                IgnoreAssocItems::DocHiddenAttrPresent,
169            );
170
171            let trait_gen_args = trait_.generic_param_list().map(|list| {
172                make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone()))
173            });
174
175            let make_impl_ = |body| {
176                make::impl_trait(
177                    None,
178                    trait_.unsafe_token().is_some(),
179                    None,
180                    trait_gen_args.clone(),
181                    None,
182                    None,
183                    false,
184                    make::ty(&name.text()),
185                    make::ty_placeholder(),
186                    None,
187                    None,
188                    body,
189                )
190                .clone_for_update()
191            };
192
193            let impl_ = if missing_items.is_empty() {
194                make_impl_(None)
195            } else {
196                let impl_ = make_impl_(None);
197                let assoc_items = utils::add_trait_assoc_items_to_impl(
198                    &ctx.sema,
199                    ctx.config,
200                    &missing_items,
201                    hir_trait,
202                    &impl_,
203                    &target_scope,
204                );
205                let assoc_item_list = make::assoc_item_list(Some(assoc_items));
206                make_impl_(Some(assoc_item_list))
207            };
208
209            if let Some(cap) = ctx.config.snippet_cap {
210                if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) {
211                    for generic in generics.generic_args() {
212                        let placeholder = edit.make_placeholder_snippet(cap);
213                        editor.add_annotation(generic.syntax(), placeholder);
214                    }
215                }
216
217                if let Some(ty) = impl_.self_ty() {
218                    let placeholder = edit.make_placeholder_snippet(cap);
219                    editor.add_annotation(ty.syntax(), placeholder);
220                }
221
222                if let Some(expr) =
223                    impl_.assoc_item_list().and_then(|it| it.assoc_items().find_map(extract_expr))
224                {
225                    let tabstop = edit.make_tabstop_before(cap);
226                    editor.add_annotation(expr.syntax(), tabstop);
227                } else if let Some(l_curly) =
228                    impl_.assoc_item_list().and_then(|it| it.l_curly_token())
229                {
230                    let tabstop = edit.make_tabstop_after(cap);
231                    editor.add_annotation(l_curly, tabstop);
232                }
233            }
234
235            insert_impl(&mut editor, &impl_, &trait_);
236            edit.add_file_edits(ctx.vfs_file_id(), editor);
237        },
238    )
239}
240
241fn extract_expr(item: ast::AssocItem) -> Option<ast::Expr> {
242    let ast::AssocItem::Fn(f) = item else {
243        return None;
244    };
245    f.body()?.tail_expr()
246}
247
248#[cfg(test)]
249mod tests {
250    use crate::tests::{check_assist, check_assist_target};
251
252    use super::*;
253
254    #[test]
255    fn test_add_impl() {
256        check_assist(
257            generate_impl,
258            r#"
259                struct Foo$0 {}
260            "#,
261            r#"
262                struct Foo {}
263
264                impl Foo {$0}
265            "#,
266        );
267    }
268
269    #[test]
270    fn test_add_impl_with_generics() {
271        check_assist(
272            generate_impl,
273            r#"
274                struct Foo$0<T: Clone> {}
275            "#,
276            r#"
277                struct Foo<T: Clone> {}
278
279                impl<T: Clone> Foo<T> {$0}
280            "#,
281        );
282    }
283
284    #[test]
285    fn test_add_impl_with_generics_and_lifetime_parameters() {
286        check_assist(
287            generate_impl,
288            r#"
289                struct Foo<'a, T: Foo<'a>>$0 {}
290            "#,
291            r#"
292                struct Foo<'a, T: Foo<'a>> {}
293
294                impl<'a, T: Foo<'a>> Foo<'a, T> {$0}
295            "#,
296        );
297    }
298
299    #[test]
300    fn test_add_impl_with_attributes() {
301        check_assist(
302            generate_impl,
303            r#"
304                #[cfg(feature = "foo")]
305                struct Foo<'a, T: Foo$0<'a>> {}
306            "#,
307            r#"
308                #[cfg(feature = "foo")]
309                struct Foo<'a, T: Foo<'a>> {}
310
311                #[cfg(feature = "foo")]
312                impl<'a, T: Foo<'a>> Foo<'a, T> {$0}
313            "#,
314        );
315    }
316
317    #[test]
318    fn test_add_impl_with_default_generic() {
319        check_assist(
320            generate_impl,
321            r#"
322                struct Defaulted$0<T = i32> {}
323            "#,
324            r#"
325                struct Defaulted<T = i32> {}
326
327                impl<T> Defaulted<T> {$0}
328            "#,
329        );
330    }
331
332    #[test]
333    fn test_add_impl_with_constrained_default_generic() {
334        check_assist(
335            generate_impl,
336            r#"
337                struct Defaulted$0<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
338            "#,
339            r#"
340                struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
341
342                impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> Defaulted<'a, 'b, T, S> {$0}
343            "#,
344        );
345    }
346
347    #[test]
348    fn test_add_impl_with_const_defaulted_generic() {
349        check_assist(
350            generate_impl,
351            r#"
352                struct Defaulted$0<const N: i32 = 0> {}
353            "#,
354            r#"
355                struct Defaulted<const N: i32 = 0> {}
356
357                impl<const N: i32> Defaulted<N> {$0}
358            "#,
359        );
360    }
361
362    #[test]
363    fn test_add_impl_with_trait_constraint() {
364        check_assist(
365            generate_impl,
366            r#"
367                pub trait Trait {}
368                struct Struct$0<T>
369                where
370                    T: Trait,
371                {
372                    inner: T,
373                }
374            "#,
375            r#"
376                pub trait Trait {}
377                struct Struct<T>
378                where
379                    T: Trait,
380                {
381                    inner: T,
382                }
383
384                impl<T> Struct<T>
385                where
386                    T: Trait,
387                {$0
388                }
389            "#,
390        );
391    }
392
393    #[test]
394    fn add_impl_target() {
395        check_assist_target(
396            generate_impl,
397            r#"
398                struct SomeThingIrrelevant;
399                /// Has a lifetime parameter
400                struct Foo$0<'a, T: Foo<'a>> {}
401                struct EvenMoreIrrelevant;
402            "#,
403            "/// Has a lifetime parameter\nstruct Foo<'a, T: Foo<'a>> {}",
404        );
405    }
406
407    #[test]
408    fn test_add_trait_impl() {
409        check_assist(
410            generate_trait_impl,
411            r#"
412                struct Foo$0 {}
413            "#,
414            r#"
415                struct Foo {}
416
417                impl ${1:_} for Foo {$0}
418            "#,
419        );
420    }
421
422    #[test]
423    fn test_add_trait_impl_with_generics() {
424        check_assist(
425            generate_trait_impl,
426            r#"
427                struct Foo$0<T: Clone> {}
428            "#,
429            r#"
430                struct Foo<T: Clone> {}
431
432                impl<T: Clone> ${1:_} for Foo<T> {$0}
433            "#,
434        );
435    }
436
437    #[test]
438    fn test_add_trait_impl_with_generics_and_lifetime_parameters() {
439        check_assist(
440            generate_trait_impl,
441            r#"
442                struct Foo<'a, T: Foo<'a>>$0 {}
443            "#,
444            r#"
445                struct Foo<'a, T: Foo<'a>> {}
446
447                impl<'a, T: Foo<'a>> ${1:_} for Foo<'a, T> {$0}
448            "#,
449        );
450    }
451
452    #[test]
453    fn test_add_trait_impl_with_attributes() {
454        check_assist(
455            generate_trait_impl,
456            r#"
457                #[cfg(feature = "foo")]
458                struct Foo<'a, T: Foo$0<'a>> {}
459            "#,
460            r#"
461                #[cfg(feature = "foo")]
462                struct Foo<'a, T: Foo<'a>> {}
463
464                #[cfg(feature = "foo")]
465                impl<'a, T: Foo<'a>> ${1:_} for Foo<'a, T> {$0}
466            "#,
467        );
468    }
469
470    #[test]
471    fn test_add_trait_impl_with_default_generic() {
472        check_assist(
473            generate_trait_impl,
474            r#"
475                struct Defaulted$0<T = i32> {}
476            "#,
477            r#"
478                struct Defaulted<T = i32> {}
479
480                impl<T> ${1:_} for Defaulted<T> {$0}
481            "#,
482        );
483    }
484
485    #[test]
486    fn test_add_trait_impl_with_constrained_default_generic() {
487        check_assist(
488            generate_trait_impl,
489            r#"
490                struct Defaulted$0<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
491            "#,
492            r#"
493                struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String, const S: usize> {}
494
495                impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b, const S: usize> ${1:_} for Defaulted<'a, 'b, T, S> {$0}
496            "#,
497        );
498    }
499
500    #[test]
501    fn test_add_trait_impl_with_const_defaulted_generic() {
502        check_assist(
503            generate_trait_impl,
504            r#"
505                struct Defaulted$0<const N: i32 = 0> {}
506            "#,
507            r#"
508                struct Defaulted<const N: i32 = 0> {}
509
510                impl<const N: i32> ${1:_} for Defaulted<N> {$0}
511            "#,
512        );
513    }
514
515    #[test]
516    fn test_add_trait_impl_with_trait_constraint() {
517        check_assist(
518            generate_trait_impl,
519            r#"
520                pub trait Trait {}
521                struct Struct$0<T>
522                where
523                    T: Trait,
524                {
525                    inner: T,
526                }
527            "#,
528            r#"
529                pub trait Trait {}
530                struct Struct<T>
531                where
532                    T: Trait,
533                {
534                    inner: T,
535                }
536
537                impl<T> ${1:_} for Struct<T>
538                where
539                    T: Trait,
540                {$0
541                }
542            "#,
543        );
544    }
545
546    #[test]
547    fn add_trait_impl_target() {
548        check_assist_target(
549            generate_trait_impl,
550            r#"
551                struct SomeThingIrrelevant;
552                /// Has a lifetime parameter
553                struct Foo$0<'a, T: Foo<'a>> {}
554                struct EvenMoreIrrelevant;
555            "#,
556            "/// Has a lifetime parameter\nstruct Foo<'a, T: Foo<'a>> {}",
557        );
558    }
559
560    #[test]
561    fn add_impl_with_indent() {
562        check_assist(
563            generate_impl,
564            r#"
565                mod foo {
566                    struct Bar$0 {}
567                }
568            "#,
569            r#"
570                mod foo {
571                    struct Bar {}
572
573                    impl Bar {$0}
574                }
575            "#,
576        );
577    }
578
579    #[test]
580    fn add_impl_with_multiple_indent() {
581        check_assist(
582            generate_impl,
583            r#"
584                mod foo {
585                    fn bar() {
586                        struct Baz$0 {}
587                    }
588                }
589            "#,
590            r#"
591                mod foo {
592                    fn bar() {
593                        struct Baz {}
594
595                        impl Baz {$0}
596                    }
597                }
598            "#,
599        );
600    }
601
602    #[test]
603    fn add_trait_impl_with_indent() {
604        check_assist(
605            generate_trait_impl,
606            r#"
607                mod foo {
608                    struct Bar$0 {}
609                }
610            "#,
611            r#"
612                mod foo {
613                    struct Bar {}
614
615                    impl ${1:_} for Bar {$0}
616                }
617            "#,
618        );
619    }
620
621    #[test]
622    fn test_add_impl_trait() {
623        check_assist(
624            generate_impl_trait,
625            r#"
626                trait $0Foo {
627                    fn foo(&self) -> i32;
628
629                    fn bar(&self) -> i32 {
630                        self.foo()
631                    }
632                }
633            "#,
634            r#"
635                trait Foo {
636                    fn foo(&self) -> i32;
637
638                    fn bar(&self) -> i32 {
639                        self.foo()
640                    }
641                }
642
643                impl Foo for ${1:_} {
644                    fn foo(&self) -> i32 {
645                        $0todo!()
646                    }
647                }
648            "#,
649        );
650    }
651
652    #[test]
653    fn test_add_impl_trait_use_generic() {
654        check_assist(
655            generate_impl_trait,
656            r#"
657                trait $0Foo<T> {
658                    fn foo(&self) -> T;
659
660                    fn bar(&self) -> T {
661                        self.foo()
662                    }
663                }
664            "#,
665            r#"
666                trait Foo<T> {
667                    fn foo(&self) -> T;
668
669                    fn bar(&self) -> T {
670                        self.foo()
671                    }
672                }
673
674                impl Foo<${1:_}> for ${2:_} {
675                    fn foo(&self) -> _ {
676                        $0todo!()
677                    }
678                }
679            "#,
680        );
681        check_assist(
682            generate_impl_trait,
683            r#"
684                trait $0Foo<T, U> {
685                    fn foo(&self) -> T;
686
687                    fn bar(&self) -> T {
688                        self.foo()
689                    }
690                }
691            "#,
692            r#"
693                trait Foo<T, U> {
694                    fn foo(&self) -> T;
695
696                    fn bar(&self) -> T {
697                        self.foo()
698                    }
699                }
700
701                impl Foo<${1:_}, ${2:_}> for ${3:_} {
702                    fn foo(&self) -> _ {
703                        $0todo!()
704                    }
705                }
706            "#,
707        );
708    }
709
710    #[test]
711    fn test_add_impl_trait_docs() {
712        check_assist(
713            generate_impl_trait,
714            r#"
715                /// foo
716                trait $0Foo {
717                    /// foo method
718                    fn foo(&self) -> i32;
719
720                    fn bar(&self) -> i32 {
721                        self.foo()
722                    }
723                }
724            "#,
725            r#"
726                /// foo
727                trait Foo {
728                    /// foo method
729                    fn foo(&self) -> i32;
730
731                    fn bar(&self) -> i32 {
732                        self.foo()
733                    }
734                }
735
736                impl Foo for ${1:_} {
737                    fn foo(&self) -> i32 {
738                        $0todo!()
739                    }
740                }
741            "#,
742        );
743    }
744
745    #[test]
746    fn test_add_impl_trait_assoc_types() {
747        check_assist(
748            generate_impl_trait,
749            r#"
750                trait $0Foo {
751                    type Output;
752
753                    fn foo(&self) -> Self::Output;
754                }
755            "#,
756            r#"
757                trait Foo {
758                    type Output;
759
760                    fn foo(&self) -> Self::Output;
761                }
762
763                impl Foo for ${1:_} {
764                    type Output;
765
766                    fn foo(&self) -> Self::Output {
767                        $0todo!()
768                    }
769                }
770            "#,
771        );
772    }
773
774    #[test]
775    fn test_add_impl_trait_indent() {
776        check_assist(
777            generate_impl_trait,
778            r#"
779                mod foo {
780                    mod bar {
781                        trait $0Foo {
782                            type Output;
783
784                            fn foo(&self) -> Self::Output;
785                        }
786                    }
787                }
788            "#,
789            r#"
790                mod foo {
791                    mod bar {
792                        trait Foo {
793                            type Output;
794
795                            fn foo(&self) -> Self::Output;
796                        }
797
798                        impl Foo for ${1:_} {
799                            type Output;
800
801                            fn foo(&self) -> Self::Output {
802                                $0todo!()
803                            }
804                        }
805                    }
806                }
807            "#,
808        );
809    }
810
811    #[test]
812    fn test_add_impl_trait_empty() {
813        check_assist(
814            generate_impl_trait,
815            r#"
816                trait $0Foo {}
817            "#,
818            r#"
819                trait Foo {}
820
821                impl Foo for ${1:_} {$0}
822            "#,
823        );
824    }
825}