1use ide_db::{
2 imports::import_assets::item_for_path_search, syntax_helpers::suggest_name::NameGenerator,
3 use_trivial_constructor::use_trivial_constructor_with_factory,
4};
5use syntax::{
6 ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit},
7 syntax_editor::Position,
8};
9
10use crate::{
11 AssistContext, AssistId, Assists,
12 utils::{find_struct_impl, generate_impl_with_item},
13};
14
15pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
37 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
38 let field_list: Vec<(String, ast::Type)> = match strukt.kind() {
39 StructKind::Record(named) => {
40 named.fields().filter_map(|f| Some((f.name()?.to_string(), f.ty()?))).collect()
41 }
42 StructKind::Tuple(tuple) => {
43 let mut name_generator = NameGenerator::default();
44 tuple
45 .fields()
46 .enumerate()
47 .filter_map(|(i, f)| {
48 let ty = f.ty()?;
49 let name = match name_generator.for_type(
50 &ctx.sema.resolve_type(&ty)?,
51 ctx.db(),
52 ctx.edition(),
53 ) {
54 Some(name) => name.to_string(),
55 None => name_generator.suggest_name(&format!("_{i}")).to_string(),
56 };
57 Some((name, ty))
58 })
59 .collect()
60 }
61 StructKind::Unit => return None,
62 };
63
64 let impl_def =
66 find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &[String::from("new")])?;
67
68 let current_module = ctx.sema.scope(strukt.syntax())?.module();
69
70 let target = strukt.syntax().text_range();
71 acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| {
72 let editor = builder.make_editor(strukt.syntax());
73 let make = editor.make();
74
75 let trivial_constructors = field_list
76 .iter()
77 .map(|(name, ty)| {
78 let ty = ctx.sema.resolve_type(ty)?;
79
80 let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
81
82 let cfg = ctx
83 .config
84 .find_path_config(ctx.sema.is_nightly(current_module.krate(ctx.sema.db)));
85 let type_path = current_module.find_path(
86 ctx.sema.db,
87 item_for_path_search(ctx.sema.db, item_in_ns)?,
88 cfg,
89 )?;
90
91 let edition = current_module.krate(ctx.db()).edition(ctx.db());
92
93 let expr = use_trivial_constructor_with_factory(
94 make,
95 ctx.sema.db,
96 ide_db::helpers::mod_path_to_ast_with_factory(make, &type_path, edition),
97 &ty,
98 edition,
99 )?;
100
101 Some((make.name_ref(name), Some(expr)))
102 })
103 .collect::<Vec<_>>();
104
105 let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| {
106 if trivial_constructors[i].is_none() {
107 Some(make.param(make.ident_pat(false, false, make.name(name)).into(), ty.clone()))
108 } else {
109 None
110 }
111 });
112 let params = make.param_list(None, params);
113
114 let fields = field_list.iter().enumerate().map(|(i, (name, _))| {
115 if let Some(constructor) = trivial_constructors[i].clone() {
116 constructor
117 } else {
118 (make.name_ref(name), None)
119 }
120 });
121
122 let tail_expr: ast::Expr = match strukt.kind() {
123 StructKind::Record(_) => {
124 let fields = fields.map(|(name, expr)| make.record_expr_field(name, expr));
125 let fields = make.record_expr_field_list(fields);
126 make.record_expr(make.ident_path("Self"), fields).into()
127 }
128 StructKind::Tuple(_) => {
129 let args = fields.map(|(arg, expr)| {
130 let arg = || make.expr_path(make.path_unqualified(make.path_segment(arg)));
131 expr.unwrap_or_else(arg)
132 });
133 let arg_list = make.arg_list(args);
134 make.expr_call(make.expr_path(make.ident_path("Self")), arg_list).into()
135 }
136 StructKind::Unit => unreachable!(),
137 };
138 let body = make.block_expr(None, tail_expr.into());
139
140 let ret_type = make.ret_type(make.ty_path(make.ident_path("Self")).into());
141
142 let fn_ = make
143 .fn_(
144 [],
145 strukt.visibility(),
146 make.name("new"),
147 None,
148 None,
149 params,
150 body,
151 Some(ret_type),
152 false,
153 false,
154 false,
155 false,
156 )
157 .indent(1.into());
158
159 let contain_fn = if let Some(impl_def) = impl_def {
161 let fn_ = fn_.indent(impl_def.indent_level());
162
163 if let Some(l_curly) = impl_def.assoc_item_list().and_then(|list| list.l_curly_token())
164 {
165 editor.insert_all(
166 Position::after(l_curly),
167 vec![
168 make.whitespace(&format!("\n{}", impl_def.indent_level() + 1)).into(),
169 fn_.syntax().clone().into(),
170 make.whitespace("\n").into(),
171 ],
172 );
173 fn_.syntax().clone()
174 } else {
175 let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]);
176 editor.insert(Position::after(impl_def.syntax()), list.syntax());
177 list.syntax().clone()
178 }
179 } else {
180 let indent_level = strukt.indent_level();
182 let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]);
183 let impl_def =
184 generate_impl_with_item(make, &ast::Adt::Struct(strukt.clone()), Some(list))
185 .indent(strukt.indent_level());
186
187 editor.insert_all(
189 Position::after(strukt.syntax()),
190 vec![
191 make.whitespace(&format!("\n\n{indent_level}")).into(),
192 impl_def.syntax().clone().into(),
193 ],
194 );
195 impl_def.syntax().clone()
196 };
197
198 if let Some(fn_) = contain_fn.descendants().find_map(ast::Fn::cast)
199 && let Some(cap) = ctx.config.snippet_cap
200 {
201 match strukt.kind() {
202 StructKind::Tuple(_) => {
203 let struct_args = fn_
204 .body()
205 .unwrap()
206 .syntax()
207 .descendants()
208 .filter(|it| syntax::ast::ArgList::can_cast(it.kind()))
209 .flat_map(|args| args.children())
210 .filter(|it| syntax::ast::PathExpr::can_cast(it.kind()))
211 .enumerate()
212 .filter_map(|(i, node)| {
213 if trivial_constructors[i].is_none() { Some(node) } else { None }
214 });
215 if let Some(fn_params) = fn_.param_list() {
216 for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) {
217 if let Some(fn_pat) = fn_param.pat() {
218 let fn_pat = fn_pat.syntax().clone();
219 let placeholder = builder.make_placeholder_snippet(cap);
220 editor.add_annotation_all(vec![struct_arg, fn_pat], placeholder)
221 }
222 }
223 }
224 }
225 _ => {}
226 }
227
228 if let Some(name) = fn_.name() {
230 let tabstop_before = builder.make_tabstop_before(cap);
231 editor.add_annotation(name.syntax(), tabstop_before);
232 }
233 }
234 builder.add_file_edits(ctx.vfs_file_id(), editor);
235 })
236}
237
238#[cfg(test)]
239mod record_tests {
240 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
241
242 use super::*;
243
244 #[test]
245 fn test_generate_new_with_zst_fields() {
246 check_assist(
247 generate_new,
248 r#"
249struct Empty;
250
251struct Foo { empty: Empty $0}
252"#,
253 r#"
254struct Empty;
255
256struct Foo { empty: Empty }
257
258impl Foo {
259 fn $0new() -> Self {
260 Self { empty: Empty }
261 }
262}
263"#,
264 );
265 check_assist(
266 generate_new,
267 r#"
268struct Empty;
269
270struct Foo { baz: String, empty: Empty $0}
271"#,
272 r#"
273struct Empty;
274
275struct Foo { baz: String, empty: Empty }
276
277impl Foo {
278 fn $0new(baz: String) -> Self {
279 Self { baz, empty: Empty }
280 }
281}
282"#,
283 );
284 check_assist(
285 generate_new,
286 r#"
287enum Empty { Bar }
288
289struct Foo { empty: Empty $0}
290"#,
291 r#"
292enum Empty { Bar }
293
294struct Foo { empty: Empty }
295
296impl Foo {
297 fn $0new() -> Self {
298 Self { empty: Empty::Bar }
299 }
300}
301"#,
302 );
303
304 check_assist(
306 generate_new,
307 r#"
308struct Empty {}
309
310struct Foo { empty: Empty $0}
311"#,
312 r#"
313struct Empty {}
314
315struct Foo { empty: Empty }
316
317impl Foo {
318 fn $0new(empty: Empty) -> Self {
319 Self { empty }
320 }
321}
322"#,
323 );
324 check_assist(
325 generate_new,
326 r#"
327enum Empty { Bar {} }
328
329struct Foo { empty: Empty $0}
330"#,
331 r#"
332enum Empty { Bar {} }
333
334struct Foo { empty: Empty }
335
336impl Foo {
337 fn $0new(empty: Empty) -> Self {
338 Self { empty }
339 }
340}
341"#,
342 );
343 }
344
345 #[test]
346 fn test_generate_new() {
347 check_assist(
348 generate_new,
349 r#"
350struct Foo {$0}
351"#,
352 r#"
353struct Foo {}
354
355impl Foo {
356 fn $0new() -> Self {
357 Self { }
358 }
359}
360"#,
361 );
362 check_assist(
363 generate_new,
364 r#"
365struct Foo<T: Clone> {$0}
366"#,
367 r#"
368struct Foo<T: Clone> {}
369
370impl<T: Clone> Foo<T> {
371 fn $0new() -> Self {
372 Self { }
373 }
374}
375"#,
376 );
377 check_assist(
378 generate_new,
379 r#"
380struct Foo<'a, T: Foo<'a>> {$0}
381"#,
382 r#"
383struct Foo<'a, T: Foo<'a>> {}
384
385impl<'a, T: Foo<'a>> Foo<'a, T> {
386 fn $0new() -> Self {
387 Self { }
388 }
389}
390"#,
391 );
392 check_assist(
393 generate_new,
394 r#"
395struct Foo { baz: String $0}
396"#,
397 r#"
398struct Foo { baz: String }
399
400impl Foo {
401 fn $0new(baz: String) -> Self {
402 Self { baz }
403 }
404}
405"#,
406 );
407 check_assist(
408 generate_new,
409 r#"
410struct Foo { baz: String, qux: Vec<i32> $0}
411"#,
412 r#"
413struct Foo { baz: String, qux: Vec<i32> }
414
415impl Foo {
416 fn $0new(baz: String, qux: Vec<i32>) -> Self {
417 Self { baz, qux }
418 }
419}
420"#,
421 );
422 }
423
424 #[test]
425 fn check_that_visibility_modifiers_dont_get_brought_in() {
426 check_assist(
427 generate_new,
428 r#"
429struct Foo { pub baz: String, pub qux: Vec<i32> $0}
430"#,
431 r#"
432struct Foo { pub baz: String, pub qux: Vec<i32> }
433
434impl Foo {
435 fn $0new(baz: String, qux: Vec<i32>) -> Self {
436 Self { baz, qux }
437 }
438}
439"#,
440 );
441 }
442
443 #[test]
444 fn check_it_reuses_existing_impls() {
445 check_assist(
446 generate_new,
447 r#"
448struct Foo {$0}
449
450impl Foo {}
451"#,
452 r#"
453struct Foo {}
454
455impl Foo {
456 fn $0new() -> Self {
457 Self { }
458 }
459}
460"#,
461 );
462 check_assist(
463 generate_new,
464 r#"
465struct Foo {$0}
466
467impl Foo {
468 fn qux(&self) {}
469}
470"#,
471 r#"
472struct Foo {}
473
474impl Foo {
475 fn $0new() -> Self {
476 Self { }
477 }
478
479 fn qux(&self) {}
480}
481"#,
482 );
483
484 check_assist(
485 generate_new,
486 r#"
487struct Foo {$0}
488
489impl Foo {
490 fn qux(&self) {}
491 fn baz() -> i32 {
492 5
493 }
494}
495"#,
496 r#"
497struct Foo {}
498
499impl Foo {
500 fn $0new() -> Self {
501 Self { }
502 }
503
504 fn qux(&self) {}
505 fn baz() -> i32 {
506 5
507 }
508}
509"#,
510 );
511 }
512
513 #[test]
514 fn non_zero_indent() {
515 check_assist(
516 generate_new,
517 r#"
518mod foo {
519 struct $0Foo {}
520}
521"#,
522 r#"
523mod foo {
524 struct Foo {}
525
526 impl Foo {
527 fn $0new() -> Self {
528 Self { }
529 }
530 }
531}
532"#,
533 );
534 check_assist(
535 generate_new,
536 r#"
537mod foo {
538 mod bar {
539 struct $0Foo {}
540 }
541}
542"#,
543 r#"
544mod foo {
545 mod bar {
546 struct Foo {}
547
548 impl Foo {
549 fn $0new() -> Self {
550 Self { }
551 }
552 }
553 }
554}
555"#,
556 );
557 check_assist(
558 generate_new,
559 r#"
560mod foo {
561 struct $0Foo {}
562
563 impl Foo {
564 fn some() {}
565 }
566}
567"#,
568 r#"
569mod foo {
570 struct Foo {}
571
572 impl Foo {
573 fn $0new() -> Self {
574 Self { }
575 }
576
577 fn some() {}
578 }
579}
580"#,
581 );
582 check_assist(
583 generate_new,
584 r#"
585mod foo {
586 mod bar {
587 struct $0Foo {}
588
589 impl Foo {
590 fn some() {}
591 }
592 }
593}
594"#,
595 r#"
596mod foo {
597 mod bar {
598 struct Foo {}
599
600 impl Foo {
601 fn $0new() -> Self {
602 Self { }
603 }
604
605 fn some() {}
606 }
607 }
608}
609"#,
610 );
611 check_assist(
612 generate_new,
613 r#"
614mod foo {
615 mod bar {
616struct $0Foo {}
617
618 impl Foo {
619 fn some() {}
620 }
621 }
622}
623"#,
624 r#"
625mod foo {
626 mod bar {
627struct Foo {}
628
629 impl Foo {
630 fn $0new() -> Self {
631 Self { }
632 }
633
634 fn some() {}
635 }
636 }
637}
638"#,
639 );
640 }
641
642 #[test]
643 fn check_visibility_of_new_fn_based_on_struct() {
644 check_assist(
645 generate_new,
646 r#"
647pub struct Foo {$0}
648"#,
649 r#"
650pub struct Foo {}
651
652impl Foo {
653 pub fn $0new() -> Self {
654 Self { }
655 }
656}
657"#,
658 );
659 check_assist(
660 generate_new,
661 r#"
662pub(crate) struct Foo {$0}
663"#,
664 r#"
665pub(crate) struct Foo {}
666
667impl Foo {
668 pub(crate) fn $0new() -> Self {
669 Self { }
670 }
671}
672"#,
673 );
674 }
675
676 #[test]
677 fn generate_new_not_applicable_if_fn_exists() {
678 check_assist_not_applicable(
679 generate_new,
680 r#"
681struct Foo {$0}
682
683impl Foo {
684 fn new() -> Self {
685 Self
686 }
687}
688"#,
689 );
690
691 check_assist_not_applicable(
692 generate_new,
693 r#"
694struct Foo {$0}
695
696impl Foo {
697 fn New() -> Self {
698 Self
699 }
700}
701"#,
702 );
703 }
704
705 #[test]
706 fn generate_new_target() {
707 check_assist_target(
708 generate_new,
709 r#"
710struct SomeThingIrrelevant;
711/// Has a lifetime parameter
712struct Foo<'a, T: Foo<'a>> {$0}
713struct EvenMoreIrrelevant;
714"#,
715 "/// Has a lifetime parameter
716struct Foo<'a, T: Foo<'a>> {}",
717 );
718 }
719
720 #[test]
721 fn test_unrelated_new() {
722 check_assist(
723 generate_new,
724 r#"
725pub struct AstId<N: AstNode> {
726 file_id: HirFileId,
727 file_ast_id: FileAstId<N>,
728}
729
730impl<N: AstNode> AstId<N> {
731 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
732 AstId { file_id, file_ast_id }
733 }
734}
735
736pub struct Source<T> {
737 pub file_id: HirFileId,$0
738 pub ast: T,
739}
740
741impl<T> Source<T> {
742 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
743 Source { file_id: self.file_id, ast: f(self.ast) }
744 }
745}
746"#,
747 r#"
748pub struct AstId<N: AstNode> {
749 file_id: HirFileId,
750 file_ast_id: FileAstId<N>,
751}
752
753impl<N: AstNode> AstId<N> {
754 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
755 AstId { file_id, file_ast_id }
756 }
757}
758
759pub struct Source<T> {
760 pub file_id: HirFileId,
761 pub ast: T,
762}
763
764impl<T> Source<T> {
765 pub fn $0new(file_id: HirFileId, ast: T) -> Self {
766 Self { file_id, ast }
767 }
768
769 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
770 Source { file_id: self.file_id, ast: f(self.ast) }
771 }
772}
773"#,
774 );
775 }
776}
777
778#[cfg(test)]
779mod tuple_tests {
780 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
781
782 use super::*;
783
784 #[test]
785 fn test_generate_new_with_zst_fields() {
786 check_assist(
787 generate_new,
788 r#"
789struct Empty;
790
791struct Foo(Empty$0);
792"#,
793 r#"
794struct Empty;
795
796struct Foo(Empty);
797
798impl Foo {
799 fn $0new() -> Self {
800 Self(Empty)
801 }
802}
803"#,
804 );
805 check_assist(
806 generate_new,
807 r#"
808struct Empty;
809
810struct Foo(String, Empty$0);
811"#,
812 r#"
813struct Empty;
814
815struct Foo(String, Empty);
816
817impl Foo {
818 fn $0new(${1:_0}: String) -> Self {
819 Self(${1:_0}, Empty)
820 }
821}
822"#,
823 );
824 check_assist(
825 generate_new,
826 r#"
827enum Empty { Bar }
828
829struct Foo(Empty$0);
830"#,
831 r#"
832enum Empty { Bar }
833
834struct Foo(Empty);
835
836impl Foo {
837 fn $0new() -> Self {
838 Self(Empty::Bar)
839 }
840}
841"#,
842 );
843
844 check_assist(
846 generate_new,
847 r#"
848struct Empty {}
849
850struct Foo(Empty$0);
851"#,
852 r#"
853struct Empty {}
854
855struct Foo(Empty);
856
857impl Foo {
858 fn $0new(${1:empty}: Empty) -> Self {
859 Self(${1:empty})
860 }
861}
862"#,
863 );
864 check_assist(
865 generate_new,
866 r#"
867enum Empty { Bar {} }
868
869struct Foo(Empty$0);
870"#,
871 r#"
872enum Empty { Bar {} }
873
874struct Foo(Empty);
875
876impl Foo {
877 fn $0new(${1:empty}: Empty) -> Self {
878 Self(${1:empty})
879 }
880}
881"#,
882 );
883 }
884
885 #[test]
886 fn test_generate_new() {
887 check_assist(
888 generate_new,
889 r#"
890struct Foo($0);
891"#,
892 r#"
893struct Foo();
894
895impl Foo {
896 fn $0new() -> Self {
897 Self()
898 }
899}
900"#,
901 );
902 check_assist(
903 generate_new,
904 r#"
905struct Foo<T: Clone>($0);
906"#,
907 r#"
908struct Foo<T: Clone>();
909
910impl<T: Clone> Foo<T> {
911 fn $0new() -> Self {
912 Self()
913 }
914}
915"#,
916 );
917 check_assist(
918 generate_new,
919 r#"
920struct Foo<'a, T: Foo<'a>>($0);
921"#,
922 r#"
923struct Foo<'a, T: Foo<'a>>();
924
925impl<'a, T: Foo<'a>> Foo<'a, T> {
926 fn $0new() -> Self {
927 Self()
928 }
929}
930"#,
931 );
932 check_assist(
933 generate_new,
934 r#"
935struct Foo(String$0);
936"#,
937 r#"
938struct Foo(String);
939
940impl Foo {
941 fn $0new(${1:_0}: String) -> Self {
942 Self(${1:_0})
943 }
944}
945"#,
946 );
947 check_assist(
948 generate_new,
949 r#"
950struct Vec<T> { };
951struct Foo(String, Vec<i32>$0);
952"#,
953 r#"
954struct Vec<T> { };
955struct Foo(String, Vec<i32>);
956
957impl Foo {
958 fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
959 Self(${1:_0}, ${2:items})
960 }
961}
962"#,
963 );
964 }
965
966 #[test]
967 fn check_that_visibility_modifiers_dont_get_brought_in() {
968 check_assist(
969 generate_new,
970 r#"
971struct Vec<T> { };
972struct Foo(pub String, pub Vec<i32>$0);
973"#,
974 r#"
975struct Vec<T> { };
976struct Foo(pub String, pub Vec<i32>);
977
978impl Foo {
979 fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
980 Self(${1:_0}, ${2:items})
981 }
982}
983"#,
984 );
985 }
986
987 #[test]
988 fn generate_new_not_applicable_if_fn_exists() {
989 check_assist_not_applicable(
990 generate_new,
991 r#"
992struct Foo($0);
993
994impl Foo {
995 fn new() -> Self {
996 Self
997 }
998}
999"#,
1000 );
1001
1002 check_assist_not_applicable(
1003 generate_new,
1004 r#"
1005struct Foo($0);
1006
1007impl Foo {
1008 fn New() -> Self {
1009 Self
1010 }
1011}
1012"#,
1013 );
1014 }
1015
1016 #[test]
1017 fn generate_new_target() {
1018 check_assist_target(
1019 generate_new,
1020 r#"
1021struct SomeThingIrrelevant;
1022/// Has a lifetime parameter
1023struct Foo<'a, T: Foo<'a>>($0);
1024struct EvenMoreIrrelevant;
1025"#,
1026 "/// Has a lifetime parameter
1027struct Foo<'a, T: Foo<'a>>();",
1028 );
1029 }
1030
1031 #[test]
1032 fn test_unrelated_new() {
1033 check_assist(
1034 generate_new,
1035 r#"
1036pub struct AstId<N: AstNode> {
1037 file_id: HirFileId,
1038 file_ast_id: FileAstId<N>,
1039}
1040
1041impl<N: AstNode> AstId<N> {
1042 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
1043 AstId { file_id, file_ast_id }
1044 }
1045}
1046
1047pub struct Source<T>(pub HirFileId,$0 pub T);
1048
1049impl<T> Source<T> {
1050 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
1051 Source(self.file_id, f(self.ast))
1052 }
1053}
1054"#,
1055 r#"
1056pub struct AstId<N: AstNode> {
1057 file_id: HirFileId,
1058 file_ast_id: FileAstId<N>,
1059}
1060
1061impl<N: AstNode> AstId<N> {
1062 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
1063 AstId { file_id, file_ast_id }
1064 }
1065}
1066
1067pub struct Source<T>(pub HirFileId, pub T);
1068
1069impl<T> Source<T> {
1070 pub fn $0new(${1:_0}: HirFileId, ${2:_1}: T) -> Self {
1071 Self(${1:_0}, ${2:_1})
1072 }
1073
1074 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
1075 Source(self.file_id, f(self.ast))
1076 }
1077}
1078"#,
1079 );
1080 }
1081}