@@ -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,34 +103,50 @@ 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
121
144
let mut first_assign = None ;
122
145
let mut assigned_fields = Vec :: new ( ) ;
123
146
let mut cancel_lint = false ;
124
147
for consecutive_statement in & block. stmts [ stmt_idx + 1 ..] {
125
- // interrupt if the statement is a let binding (`Local`) that shadows the original
126
- // binding
127
- if stmt_shadows_binding ( consecutive_statement, binding_name) {
128
- break ;
129
- }
130
148
// find out if and which field was set by this `consecutive_statement`
131
- else if let Some ( ( field_ident, assign_rhs) ) =
132
- field_reassigned_by_stmt ( consecutive_statement, binding_name)
133
- {
149
+ if let Some ( ( field_ident, assign_rhs) ) = field_reassigned_by_stmt ( consecutive_statement, binding_name) {
134
150
// interrupt and cancel lint if assign_rhs references the original binding
135
151
if contains_name ( binding_name, assign_rhs) {
136
152
cancel_lint = true ;
@@ -152,7 +168,7 @@ impl LateLintPass<'_> for Default {
152
168
first_assign = Some ( consecutive_statement) ;
153
169
}
154
170
}
155
- // interrupt also if no field was assigned, since we only want to look at consecutive statements
171
+ // interrupt if no field was assigned, since we only want to look at consecutive statements
156
172
else {
157
173
break ;
158
174
}
@@ -161,55 +177,45 @@ impl LateLintPass<'_> for Default {
161
177
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
162
178
// construction using `Ty { fields, ..Default::default() }`
163
179
if !assigned_fields. is_empty ( ) && !cancel_lint {
164
- // take the original assignment as span
165
- let stmt = & block. stmts [ stmt_idx] ;
166
-
167
- if let StmtKind :: Local ( preceding_local) = & stmt. kind {
168
- // filter out fields like `= Default::default()`, because the FRU already covers them
169
- let assigned_fields = assigned_fields
170
- . into_iter ( )
171
- . filter ( |( _, rhs) | !is_expr_default ( rhs, cx) )
172
- . collect :: < Vec < ( Symbol , & Expr < ' _ > ) > > ( ) ;
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 ) ) ;
173
185
174
- // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
175
- let ext_with_default = !fields_of_type ( binding_type)
176
- . iter ( )
177
- . all ( |field| assigned_fields. iter ( ) . any ( |( a, _) | a == & field. name ) ) ;
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 ( ", " ) ;
178
195
179
- let field_list = assigned_fields
180
- . into_iter ( )
181
- . map ( |( field, rhs) | {
182
- // extract and store the assigned value for help message
183
- let value_snippet = snippet ( cx, rhs. span , ".." ) ;
184
- format ! ( "{}: {}" , field, value_snippet)
185
- } )
186
- . collect :: < Vec < String > > ( )
187
- . join ( ", " ) ;
188
-
189
- let sugg = if ext_with_default {
190
- if field_list. is_empty ( ) {
191
- format ! ( "{}::default()" , binding_type)
192
- } else {
193
- format ! ( "{} {{ {}, ..Default::default() }}" , binding_type, field_list)
194
- }
196
+ let sugg = if ext_with_default {
197
+ if field_list. is_empty ( ) {
198
+ format ! ( "{}::default()" , binding_type)
195
199
} else {
196
- format ! ( "{} {{ {} }}" , binding_type, field_list)
197
- } ;
200
+ format ! ( "{} {{ {}, ..Default::default() }}" , binding_type, field_list)
201
+ }
202
+ } else {
203
+ format ! ( "{} {{ {} }}" , binding_type, field_list)
204
+ } ;
198
205
199
- // span lint once per statement that binds default
200
- span_lint_and_note (
201
- cx,
202
- FIELD_REASSIGN_WITH_DEFAULT ,
203
- first_assign. unwrap ( ) . span ,
204
- "field assignment outside of initializer for an instance created with Default::default()" ,
205
- Some ( preceding_local. span ) ,
206
- & format ! (
207
- "consider initializing the variable with `{}` and removing relevant reassignments" ,
208
- sugg
209
- ) ,
210
- ) ;
211
- self . reassigned_linted . insert ( span) ;
212
- }
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) ;
213
219
}
214
220
}
215
221
}
@@ -230,47 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
230
236
}
231
237
}
232
238
233
- /// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
234
- /// for when the pattern type is a tuple.
235
- fn enumerate_bindings_using_default < ' tcx > (
236
- cx : & LateContext < ' tcx > ,
237
- block : & Block < ' tcx > ,
238
- ) -> Vec < ( usize , Symbol , Ty < ' tcx > , Span ) > {
239
- block
240
- . stmts
241
- . iter ( )
242
- . enumerate ( )
243
- . filter_map ( |( idx, stmt) | {
244
- if_chain ! {
245
- // only take `let ...` statements
246
- if let StmtKind :: Local ( ref local) = stmt. kind;
247
- // only take bindings to identifiers
248
- if let PatKind :: Binding ( _, _, ident, _) = local. pat. kind;
249
- // that are not tuples
250
- let ty = cx. typeck_results( ) . pat_ty( local. pat) ;
251
- if !matches!( ty. kind( ) , ty:: Tuple ( _) ) ;
252
- // only when assigning `... = Default::default()`
253
- if let Some ( ref expr) = local. init;
254
- if is_expr_default( expr, cx) ;
255
- then {
256
- Some ( ( idx, ident. name, ty, expr. span) )
257
- } else {
258
- None
259
- }
260
- }
261
- } )
262
- . collect ( )
263
- }
264
-
265
- fn stmt_shadows_binding ( this : & Stmt < ' _ > , shadowed : Symbol ) -> bool {
266
- if let StmtKind :: Local ( local) = & this. kind {
267
- if let PatKind :: Binding ( _, _, ident, _) = local. pat . kind {
268
- return ident. name == shadowed;
269
- }
270
- }
271
- false
272
- }
273
-
274
239
/// Returns the reassigned field and the assigning expression (right-hand side of assign).
275
240
fn field_reassigned_by_stmt < ' tcx > ( this : & Stmt < ' tcx > , binding_name : Symbol ) -> Option < ( Ident , & ' tcx Expr < ' tcx > ) > {
276
241
if_chain ! {
@@ -290,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
290
255
}
291
256
}
292
257
}
293
-
294
- /// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
295
- fn fields_of_type ( ty : Ty < ' _ > ) -> Vec < Ident > {
296
- if let Adt ( adt, _) = ty. kind ( ) {
297
- if adt. is_struct ( ) {
298
- let variant = & adt. non_enum_variant ( ) ;
299
- return variant. fields . iter ( ) . map ( |f| f. ident ) . collect ( ) ;
300
- }
301
- }
302
- vec ! [ ]
303
- }
0 commit comments