@@ -40,7 +40,7 @@ declare_clippy_lint! {
40
40
declare_lint_pass ! ( FieldReassignWithDefault => [ FIELD_REASSIGN_WITH_DEFAULT ] ) ;
41
41
42
42
impl LateLintPass < ' _ > for FieldReassignWithDefault {
43
- fn check_block ( & mut self , cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
43
+ fn check_block < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
44
44
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
45
45
// `default` method of the `Default` trait, and store statement index in current block being
46
46
// checked and the name of the bound variable
@@ -68,11 +68,8 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
68
68
else if let Some ( ( field_ident, assign_rhs) ) =
69
69
field_reassigned_by_stmt ( consecutive_statement, binding_name)
70
70
{
71
- // extract and store the assigned value for help message
72
- let value_snippet = snippet ( cx, assign_rhs. span , ".." ) ;
73
-
74
71
// always re-insert set value, this way the latest value is stored for output snippet
75
- assigned_fields. insert ( field_ident. name , value_snippet ) ;
72
+ assigned_fields. insert ( field_ident. name , assign_rhs ) ;
76
73
77
74
// also set first instance of error for help message
78
75
if first_assign. is_none ( ) {
@@ -92,19 +89,33 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
92
89
let stmt = & block. stmts [ stmt_idx] ;
93
90
94
91
if let StmtKind :: Local ( preceding_local) = & stmt. kind {
92
+ // filter out fields like `= Default::default()`, because the FRU already covers them
93
+ let assigned_fields = assigned_fields
94
+ . into_iter ( )
95
+ . filter ( |( _, rhs) | !is_expr_default ( rhs, cx) )
96
+ . collect :: < FxHashMap < Symbol , & Expr < ' _ > > > ( ) ;
97
+
95
98
// if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
96
99
let ext_with_default = !fields_of_type ( binding_type)
97
100
. iter ( )
98
101
. all ( |field| assigned_fields. contains_key ( & field. name ) ) ;
99
102
100
103
let field_list = assigned_fields
101
104
. into_iter ( )
102
- . map ( |( field, value) | format ! ( "{}: {}" , field, value) )
105
+ . map ( |( field, rhs) | {
106
+ // extract and store the assigned value for help message
107
+ let value_snippet = snippet ( cx, rhs. span , ".." ) ;
108
+ format ! ( "{}: {}" , field, value_snippet)
109
+ } )
103
110
. collect :: < Vec < String > > ( )
104
111
. join ( ", " ) ;
105
112
106
113
let sugg = if ext_with_default {
107
- format ! ( "{} {{ {}, ..Default::default() }}" , binding_type, field_list)
114
+ if field_list. is_empty ( ) {
115
+ format ! ( "{}::default()" , binding_type)
116
+ } else {
117
+ format ! ( "{} {{ {}, ..Default::default() }}" , binding_type, field_list)
118
+ }
108
119
} else {
109
120
format ! ( "{} {{ {} }}" , binding_type, field_list)
110
121
} ;
@@ -124,9 +135,28 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
124
135
}
125
136
}
126
137
138
+ /// Checks if the given expression is the `default` method belonging to the `Default` trait.
139
+ fn is_expr_default < ' tcx > ( expr : & ' tcx Expr < ' tcx > , cx : & LateContext < ' tcx > ) -> bool {
140
+ if_chain ! {
141
+ if let ExprKind :: Call ( ref fn_expr, _) = & expr. kind;
142
+ if let ExprKind :: Path ( qpath) = & fn_expr. kind;
143
+ if let Res :: Def ( _, def_id) = qpath_res( cx, qpath, fn_expr. hir_id) ;
144
+ // right hand side of assignment is `Default::default`
145
+ if match_def_path( cx, def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
146
+ then {
147
+ true
148
+ } else {
149
+ false
150
+ }
151
+ }
152
+ }
153
+
127
154
/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
128
155
/// for when the pattern type is a tuple.
129
- fn enumerate_bindings_using_default < ' tcx > ( cx : & LateContext < ' tcx > , block : & Block < ' _ > ) -> Vec < ( usize , Symbol , Ty < ' tcx > ) > {
156
+ fn enumerate_bindings_using_default < ' tcx > (
157
+ cx : & LateContext < ' tcx > ,
158
+ block : & Block < ' tcx > ,
159
+ ) -> Vec < ( usize , Symbol , Ty < ' tcx > ) > {
130
160
block
131
161
. stmts
132
162
. iter ( )
@@ -142,11 +172,7 @@ fn enumerate_bindings_using_default<'tcx>(cx: &LateContext<'tcx>, block: &Block<
142
172
if !matches!( ty. kind( ) , ty:: Tuple ( _) ) ;
143
173
// only when assigning `... = Default::default()`
144
174
if let Some ( ref expr) = local. init;
145
- if let ExprKind :: Call ( ref fn_expr, _) = & expr. kind;
146
- if let ExprKind :: Path ( qpath) = & fn_expr. kind;
147
- if let Res :: Def ( _, def_id) = qpath_res( cx, qpath, fn_expr. hir_id) ;
148
- // right hand side of assignment is `Default::default`
149
- if match_def_path( cx, def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
175
+ if is_expr_default( expr, cx) ;
150
176
then {
151
177
Some ( ( idx, ident. name, ty) )
152
178
} else {
0 commit comments