Skip to main content

ide_diagnostics/handlers/
missing_fields.rs

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
26// Diagnostic: missing-fields
27//
28// This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
29//
30// Example:
31//
32// ```rust
33// struct A { a: u8, b: u8 }
34//
35// let a = A { a: 10 };
36// ```
37pub(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    // Note that although we could add a diagnostics to
60    // fill the missing tuple field, e.g :
61    // `struct A(usize);`
62    // `let a = A { 0: () }`
63    // but it is uncommon usage and it should not be encouraged.
64    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                // we can't map the diff up into the macro input unfortunately, as the macro loses all
90                // whitespace information so the diff wouldn't be applicable no matter what
91                // This has the downside that the cursor will be moved in macros by doing it without a diff
92                // but that is a trade off we can make.
93                // FIXME: this also currently discards a lot of whitespace in the input... we really need a formatter here
94                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    // Look for a ::new() associated function
250    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        // make sure the assist doesn't fill non Unit variants
470        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        // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
860
861        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        // This should work without errors - only field 'b' is required
889        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        // This should report a missing field error because email is required
907        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        // without `..` should report missing fields
928        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}