@@ -4,6 +4,7 @@ use rustc_hir::def::Res;
4
4
use rustc_data_structures:: fx:: FxHashMap ;
5
5
use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
6
6
use rustc_span:: symbol:: { Ident , Symbol } ;
7
+ use rustc_middle:: ty:: { Adt , TyS } ;
7
8
8
9
use rustc_lint:: { LateContext , LateLintPass } ;
9
10
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -44,11 +45,11 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
44
45
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
45
46
// `default` method of the `Default` trait. and store statement index in current block being
46
47
// checked and the name of the bound variable
47
- let binding_statements_using_default: Vec < ( usize , Symbol ) > = enumerate_bindings_using_default ( cx, block) ;
48
+ let binding_statements_using_default: Vec < ( usize , Symbol , & TyS < ' _ > ) > = enumerate_bindings_using_default ( cx, block) ;
48
49
49
50
// start from the `let mut _ = _::default();` and look at all the following
50
51
// statements, see if they re-assign the fields of the binding
51
- for ( stmt_idx, binding_name) in binding_statements_using_default {
52
+ for ( stmt_idx, binding_name, binding_type ) in binding_statements_using_default {
52
53
// last statement of block cannot trigger the lint
53
54
if stmt_idx == block. stmts . len ( ) - 1 {
54
55
break ;
@@ -71,9 +72,9 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
71
72
if let Some ( ( field_ident, assign_rhs) ) = field_reassigned_by_stmt ( consequtive_statement, & binding_name) {
72
73
// extract and store the assigned value for help message
73
74
let value_snippet = snippet ( cx, assign_rhs. span , ".." ) ;
74
- if !assigned_fields . contains_key ( & field_ident . name ) {
75
- assigned_fields . insert ( field_ident . name , value_snippet ) ;
76
- }
75
+
76
+ // always re-insert set value, this way the latest value is stored for output snippet
77
+ assigned_fields . insert ( field_ident . name , value_snippet ) ;
77
78
78
79
// also set first instance of error for help message
79
80
if first_assign. is_none ( ) {
@@ -88,8 +89,13 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
88
89
if !assigned_fields. is_empty ( ) {
89
90
// take the original assignment as span
90
91
let stmt = & block. stmts [ stmt_idx] ;
92
+
91
93
if let StmtKind :: Local ( preceding_local) = & stmt. kind {
92
94
if let Some ( ty) = & preceding_local. ty {
95
+
96
+ // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
97
+ let ext_with_default = !fields_of_type ( & binding_type) . iter ( ) . all ( |field| assigned_fields. contains_key ( & field. name ) ) ;
98
+
93
99
let ty_snippet = snippet ( cx, ty. span , "_" ) ;
94
100
95
101
let field_list = assigned_fields
@@ -98,7 +104,11 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
98
104
. collect :: < Vec < String > > ( )
99
105
. join ( ", " ) ;
100
106
101
- let sugg = format ! ( "{} {{ {}, ..Default::default() }}" , ty_snippet, field_list) ;
107
+ let sugg = if ext_with_default {
108
+ format ! ( "{} {{ {}, ..Default::default() }}" , ty_snippet, field_list)
109
+ } else {
110
+ format ! ( "{} {{ {} }}" , ty_snippet, field_list)
111
+ } ;
102
112
103
113
// span lint once per statement that binds default
104
114
span_lint_and_note (
@@ -109,14 +119,16 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
109
119
Some ( preceding_local. span ) ,
110
120
& format ! ( "consider initializing the variable with `{}`" , sugg) ,
111
121
) ;
122
+
112
123
}
113
124
}
114
125
}
115
126
}
116
127
}
117
128
}
118
129
119
- fn enumerate_bindings_using_default ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) -> Vec < ( usize , Symbol ) > {
130
+ /// Returns the indices, identifiers and types of bindings set as `Default::default()`.
131
+ fn enumerate_bindings_using_default < ' cx , ' hir > ( cx : & LateContext < ' cx > , block : & Block < ' hir > ) -> Vec < ( usize , Symbol , & ' cx TyS < ' cx > ) > {
120
132
block
121
133
. stmts
122
134
. iter ( )
@@ -135,7 +147,9 @@ fn enumerate_bindings_using_default(cx: &LateContext<'_>, block: &Block<'_>) ->
135
147
// right hand side of assignment is `Default::default`
136
148
if match_def_path( cx, def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
137
149
then {
138
- Some ( ( idx, ident. name) )
150
+ // Get the type of the pattern
151
+ let ty = cx. typeck_results( ) . pat_ty( local. pat) ;
152
+ Some ( ( idx, ident. name, ty) )
139
153
}
140
154
else {
141
155
None
@@ -173,3 +187,15 @@ fn field_reassigned_by_stmt<'hir>(this: &Stmt<'hir>, binding_name: &Symbol) -> O
173
187
}
174
188
}
175
189
}
190
+
191
+ /// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
192
+ fn fields_of_type < ' a > ( ty : & ' a TyS < ' _ > ) -> Vec < Ident > {
193
+ if let Adt ( adt, _) = ty. kind ( ) {
194
+ if adt. is_struct ( ) {
195
+ // unwrap is safe, because this is a struct and structs have only one variant
196
+ let variant = & adt. variants . get ( 0usize . into ( ) ) . unwrap ( ) ;
197
+ return variant. fields . iter ( ) . map ( |f| f. ident ) . collect ( ) ;
198
+ }
199
+ }
200
+ vec ! [ ]
201
+ }
0 commit comments