@@ -9,7 +9,7 @@ use stdx::format_to;
9
9
use syntax:: {
10
10
algo,
11
11
ast:: { self , make} ,
12
- AstNode , SyntaxNodePtr ,
12
+ AstNode , SyntaxNode , SyntaxNodePtr ,
13
13
} ;
14
14
use text_edit:: TextEdit ;
15
15
@@ -55,70 +55,95 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
55
55
}
56
56
57
57
let root = ctx. sema . db . parse_or_expand ( d. file ) ?;
58
- let field_list_parent = match & d. field_list_parent {
59
- Either :: Left ( record_expr) => record_expr. to_node ( & root) ,
60
- // FIXE: patterns should be fixable as well.
61
- Either :: Right ( _) => return None ,
62
- } ;
63
- let old_field_list = field_list_parent. record_expr_field_list ( ) ?;
64
58
65
- let new_field_list = old_field_list. clone_for_update ( ) ;
66
- let mut locals = FxHashMap :: default ( ) ;
67
- ctx. sema . scope ( field_list_parent. syntax ( ) ) ?. process_all_names ( & mut |name, def| {
68
- if let hir:: ScopeDef :: Local ( local) = def {
69
- locals. insert ( name, local) ;
70
- }
71
- } ) ;
72
- let missing_fields = ctx. sema . record_literal_missing_fields ( & field_list_parent) ;
73
-
74
- let generate_fill_expr = |ty : & Type | match ctx. config . expr_fill_default {
75
- crate :: ExprFillDefaultMode :: Todo => Some ( make:: ext:: expr_todo ( ) ) ,
76
- crate :: ExprFillDefaultMode :: Default => {
77
- let default_constr = get_default_constructor ( ctx, d, ty) ;
78
- match default_constr {
79
- Some ( default_constr) => Some ( default_constr) ,
80
- _ => Some ( make:: ext:: expr_todo ( ) ) ,
59
+ let build_text_edit = |parent_syntax, new_syntax : & SyntaxNode , old_syntax| {
60
+ let edit = {
61
+ let mut builder = TextEdit :: builder ( ) ;
62
+ if d. file . is_macro ( ) {
63
+ // we can't map the diff up into the macro input unfortunately, as the macro loses all
64
+ // whitespace information so the diff wouldn't be applicable no matter what
65
+ // This has the downside that the cursor will be moved in macros by doing it without a diff
66
+ // but that is a trade off we can make.
67
+ // FIXME: this also currently discards a lot of whitespace in the input... we really need a formatter here
68
+ let range = ctx. sema . original_range_opt ( old_syntax) ?;
69
+ builder. replace ( range. range , new_syntax. to_string ( ) ) ;
70
+ } else {
71
+ algo:: diff ( old_syntax, new_syntax) . into_text_edit ( & mut builder) ;
81
72
}
82
- }
73
+ builder. finish ( )
74
+ } ;
75
+ Some ( vec ! [ fix(
76
+ "fill_missing_fields" ,
77
+ "Fill struct fields" ,
78
+ SourceChange :: from_text_edit( d. file. original_file( ctx. sema. db) , edit) ,
79
+ ctx. sema. original_range( parent_syntax) . range,
80
+ ) ] )
83
81
} ;
84
82
85
- for ( f, ty) in missing_fields. iter ( ) {
86
- let field_expr = if let Some ( local_candidate) = locals. get ( & f. name ( ctx. sema . db ) ) {
87
- cov_mark:: hit!( field_shorthand) ;
88
- let candidate_ty = local_candidate. ty ( ctx. sema . db ) ;
89
- if ty. could_unify_with ( ctx. sema . db , & candidate_ty) {
90
- None
91
- } else {
92
- generate_fill_expr ( ty)
83
+ match & d. field_list_parent {
84
+ Either :: Left ( record_expr) => {
85
+ let field_list_parent = record_expr. to_node ( & root) ;
86
+ let missing_fields = ctx. sema . record_literal_missing_fields ( & field_list_parent) ;
87
+
88
+ let mut locals = FxHashMap :: default ( ) ;
89
+ ctx. sema . scope ( field_list_parent. syntax ( ) ) ?. process_all_names ( & mut |name, def| {
90
+ if let hir:: ScopeDef :: Local ( local) = def {
91
+ locals. insert ( name, local) ;
92
+ }
93
+ } ) ;
94
+
95
+ let generate_fill_expr = |ty : & Type | match ctx. config . expr_fill_default {
96
+ crate :: ExprFillDefaultMode :: Todo => make:: ext:: expr_todo ( ) ,
97
+ crate :: ExprFillDefaultMode :: Default => {
98
+ get_default_constructor ( ctx, d, ty) . unwrap_or_else ( || make:: ext:: expr_todo ( ) )
99
+ }
100
+ } ;
101
+
102
+ let old_field_list = field_list_parent. record_expr_field_list ( ) ?;
103
+ let new_field_list = old_field_list. clone_for_update ( ) ;
104
+ for ( f, ty) in missing_fields. iter ( ) {
105
+ let field_expr = if let Some ( local_candidate) = locals. get ( & f. name ( ctx. sema . db ) ) {
106
+ cov_mark:: hit!( field_shorthand) ;
107
+ let candidate_ty = local_candidate. ty ( ctx. sema . db ) ;
108
+ if ty. could_unify_with ( ctx. sema . db , & candidate_ty) {
109
+ None
110
+ } else {
111
+ Some ( generate_fill_expr ( ty) )
112
+ }
113
+ } else {
114
+ Some ( generate_fill_expr ( ty) )
115
+ } ;
116
+ let field = make:: record_expr_field (
117
+ make:: name_ref ( & f. name ( ctx. sema . db ) . to_smol_str ( ) ) ,
118
+ field_expr,
119
+ ) ;
120
+ new_field_list. add_field ( field. clone_for_update ( ) ) ;
93
121
}
94
- } else {
95
- generate_fill_expr ( ty)
96
- } ;
97
- let field =
98
- make:: record_expr_field ( make:: name_ref ( & f. name ( ctx. sema . db ) . to_smol_str ( ) ) , field_expr)
99
- . clone_for_update ( ) ;
100
- new_field_list. add_field ( field) ;
101
- }
102
-
103
- let mut builder = TextEdit :: builder ( ) ;
104
- if d. file . is_macro ( ) {
105
- // we can't map the diff up into the macro input unfortunately, as the macro loses all
106
- // whitespace information so the diff wouldn't be applicable no matter what
107
- // This has the downside that the cursor will be moved in macros by doing it without a diff
108
- // but that is a trade off we can make.
109
- // FIXE: this also currently discards a lot of whitespace in the input... we really need a formatter here
110
- let range = ctx. sema . original_range_opt ( old_field_list. syntax ( ) ) ?;
111
- builder. replace ( range. range , new_field_list. to_string ( ) ) ;
112
- } else {
113
- algo:: diff ( old_field_list. syntax ( ) , new_field_list. syntax ( ) ) . into_text_edit ( & mut builder) ;
122
+ build_text_edit (
123
+ field_list_parent. syntax ( ) ,
124
+ new_field_list. syntax ( ) ,
125
+ old_field_list. syntax ( ) ,
126
+ )
127
+ }
128
+ Either :: Right ( record_pat) => {
129
+ let field_list_parent = record_pat. to_node ( & root) ;
130
+ let missing_fields = ctx. sema . record_pattern_missing_fields ( & field_list_parent) ;
131
+
132
+ let old_field_list = field_list_parent. record_pat_field_list ( ) ?;
133
+ let new_field_list = old_field_list. clone_for_update ( ) ;
134
+ for ( f, _) in missing_fields. iter ( ) {
135
+ let field = make:: record_pat_field_shorthand ( make:: name_ref (
136
+ & f. name ( ctx. sema . db ) . to_smol_str ( ) ,
137
+ ) ) ;
138
+ new_field_list. add_field ( field. clone_for_update ( ) ) ;
139
+ }
140
+ build_text_edit (
141
+ field_list_parent. syntax ( ) ,
142
+ new_field_list. syntax ( ) ,
143
+ old_field_list. syntax ( ) ,
144
+ )
145
+ }
114
146
}
115
- let edit = builder. finish ( ) ;
116
- Some ( vec ! [ fix(
117
- "fill_missing_fields" ,
118
- "Fill struct fields" ,
119
- SourceChange :: from_text_edit( d. file. original_file( ctx. sema. db) , edit) ,
120
- ctx. sema. original_range( field_list_parent. syntax( ) ) . range,
121
- ) ] )
122
147
}
123
148
124
149
fn make_ty ( ty : & hir:: Type , db : & dyn HirDatabase , module : hir:: Module ) -> ast:: Type {
@@ -190,7 +215,7 @@ mod tests {
190
215
struct S { foo: i32, bar: () }
191
216
fn baz(s: S) {
192
217
let S { foo: _ } = s;
193
- //^ error: missing structure fields:
218
+ //^ 💡 error: missing structure fields:
194
219
//| - bar
195
220
}
196
221
"# ,
@@ -581,6 +606,56 @@ fn f() {
581
606
) ;
582
607
}
583
608
609
+ #[ test]
610
+ fn test_fill_struct_pat_fields ( ) {
611
+ check_fix (
612
+ r#"
613
+ struct S { a: &'static str, b: i32 }
614
+
615
+ fn f() {
616
+ let S {
617
+ $0
618
+ };
619
+ }
620
+ "# ,
621
+ r#"
622
+ struct S { a: &'static str, b: i32 }
623
+
624
+ fn f() {
625
+ let S {
626
+ a,
627
+ b,
628
+ };
629
+ }
630
+ "# ,
631
+ ) ;
632
+ }
633
+
634
+ #[ test]
635
+ fn test_fill_struct_pat_fields_partial ( ) {
636
+ check_fix (
637
+ r#"
638
+ struct S { a: &'static str, b: i32 }
639
+
640
+ fn f() {
641
+ let S {
642
+ a,$0
643
+ };
644
+ }
645
+ "# ,
646
+ r#"
647
+ struct S { a: &'static str, b: i32 }
648
+
649
+ fn f() {
650
+ let S {
651
+ a,
652
+ b,
653
+ };
654
+ }
655
+ "# ,
656
+ ) ;
657
+ }
658
+
584
659
#[ test]
585
660
fn import_extern_crate_clash_with_inner_item ( ) {
586
661
// This is more of a resolver test, but doesn't really work with the hir_def testsuite.
0 commit comments