Skip to main content

ide_assists/handlers/
generate_impl.rs

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