3 db
::{AstDatabase, HirDatabase}
,
4 known
, AssocItem
, HirDisplay
, InFile
, Type
,
7 assists
::Assist
, famous_defs
::FamousDefs
, imports
::import_assets
::item_for_path_search
,
8 source_change
::SourceChange
, use_trivial_contructor
::use_trivial_constructor
, FxHashMap
,
14 AstNode
, SyntaxNode
, SyntaxNodePtr
,
16 use text_edit
::TextEdit
;
18 use crate::{fix, Diagnostic, DiagnosticsContext}
;
20 // Diagnostic: missing-fields
22 // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure.
27 // struct A { a: u8, b: u8 }
29 // let a = A { a: 10 };
31 pub(crate) fn missing_fields(ctx
: &DiagnosticsContext
<'_
>, d
: &hir
::MissingFields
) -> Diagnostic
{
32 let mut message
= String
::from("missing structure fields:\n");
33 for field
in &d
.missed_fields
{
34 format_to
!(message
, "- {}\n", field
);
37 let ptr
= InFile
::new(
39 d
.field_list_parent_path
41 .map(SyntaxNodePtr
::from
)
42 .unwrap_or_else(|| d
.field_list_parent
.clone().either(|it
| it
.into(), |it
| it
.into())),
45 Diagnostic
::new("missing-fields", message
, ctx
.sema
.diagnostics_display_range(ptr
).range
)
46 .with_fixes(fixes(ctx
, d
))
49 fn fixes(ctx
: &DiagnosticsContext
<'_
>, d
: &hir
::MissingFields
) -> Option
<Vec
<Assist
>> {
50 // Note that although we could add a diagnostics to
51 // fill the missing tuple field, e.g :
53 // `let a = A { 0: () }`
54 // but it is uncommon usage and it should not be encouraged.
55 if d
.missed_fields
.iter().any(|it
| it
.as_tuple_index().is_some()) {
59 let root
= ctx
.sema
.db
.parse_or_expand(d
.file
)?
;
61 let current_module
= match &d
.field_list_parent
{
62 Either
::Left(ptr
) => ctx
.sema
.scope(ptr
.to_node(&root
).syntax()).map(|it
| it
.module()),
63 Either
::Right(ptr
) => ctx
.sema
.scope(ptr
.to_node(&root
).syntax()).map(|it
| it
.module()),
66 let build_text_edit
= |parent_syntax
, new_syntax
: &SyntaxNode
, old_syntax
| {
68 let mut builder
= TextEdit
::builder();
69 if d
.file
.is_macro() {
70 // we can't map the diff up into the macro input unfortunately, as the macro loses all
71 // whitespace information so the diff wouldn't be applicable no matter what
72 // This has the downside that the cursor will be moved in macros by doing it without a diff
73 // but that is a trade off we can make.
74 // FIXME: this also currently discards a lot of whitespace in the input... we really need a formatter here
75 let range
= ctx
.sema
.original_range_opt(old_syntax
)?
;
76 builder
.replace(range
.range
, new_syntax
.to_string());
78 algo
::diff(old_syntax
, new_syntax
).into_text_edit(&mut builder
);
83 "fill_missing_fields",
85 SourceChange
::from_text_edit(d
.file
.original_file(ctx
.sema
.db
), edit
),
86 ctx
.sema
.original_range(parent_syntax
).range
,
90 match &d
.field_list_parent
{
91 Either
::Left(record_expr
) => {
92 let field_list_parent
= record_expr
.to_node(&root
);
93 let missing_fields
= ctx
.sema
.record_literal_missing_fields(&field_list_parent
);
95 let mut locals
= FxHashMap
::default();
96 ctx
.sema
.scope(field_list_parent
.syntax())?
.process_all_names(&mut |name
, def
| {
97 if let hir
::ScopeDef
::Local(local
) = def
{
98 locals
.insert(name
, local
);
102 let generate_fill_expr
= |ty
: &Type
| match ctx
.config
.expr_fill_default
{
103 crate::ExprFillDefaultMode
::Todo
=> make
::ext
::expr_todo(),
104 crate::ExprFillDefaultMode
::Default
=> {
105 get_default_constructor(ctx
, d
, ty
).unwrap_or_else(|| make
::ext
::expr_todo())
109 let old_field_list
= field_list_parent
.record_expr_field_list()?
;
110 let new_field_list
= old_field_list
.clone_for_update();
111 for (f
, ty
) in missing_fields
.iter() {
112 let field_expr
= if let Some(local_candidate
) = locals
.get(&f
.name(ctx
.sema
.db
)) {
113 cov_mark
::hit
!(field_shorthand
);
114 let candidate_ty
= local_candidate
.ty(ctx
.sema
.db
);
115 if ty
.could_unify_with(ctx
.sema
.db
, &candidate_ty
) {
118 Some(generate_fill_expr(ty
))
121 let expr
= (|| -> Option
<ast
::Expr
> {
122 let item_in_ns
= hir
::ItemInNs
::from(hir
::ModuleDef
::from(ty
.as_adt()?
));
124 let type_path
= current_module?
.find_use_path(
126 item_for_path_search(ctx
.sema
.db
, item_in_ns
)?
,
127 ctx
.config
.prefer_no_std
,
130 use_trivial_constructor(
132 ide_db
::helpers
::mod_path_to_ast(&type_path
),
140 Some(generate_fill_expr(ty
))
143 let field
= make
::record_expr_field(
144 make
::name_ref(&f
.name(ctx
.sema
.db
).to_smol_str()),
147 new_field_list
.add_field(field
.clone_for_update());
150 field_list_parent
.syntax(),
151 new_field_list
.syntax(),
152 old_field_list
.syntax(),
155 Either
::Right(record_pat
) => {
156 let field_list_parent
= record_pat
.to_node(&root
);
157 let missing_fields
= ctx
.sema
.record_pattern_missing_fields(&field_list_parent
);
159 let old_field_list
= field_list_parent
.record_pat_field_list()?
;
160 let new_field_list
= old_field_list
.clone_for_update();
161 for (f
, _
) in missing_fields
.iter() {
162 let field
= make
::record_pat_field_shorthand(make
::name_ref(
163 &f
.name(ctx
.sema
.db
).to_smol_str(),
165 new_field_list
.add_field(field
.clone_for_update());
168 field_list_parent
.syntax(),
169 new_field_list
.syntax(),
170 old_field_list
.syntax(),
176 fn make_ty(ty
: &hir
::Type
, db
: &dyn HirDatabase
, module
: hir
::Module
) -> ast
::Type
{
177 let ty_str
= match ty
.as_adt() {
178 Some(adt
) => adt
.name(db
).to_string(),
179 None
=> ty
.display_source_code(db
, module
.into()).ok().unwrap_or_else(|| "_".to_string()),
185 fn get_default_constructor(
186 ctx
: &DiagnosticsContext
<'_
>,
187 d
: &hir
::MissingFields
,
189 ) -> Option
<ast
::Expr
> {
190 if let Some(builtin_ty
) = ty
.as_builtin() {
191 if builtin_ty
.is_int() || builtin_ty
.is_uint() {
192 return Some(make
::ext
::zero_number());
194 if builtin_ty
.is_float() {
195 return Some(make
::ext
::zero_float());
197 if builtin_ty
.is_char() {
198 return Some(make
::ext
::empty_char());
200 if builtin_ty
.is_str() {
201 return Some(make
::ext
::empty_str());
203 if builtin_ty
.is_bool() {
204 return Some(make
::ext
::default_bool());
208 let krate
= ctx
.sema
.to_module_def(d
.file
.original_file(ctx
.sema
.db
))?
.krate();
209 let module
= krate
.root_module(ctx
.sema
.db
);
211 // Look for a ::new() associated function
212 let has_new_func
= ty
213 .iterate_assoc_items(ctx
.sema
.db
, krate
, |assoc_item
| {
214 if let AssocItem
::Function(func
) = assoc_item
{
215 if func
.name(ctx
.sema
.db
) == known
::new
216 && func
.assoc_fn_params(ctx
.sema
.db
).is_empty()
226 let famous_defs
= FamousDefs(&ctx
.sema
, krate
);
228 Some(make
::ext
::expr_ty_new(&make_ty(ty
, ctx
.sema
.db
, module
)))
229 } else if ty
.as_adt() == famous_defs
.core_option_Option()?
.ty(ctx
.sema
.db
).as_adt() {
230 Some(make
::ext
::option_none())
231 } else if !ty
.is_array()
232 && ty
.impls_trait(ctx
.sema
.db
, famous_defs
.core_default_Default()?
, &[])
234 Some(make
::ext
::expr_ty_default(&make_ty(ty
, ctx
.sema
.db
, module
)))
242 use crate::tests
::{check_diagnostics, check_fix}
;
245 fn missing_record_pat_field_diagnostic() {
248 struct S { foo: i32, bar: () }
250 let S { foo: _ } = s;
251 //^ 💡 error: missing structure fields:
259 fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
262 struct S { foo: i32, bar: () }
263 fn baz(s: S) -> i32 {
265 S { foo, .. } => foo,
273 fn missing_record_pat_field_box() {
276 struct S { s: Box<u32> }
285 fn missing_record_pat_field_ref() {
297 fn missing_record_expr_in_assignee_expr() {
300 struct S { s: usize, t: usize }
301 struct S2 { s: S, t: () }
309 S2 { s: S { s, .. }, .. } = a;
311 fn in_tuple(a: (S,)) {
315 fn in_array(a: [S;1]) {
319 fn in_tuple_struct(a: T) {
328 fn range_mapping_out_of_macros() {
335 macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
338 let _x = id![Foo { a: $042 }];
341 pub struct Foo { pub a: i32, pub b: i32 }
348 macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
351 let _x = id![Foo {a:42, b: 0 }];
354 pub struct Foo { pub a: i32, pub b: i32 }
360 fn test_fill_struct_fields_empty() {
364 struct TestStruct { one: i32, two: i64, three: Option<i32>, four: bool }
367 let s = TestStruct {$0};
371 struct TestStruct { one: i32, two: i64, three: Option<i32>, four: bool }
374 let s = TestStruct { one: 0, two: 0, three: None, four: false };
381 fn test_fill_struct_zst_fields() {
386 struct TestStruct { one: i32, two: Empty }
389 let s = TestStruct {$0};
395 struct TestStruct { one: i32, two: Empty }
398 let s = TestStruct { one: 0, two: Empty };
406 struct TestStruct { one: i32, two: Empty }
409 let s = TestStruct {$0};
415 struct TestStruct { one: i32, two: Empty }
418 let s = TestStruct { one: 0, two: Empty::Foo };
423 // make sure the assist doesn't fill non Unit variants
428 struct TestStruct { one: i32, two: Empty }
431 let s = TestStruct {$0};
437 struct TestStruct { one: i32, two: Empty }
440 let s = TestStruct { one: 0, two: todo!() };
446 enum Empty { Foo {} };
448 struct TestStruct { one: i32, two: Empty }
451 let s = TestStruct {$0};
455 enum Empty { Foo {} };
457 struct TestStruct { one: i32, two: Empty }
460 let s = TestStruct { one: 0, two: todo!() };
467 fn test_fill_struct_fields_self() {
470 struct TestStruct { one: i32 }
473 fn test_fn() { let s = Self {$0}; }
477 struct TestStruct { one: i32 }
480 fn test_fn() { let s = Self { one: 0 }; }
487 fn test_fill_struct_fields_enum() {
491 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
495 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
502 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
506 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
507 Expr::Bin { lhs, rhs }
515 fn test_fill_struct_fields_partial() {
518 struct TestStruct { one: i32, two: i64 }
521 let s = TestStruct{ two: 2$0 };
525 struct TestStruct { one: i32, two: i64 }
528 let s = TestStruct{ two: 2, one: 0 };
535 fn test_fill_struct_fields_new() {
538 struct TestWithNew(usize);
540 pub fn new() -> Self {
544 struct TestStruct { one: i32, two: TestWithNew }
547 let s = TestStruct{ $0 };
551 struct TestWithNew(usize);
553 pub fn new() -> Self {
557 struct TestStruct { one: i32, two: TestWithNew }
560 let s = TestStruct{ one: 0, two: TestWithNew::new() };
567 fn test_fill_struct_fields_default() {
570 //- minicore: default, option
571 struct TestWithDefault(usize);
572 impl Default for TestWithDefault {
573 pub fn default() -> Self {
577 struct TestStruct { one: i32, two: TestWithDefault }
580 let s = TestStruct{ $0 };
584 struct TestWithDefault(usize);
585 impl Default for TestWithDefault {
586 pub fn default() -> Self {
590 struct TestStruct { one: i32, two: TestWithDefault }
593 let s = TestStruct{ one: 0, two: TestWithDefault::default() };
600 fn test_fill_struct_fields_raw_ident() {
603 struct TestStruct { r#type: u8 }
610 struct TestStruct { r#type: u8 }
613 TestStruct { r#type: 0 };
620 fn test_fill_struct_fields_no_diagnostic() {
623 struct TestStruct { one: i32, two: i64 }
627 let s = TestStruct{ one, two: 2 };
634 fn test_fill_struct_fields_no_diagnostic_on_spread() {
637 struct TestStruct { one: i32, two: i64 }
641 let s = TestStruct{ ..a };
648 fn test_fill_struct_fields_blank_line() {
651 struct S { a: (), b: () }
660 struct S { a: (), b: () }
673 fn test_fill_struct_fields_shorthand() {
674 cov_mark
::check
!(field_shorthand
);
677 struct S { a: &'static str, b: i32 }
688 struct S { a: &'static str, b: i32 }
703 fn test_fill_struct_fields_shorthand_ty_mismatch() {
706 struct S { a: &'static str, b: i32 }
717 struct S { a: &'static str, b: i32 }
732 fn test_fill_struct_fields_shorthand_unifies() {
735 struct S<T> { a: &'static str, b: T }
746 struct S<T> { a: &'static str, b: T }
761 fn test_fill_struct_pat_fields() {
764 struct S { a: &'static str, b: i32 }
773 struct S { a: &'static str, b: i32 }
786 fn test_fill_struct_pat_fields_partial() {
789 struct S { a: &'static str, b: i32 }
798 struct S { a: &'static str, b: i32 }
811 fn import_extern_crate_clash_with_inner_item() {
812 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
816 //- /lib.rs crate:lib deps:jwt
819 use permissions::jwt;
823 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
831 //- /jwt/lib.rs crate:jwt