@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
6
6
use rustc_hir:: def:: Res ;
7
7
use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
8
8
use rustc_lint:: { LateContext , LateLintPass } ;
9
- use rustc_middle:: ty:: { self , Adt , Ty } ;
9
+ use rustc_middle:: ty;
10
10
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
11
11
use rustc_span:: symbol:: { Ident , Symbol } ;
12
12
use rustc_span:: Span ;
@@ -103,18 +103,41 @@ impl LateLintPass<'_> for Default {
103
103
}
104
104
105
105
fn check_block < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
106
- // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
107
- // `default` method of the `Default` trait, and store statement index in current block being
108
- // checked and the name of the bound variable
109
- let binding_statements_using_default = enumerate_bindings_using_default ( cx, block) ;
110
-
111
106
// start from the `let mut _ = _::default();` and look at all the following
112
107
// statements, see if they re-assign the fields of the binding
113
- for ( stmt_idx, binding_name, binding_type, span) in binding_statements_using_default {
114
- // the last statement of a block cannot trigger the lint
115
- if stmt_idx == block. stmts . len ( ) - 1 {
116
- break ;
117
- }
108
+ let stmts_head = match block. stmts {
109
+ // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
110
+ [ head @ .., _] if !head. is_empty ( ) => head,
111
+ _ => return ,
112
+ } ;
113
+ for ( stmt_idx, stmt) in stmts_head. iter ( ) . enumerate ( ) {
114
+ // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
115
+ // `default` method of the `Default` trait, and store statement index in current block being
116
+ // checked and the name of the bound variable
117
+ let ( local, variant, binding_name, binding_type, span) = if_chain ! {
118
+ // only take `let ...` statements
119
+ if let StmtKind :: Local ( local) = stmt. kind;
120
+ if let Some ( expr) = local. init;
121
+ // only take bindings to identifiers
122
+ if let PatKind :: Binding ( _, binding_id, ident, _) = local. pat. kind;
123
+ // only when assigning `... = Default::default()`
124
+ if is_expr_default( expr, cx) ;
125
+ let binding_type = cx. typeck_results( ) . node_type( binding_id) ;
126
+ if let Some ( adt) = binding_type. ty_adt_def( ) ;
127
+ if adt. is_struct( ) ;
128
+ let variant = adt. non_enum_variant( ) ;
129
+ if adt. did. is_local( ) || !variant. is_field_list_non_exhaustive( ) ;
130
+ let module_did = cx. tcx. parent_module( stmt. hir_id) . to_def_id( ) ;
131
+ if variant
132
+ . fields
133
+ . iter( )
134
+ . all( |field| field. vis. is_accessible_from( module_did, cx. tcx) ) ;
135
+ then {
136
+ ( local, variant, ident. name, binding_type, expr. span)
137
+ } else {
138
+ continue ;
139
+ }
140
+ } ;
118
141
119
142
// find all "later statement"'s where the fields of the binding set as
120
143
// Default::default() get reassigned, unless the reassignment refers to the original binding
@@ -154,49 +177,45 @@ impl LateLintPass<'_> for Default {
154
177
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
155
178
// construction using `Ty { fields, ..Default::default() }`
156
179
if !assigned_fields. is_empty ( ) && !cancel_lint {
157
- // take the original assignment as span
158
- let stmt = & block. stmts [ stmt_idx] ;
159
-
160
- if let StmtKind :: Local ( preceding_local) = & stmt. kind {
161
- // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
162
- let ext_with_default = !fields_of_type ( binding_type)
163
- . iter ( )
164
- . all ( |field| assigned_fields. iter ( ) . any ( |( a, _) | a == & field. name ) ) ;
180
+ // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
181
+ let ext_with_default = !variant
182
+ . fields
183
+ . iter ( )
184
+ . all ( |field| assigned_fields. iter ( ) . any ( |( a, _) | a == & field. ident . name ) ) ;
165
185
166
- let field_list = assigned_fields
167
- . into_iter ( )
168
- . map ( |( field, rhs) | {
169
- // extract and store the assigned value for help message
170
- let value_snippet = snippet ( cx, rhs. span , ".." ) ;
171
- format ! ( "{}: {}" , field, value_snippet)
172
- } )
173
- . collect :: < Vec < String > > ( )
174
- . join ( ", " ) ;
186
+ let field_list = assigned_fields
187
+ . into_iter ( )
188
+ . map ( |( field, rhs) | {
189
+ // extract and store the assigned value for help message
190
+ let value_snippet = snippet ( cx, rhs. span , ".." ) ;
191
+ format ! ( "{}: {}" , field, value_snippet)
192
+ } )
193
+ . collect :: < Vec < String > > ( )
194
+ . join ( ", " ) ;
175
195
176
- let sugg = if ext_with_default {
177
- if field_list. is_empty ( ) {
178
- format ! ( "{}::default()" , binding_type)
179
- } else {
180
- format ! ( "{} {{ {}, ..Default::default() }}" , binding_type, field_list)
181
- }
196
+ let sugg = if ext_with_default {
197
+ if field_list. is_empty ( ) {
198
+ format ! ( "{}::default()" , binding_type)
182
199
} else {
183
- format ! ( "{} {{ {} }}" , binding_type, field_list)
184
- } ;
200
+ format ! ( "{} {{ {}, ..Default::default() }}" , binding_type, field_list)
201
+ }
202
+ } else {
203
+ format ! ( "{} {{ {} }}" , binding_type, field_list)
204
+ } ;
185
205
186
- // span lint once per statement that binds default
187
- span_lint_and_note (
188
- cx,
189
- FIELD_REASSIGN_WITH_DEFAULT ,
190
- first_assign. unwrap ( ) . span ,
191
- "field assignment outside of initializer for an instance created with Default::default()" ,
192
- Some ( preceding_local. span ) ,
193
- & format ! (
194
- "consider initializing the variable with `{}` and removing relevant reassignments" ,
195
- sugg
196
- ) ,
197
- ) ;
198
- self . reassigned_linted . insert ( span) ;
199
- }
206
+ // span lint once per statement that binds default
207
+ span_lint_and_note (
208
+ cx,
209
+ FIELD_REASSIGN_WITH_DEFAULT ,
210
+ first_assign. unwrap ( ) . span ,
211
+ "field assignment outside of initializer for an instance created with Default::default()" ,
212
+ Some ( local. span ) ,
213
+ & format ! (
214
+ "consider initializing the variable with `{}` and removing relevant reassignments" ,
215
+ sugg
216
+ ) ,
217
+ ) ;
218
+ self . reassigned_linted . insert ( span) ;
200
219
}
201
220
}
202
221
}
@@ -217,38 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
217
236
}
218
237
}
219
238
220
- /// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
221
- /// for when the pattern type is a tuple.
222
- fn enumerate_bindings_using_default < ' tcx > (
223
- cx : & LateContext < ' tcx > ,
224
- block : & Block < ' tcx > ,
225
- ) -> Vec < ( usize , Symbol , Ty < ' tcx > , Span ) > {
226
- block
227
- . stmts
228
- . iter ( )
229
- . enumerate ( )
230
- . filter_map ( |( idx, stmt) | {
231
- if_chain ! {
232
- // only take `let ...` statements
233
- if let StmtKind :: Local ( ref local) = stmt. kind;
234
- // only take bindings to identifiers
235
- if let PatKind :: Binding ( _, _, ident, _) = local. pat. kind;
236
- // that are not tuples
237
- let ty = cx. typeck_results( ) . pat_ty( local. pat) ;
238
- if !matches!( ty. kind( ) , ty:: Tuple ( _) ) ;
239
- // only when assigning `... = Default::default()`
240
- if let Some ( ref expr) = local. init;
241
- if is_expr_default( expr, cx) ;
242
- then {
243
- Some ( ( idx, ident. name, ty, expr. span) )
244
- } else {
245
- None
246
- }
247
- }
248
- } )
249
- . collect ( )
250
- }
251
-
252
239
/// Returns the reassigned field and the assigning expression (right-hand side of assign).
253
240
fn field_reassigned_by_stmt < ' tcx > ( this : & Stmt < ' tcx > , binding_name : Symbol ) -> Option < ( Ident , & ' tcx Expr < ' tcx > ) > {
254
241
if_chain ! {
@@ -268,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
268
255
}
269
256
}
270
257
}
271
-
272
- /// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
273
- fn fields_of_type ( ty : Ty < ' _ > ) -> Vec < Ident > {
274
- if let Adt ( adt, _) = ty. kind ( ) {
275
- if adt. is_struct ( ) {
276
- let variant = & adt. non_enum_variant ( ) ;
277
- return variant. fields . iter ( ) . map ( |f| f. ident ) . collect ( ) ;
278
- }
279
- }
280
- vec ! [ ]
281
- }
0 commit comments