1use either::Either;
2use hir::{
3 AssocItem, FindPathConfig, HasVisibility, HirDisplay, InFile, Type,
4 db::{ExpandDatabase, HirDatabase},
5 sym,
6};
7use ide_db::{
8 FxHashMap,
9 assists::{Assist, ExprFillDefaultMode},
10 famous_defs::FamousDefs,
11 imports::import_assets::item_for_path_search,
12 source_change::SourceChange,
13 syntax_helpers::tree_diff::diff,
14 text_edit::TextEdit,
15 use_trivial_constructor::use_trivial_constructor,
16};
17use stdx::format_to;
18use syntax::{
19 AstNode, Edition, SyntaxNode, SyntaxNodePtr, ToSmolStr,
20 ast::{self, make},
21 syntax_editor::SyntaxEditor,
22};
23
24use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
25
26pub(crate) fn missing_fields(
38 ctx: &DiagnosticsContext<'_, '_>,
39 d: &hir::MissingFields,
40) -> Diagnostic {
41 let mut message = String::from("missing structure fields:\n");
42 for (field, _) in &d.missed_fields {
43 format_to!(message, "- {}\n", field.display(ctx.sema.db, ctx.edition));
44 }
45
46 let ptr = InFile::new(
47 d.file,
48 d.field_list_parent_path
49 .map(SyntaxNodePtr::from)
50 .unwrap_or_else(|| d.field_list_parent.into()),
51 );
52
53 Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError("E0063"), message, ptr)
54 .stable()
55 .with_fixes(fixes(ctx, d))
56}
57
58fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
59 if d.missed_fields.iter().any(|(name, _)| name.as_tuple_index().is_some()) {
65 return None;
66 }
67
68 let root = ctx.sema.db.parse_or_expand(d.file);
69
70 let current_module =
71 ctx.sema.scope(d.field_list_parent.to_node(&root).syntax()).map(|it| it.module());
72 let range = InFile::new(d.file, d.field_list_parent.text_range())
73 .original_node_file_range_rooted_opt(ctx.sema.db)?;
74
75 if let Some(current_module) = current_module
76 && d.missed_fields.iter().any(|(_, field)| !field.is_visible_from(ctx.db(), current_module))
77 {
78 return None;
79 }
80
81 let build_text_edit = |new_syntax: &SyntaxNode, old_syntax| {
82 let edit = {
83 let old_range = ctx.sema.original_range_opt(old_syntax)?;
84 if old_range.file_id != range.file_id {
85 return None;
86 }
87 let mut builder = TextEdit::builder();
88 if d.file.is_macro() {
89 builder.replace(old_range.range, new_syntax.to_string());
95 } else {
96 diff(old_syntax, new_syntax).into_text_edit(&mut builder);
97 }
98 builder.finish()
99 };
100 Some(vec![fix(
101 "fill_missing_fields",
102 "Fill struct fields",
103 SourceChange::from_text_edit(range.file_id.file_id(ctx.sema.db), edit),
104 range.range,
105 )])
106 };
107
108 match &d.field_list_parent.to_node(&root) {
109 Either::Left(field_list_parent) => {
110 let missing_fields = ctx.sema.record_literal_missing_fields(field_list_parent);
111
112 let mut locals = FxHashMap::default();
113 ctx.sema.scope(field_list_parent.syntax())?.process_all_names(&mut |name, def| {
114 if let hir::ScopeDef::Local(local) = def {
115 locals.insert(name, local);
116 }
117 });
118
119 let old_field_list = field_list_parent.record_expr_field_list()?;
120 let root = old_field_list.syntax().ancestors().last()?;
121 let (editor, _) = SyntaxEditor::new(root);
122 let make = editor.make();
123
124 let generate_fill_expr = |ty: &Type<'_>| match ctx.config.expr_fill_default {
125 ExprFillDefaultMode::Todo => make.expr_todo(),
126 ExprFillDefaultMode::Underscore => make.expr_underscore().into(),
127 ExprFillDefaultMode::Default => {
128 get_default_constructor(ctx, d, ty).unwrap_or_else(|| make.expr_todo())
129 }
130 };
131
132 let mut new_fields = Vec::new();
133 for (f, ty) in missing_fields.iter() {
134 let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) {
135 cov_mark::hit!(field_shorthand);
136 let candidate_ty = local_candidate.ty(ctx.sema.db);
137 if candidate_ty.could_coerce_to(ctx.sema.db, ty) {
138 None
139 } else {
140 Some(generate_fill_expr(ty))
141 }
142 } else {
143 let expr = (|| -> Option<ast::Expr> {
144 let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
145
146 let type_path = current_module?.find_path(
147 ctx.sema.db,
148 item_for_path_search(ctx.sema.db, item_in_ns)?,
149 FindPathConfig {
150 prefer_no_std: ctx.config.prefer_no_std,
151 prefer_prelude: ctx.config.prefer_prelude,
152 prefer_absolute: ctx.config.prefer_absolute,
153 allow_unstable: ctx.is_nightly,
154 },
155 )?;
156
157 use_trivial_constructor(
158 ctx.sema.db,
159 ide_db::helpers::mod_path_to_ast(&type_path, ctx.edition),
160 ty,
161 ctx.edition,
162 )
163 })();
164
165 if expr.is_some() { expr } else { Some(generate_fill_expr(ty)) }
166 };
167 let field = make.record_expr_field(
168 make.name_ref(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
169 field_expr,
170 );
171 new_fields.push(field);
172 }
173 old_field_list.add_fields(&editor, new_fields);
174 let new_field_list = editor.finish().find_element(old_field_list.syntax())?;
175 build_text_edit(&new_field_list, old_field_list.syntax())
176 }
177 Either::Right(field_list_parent) => {
178 let missing_fields = ctx.sema.record_pattern_missing_fields(field_list_parent);
179
180 let old_field_list = field_list_parent.record_pat_field_list()?;
181 let root = old_field_list.syntax().ancestors().last()?;
182 let (editor, _) = SyntaxEditor::new(root);
183 let make = editor.make();
184
185 let mut new_fields = Vec::new();
186 for (f, _) in missing_fields.iter() {
187 let field = make.record_pat_field_shorthand(
188 make.ident_pat(
189 false,
190 false,
191 make.name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
192 )
193 .into(),
194 );
195 new_fields.push(field);
196 }
197 old_field_list.add_fields(&editor, new_fields);
198 let new_field_list = editor.finish().find_element(old_field_list.syntax())?;
199 build_text_edit(&new_field_list, old_field_list.syntax())
200 }
201 }
202}
203
204fn make_ty(
205 ty: &hir::Type<'_>,
206 db: &dyn HirDatabase,
207 module: hir::Module,
208 edition: Edition,
209) -> ast::Type {
210 let ty_str = match ty.as_adt() {
211 Some(adt) => adt.name(db).display(db, edition).to_string(),
212 None => {
213 ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_owned())
214 }
215 };
216
217 make::ty(&ty_str)
218}
219
220fn get_default_constructor(
221 ctx: &DiagnosticsContext<'_, '_>,
222 d: &hir::MissingFields,
223 ty: &Type<'_>,
224) -> Option<ast::Expr> {
225 if let Some(builtin_ty) = ty.as_builtin() {
226 if builtin_ty.is_int() || builtin_ty.is_uint() {
227 return Some(make::ext::zero_number());
228 }
229 if builtin_ty.is_float() {
230 return Some(make::ext::zero_float());
231 }
232 if builtin_ty.is_char() {
233 return Some(make::ext::empty_char());
234 }
235 if builtin_ty.is_str() {
236 return Some(make::ext::empty_str());
237 }
238 if builtin_ty.is_bool() {
239 return Some(make::ext::default_bool());
240 }
241 }
242
243 let krate = ctx
244 .sema
245 .file_to_module_def(d.file.original_file(ctx.sema.db).file_id(ctx.sema.db))?
246 .krate(ctx.sema.db);
247 let module = krate.root_module(ctx.sema.db);
248
249 let has_new_func = ty
251 .iterate_assoc_items(ctx.sema.db, |assoc_item| {
252 if let AssocItem::Function(func) = assoc_item
253 && func.name(ctx.sema.db) == sym::new
254 && func.assoc_fn_params(ctx.sema.db).is_empty()
255 {
256 return Some(());
257 }
258
259 None
260 })
261 .is_some();
262
263 let famous_defs = FamousDefs(&ctx.sema, krate);
264 if has_new_func {
265 Some(make::ext::expr_ty_new(&make_ty(ty, ctx.sema.db, module, ctx.edition)))
266 } else if ty.as_adt() == famous_defs.core_option_Option()?.ty(ctx.sema.db).as_adt() {
267 Some(make::ext::option_none())
268 } else if !ty.is_array()
269 && ty.impls_trait(ctx.sema.db, famous_defs.core_default_Default()?, &[])
270 {
271 Some(make::ext::expr_ty_default(&make_ty(ty, ctx.sema.db, module, ctx.edition)))
272 } else {
273 None
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use crate::tests::{check_diagnostics, check_fix, check_no_fix};
280
281 #[test]
282 fn missing_record_pat_field_diagnostic() {
283 check_diagnostics(
284 r#"
285struct S { foo: i32, bar: () }
286fn baz(s: S) {
287 let S { foo: _ } = s;
288 //^ 💡 error: missing structure fields:
289 //| - bar
290}
291"#,
292 );
293 }
294
295 #[test]
296 fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
297 check_diagnostics(
298 r"
299struct S { foo: i32, bar: () }
300fn baz(s: S) -> i32 {
301 match s {
302 S { foo, .. } => foo,
303 }
304}
305",
306 )
307 }
308
309 #[test]
310 fn missing_record_pat_field_box() {
311 check_diagnostics(
312 r#"
313#![feature(lang_items)]
314#[lang = "owned_box"]
315struct Box<T>(T);
316struct S { s: Box<u32> }
317fn x(a: S) {
318 let S { box s } = a;
319}
320"#,
321 )
322 }
323
324 #[test]
325 fn missing_record_pat_field_ref() {
326 check_diagnostics(
327 r"
328struct S { s: u32 }
329fn x(a: S) {
330 let S { ref s } = a;
331 _ = s;
332}
333",
334 )
335 }
336
337 #[test]
338 fn missing_record_expr_in_assignee_expr() {
339 check_diagnostics(
340 r"
341struct S { s: usize, t: usize }
342struct S2 { s: S, t: () }
343struct T(S);
344fn regular(a: S) {
345 let s;
346 S { s, .. } = a;
347 _ = s;
348}
349fn nested(a: S2) {
350 let s;
351 S2 { s: S { s, .. }, .. } = a;
352 _ = s;
353}
354fn in_tuple(a: (S,)) {
355 let s;
356 (S { s, .. },) = a;
357 _ = s;
358}
359fn in_array(a: [S;1]) {
360 let s;
361 [S { s, .. },] = a;
362 _ = s;
363}
364fn in_tuple_struct(a: T) {
365 let s;
366 T(S { s, .. }) = a;
367 _ = s;
368}
369 ",
370 );
371 }
372
373 #[test]
374 fn range_mapping_out_of_macros() {
375 check_fix(
376 r#"
377fn some() {}
378fn items() {}
379fn here() {}
380
381macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
382
383fn main() {
384 let _x = id![Foo { a: $042 }];
385}
386
387pub struct Foo { pub a: i32, pub b: i32 }
388"#,
389 r#"
390fn some() {}
391fn items() {}
392fn here() {}
393
394macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
395
396fn main() {
397 let _x = id![Foo {a:42, b: 0 }];
398}
399
400pub struct Foo { pub a: i32, pub b: i32 }
401"#,
402 );
403 }
404
405 #[test]
406 fn test_fill_struct_fields_empty() {
407 check_fix(
408 r#"
409//- minicore: option
410struct TestStruct { one: i32, two: i64, three: Option<i32>, four: bool }
411
412fn test_fn() {
413 let s = TestStruct {$0};
414}
415"#,
416 r#"
417struct TestStruct { one: i32, two: i64, three: Option<i32>, four: bool }
418
419fn test_fn() {
420 let s = TestStruct { one: 0, two: 0, three: None, four: false };
421}
422"#,
423 );
424 }
425
426 #[test]
427 fn test_fill_struct_zst_fields() {
428 check_fix(
429 r#"
430struct Empty;
431
432struct TestStruct { one: i32, two: Empty }
433
434fn test_fn() {
435 let s = TestStruct {$0};
436}
437"#,
438 r#"
439struct Empty;
440
441struct TestStruct { one: i32, two: Empty }
442
443fn test_fn() {
444 let s = TestStruct { one: 0, two: Empty };
445}
446"#,
447 );
448 check_fix(
449 r#"
450enum Empty { Foo };
451
452struct TestStruct { one: i32, two: Empty }
453
454fn test_fn() {
455 let s = TestStruct {$0};
456}
457"#,
458 r#"
459enum Empty { Foo };
460
461struct TestStruct { one: i32, two: Empty }
462
463fn test_fn() {
464 let s = TestStruct { one: 0, two: Empty::Foo };
465}
466"#,
467 );
468
469 check_fix(
471 r#"
472struct Empty {};
473
474struct TestStruct { one: i32, two: Empty }
475
476fn test_fn() {
477 let s = TestStruct {$0};
478}
479"#,
480 r#"
481struct Empty {};
482
483struct TestStruct { one: i32, two: Empty }
484
485fn test_fn() {
486 let s = TestStruct { one: 0, two: todo!() };
487}
488"#,
489 );
490 check_fix(
491 r#"
492enum Empty { Foo {} };
493
494struct TestStruct { one: i32, two: Empty }
495
496fn test_fn() {
497 let s = TestStruct {$0};
498}
499"#,
500 r#"
501enum Empty { Foo {} };
502
503struct TestStruct { one: i32, two: Empty }
504
505fn test_fn() {
506 let s = TestStruct { one: 0, two: todo!() };
507}
508"#,
509 );
510 }
511
512 #[test]
513 fn test_fill_struct_fields_self() {
514 check_fix(
515 r#"
516struct TestStruct { one: i32 }
517
518impl TestStruct {
519 fn test_fn() { let s = Self {$0}; }
520}
521"#,
522 r#"
523struct TestStruct { one: i32 }
524
525impl TestStruct {
526 fn test_fn() { let s = Self { one: 0 }; }
527}
528"#,
529 );
530 }
531
532 #[test]
533 fn test_fill_struct_fields_enum() {
534 check_fix(
535 r#"
536enum Expr {
537 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
538}
539
540impl Expr {
541 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
542 Expr::Bin {$0 }
543 }
544}
545"#,
546 r#"
547enum Expr {
548 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
549}
550
551impl Expr {
552 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
553 Expr::Bin { lhs, rhs }
554 }
555}
556"#,
557 );
558 }
559
560 #[test]
561 fn test_fill_struct_fields_partial() {
562 check_fix(
563 r#"
564struct TestStruct { one: i32, two: i64 }
565
566fn test_fn() {
567 let s = TestStruct{ two: 2$0 };
568}
569"#,
570 r"
571struct TestStruct { one: i32, two: i64 }
572
573fn test_fn() {
574 let s = TestStruct{ two: 2, one: 0 };
575}
576",
577 );
578 }
579
580 #[test]
581 fn test_fill_struct_fields_new() {
582 check_fix(
583 r#"
584struct TestWithNew(usize);
585impl TestWithNew {
586 pub fn new() -> Self {
587 Self(0)
588 }
589}
590struct TestStruct { one: i32, two: TestWithNew }
591
592fn test_fn() {
593 let s = TestStruct{ $0 };
594}
595"#,
596 r"
597struct TestWithNew(usize);
598impl TestWithNew {
599 pub fn new() -> Self {
600 Self(0)
601 }
602}
603struct TestStruct { one: i32, two: TestWithNew }
604
605fn test_fn() {
606 let s = TestStruct{ one: 0, two: TestWithNew::new() };
607}
608",
609 );
610 }
611
612 #[test]
613 fn test_fill_struct_fields_default() {
614 check_fix(
615 r#"
616//- minicore: default, option, slice
617struct TestWithDefault(usize);
618impl Default for TestWithDefault {
619 pub fn default() -> Self {
620 Self(0)
621 }
622}
623struct TestStruct { one: i32, two: TestWithDefault, r: &'static [i32] }
624
625fn test_fn() {
626 let s = TestStruct{ $0 };
627}
628"#,
629 r"
630struct TestWithDefault(usize);
631impl Default for TestWithDefault {
632 pub fn default() -> Self {
633 Self(0)
634 }
635}
636struct TestStruct { one: i32, two: TestWithDefault, r: &'static [i32] }
637
638fn test_fn() {
639 let s = TestStruct{ one: 0, two: TestWithDefault::default(), r: <&'static [i32]>::default() };
640}
641",
642 );
643 }
644
645 #[test]
646 fn test_fill_struct_fields_raw_ident() {
647 check_fix(
648 r#"
649struct TestStruct { r#type: u8 }
650
651fn test_fn() {
652 TestStruct { $0 };
653}
654"#,
655 r"
656struct TestStruct { r#type: u8 }
657
658fn test_fn() {
659 TestStruct { r#type: 0 };
660}
661",
662 );
663 }
664
665 #[test]
666 fn test_fill_struct_fields_no_diagnostic() {
667 check_diagnostics(
668 r#"
669struct TestStruct { one: i32, two: i64 }
670
671fn test_fn() {
672 let one = 1;
673 let _s = TestStruct{ one, two: 2 };
674}
675 "#,
676 );
677 }
678
679 #[test]
680 fn test_fill_struct_fields_no_diagnostic_on_spread() {
681 check_diagnostics(
682 r#"
683struct TestStruct { one: i32, two: i64 }
684
685fn test_fn() {
686 let one = 1;
687 let a = TestStruct{ one, two: 2 };
688 let _ = TestStruct{ ..a };
689}
690"#,
691 );
692 }
693
694 #[test]
695 fn test_fill_struct_fields_blank_line() {
696 check_fix(
697 r#"
698struct S { a: (), b: () }
699
700fn f() {
701 S {
702 $0
703 };
704}
705"#,
706 r#"
707struct S { a: (), b: () }
708
709fn f() {
710 S {
711 a: todo!(),
712 b: todo!(),
713 };
714}
715"#,
716 );
717 }
718
719 #[test]
720 fn test_fill_struct_fields_shorthand() {
721 cov_mark::check!(field_shorthand);
722 check_fix(
723 r#"
724struct S { a: &'static str, b: i32 }
725
726fn f() {
727 let a = "hello";
728 let b = 1i32;
729 S {
730 $0
731 };
732}
733"#,
734 r#"
735struct S { a: &'static str, b: i32 }
736
737fn f() {
738 let a = "hello";
739 let b = 1i32;
740 S {
741 a,
742 b,
743 };
744}
745"#,
746 );
747 }
748
749 #[test]
750 fn test_fill_struct_fields_shorthand_ty_mismatch() {
751 check_fix(
752 r#"
753struct S { a: &'static str, b: i32 }
754
755fn f() {
756 let a = "hello";
757 let b = 1usize;
758 S {
759 $0
760 };
761}
762"#,
763 r#"
764struct S { a: &'static str, b: i32 }
765
766fn f() {
767 let a = "hello";
768 let b = 1usize;
769 S {
770 a,
771 b: 0,
772 };
773}
774"#,
775 );
776 }
777
778 #[test]
779 fn test_fill_struct_fields_shorthand_unifies() {
780 check_fix(
781 r#"
782struct S<T> { a: &'static str, b: T }
783
784fn f() {
785 let a = "hello";
786 let b = 1i32;
787 S {
788 $0
789 };
790}
791"#,
792 r#"
793struct S<T> { a: &'static str, b: T }
794
795fn f() {
796 let a = "hello";
797 let b = 1i32;
798 S {
799 a,
800 b,
801 };
802}
803"#,
804 );
805 }
806
807 #[test]
808 fn test_fill_struct_pat_fields() {
809 check_fix(
810 r#"
811struct S { a: &'static str, b: i32 }
812
813fn f() {
814 let S {
815 $0
816 };
817}
818"#,
819 r#"
820struct S { a: &'static str, b: i32 }
821
822fn f() {
823 let S {
824 a,
825 b,
826 };
827}
828"#,
829 );
830 }
831
832 #[test]
833 fn test_fill_struct_pat_fields_partial() {
834 check_fix(
835 r#"
836struct S { a: &'static str, b: i32 }
837
838fn f() {
839 let S {
840 a,$0
841 };
842}
843"#,
844 r#"
845struct S { a: &'static str, b: i32 }
846
847fn f() {
848 let S {
849 a,
850 b,
851 };
852}
853"#,
854 );
855 }
856
857 #[test]
858 fn import_extern_crate_clash_with_inner_item() {
859 check_diagnostics(
862 r#"
863//- /lib.rs crate:lib deps:jwt
864mod permissions;
865
866use permissions::jwt;
867
868fn f() {
869 fn inner() {}
870 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
871}
872
873//- /permissions.rs
874pub mod jwt {
875 pub struct Claims {}
876}
877
878//- /jwt/lib.rs crate:jwt
879pub struct Claims {
880 field: u8,
881}
882 "#,
883 );
884 }
885
886 #[test]
887 fn test_default_field_values_basic() {
888 check_diagnostics(
890 r#"
891#![feature(default_field_values)]
892struct Struct {
893 a: usize = 0,
894 b: usize,
895}
896
897fn main() {
898 Struct { b: 1, .. };
899}
900"#,
901 );
902 }
903
904 #[test]
905 fn test_default_field_values_missing_field_error() {
906 check_diagnostics(
908 r#"
909#![feature(default_field_values)]
910struct UserInfo {
911 id: i32,
912 age: f32 = 1.0,
913 email: String,
914}
915
916fn main() {
917 UserInfo { id: 20, .. };
918// ^^^^^^^^💡 error: missing structure fields:
919// |- email
920}
921"#,
922 );
923 }
924
925 #[test]
926 fn test_default_field_values_requires_spread_syntax() {
927 check_diagnostics(
929 r#"
930#![feature(default_field_values)]
931struct Point {
932 x: i32 = 0,
933 y: i32 = 0,
934}
935
936fn main() {
937 Point { x: 0 };
938// ^^^^^💡 error: missing structure fields:
939// |- y
940}
941"#,
942 );
943 }
944
945 #[test]
946 fn test_default_field_values_pattern_matching() {
947 check_diagnostics(
948 r#"
949#![feature(default_field_values)]
950struct Point {
951 x: i32 = 0,
952 y: i32 = 0,
953 z: i32,
954}
955
956fn main() {
957 let Point { x, .. } = Point { z: 5, .. };
958}
959"#,
960 );
961 }
962
963 #[test]
964 fn coerce_existing_local() {
965 check_fix(
966 r#"
967struct A {
968 v: f64,
969}
970
971fn f() -> A {
972 let v = loop {};
973 A {$0}
974}
975 "#,
976 r#"
977struct A {
978 v: f64,
979}
980
981fn f() -> A {
982 let v = loop {};
983 A { v }
984}
985 "#,
986 );
987 }
988
989 #[test]
990 fn inaccessible_fields() {
991 check_no_fix(
992 r#"
993mod foo {
994 pub struct Bar { baz: i32 }
995}
996
997fn qux() {
998 foo::Bar {$0};
999}
1000 "#,
1001 );
1002 }
1003}