@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
66use rustc_hir:: def:: Res ;
77use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
88use rustc_lint:: { LateContext , LateLintPass } ;
9- use rustc_middle:: ty:: { self , Adt , Ty } ;
9+ use rustc_middle:: ty;
1010use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1111use rustc_span:: symbol:: { Ident , Symbol } ;
1212use rustc_span:: Span ;
@@ -103,18 +103,41 @@ impl LateLintPass<'_> for Default {
103103 }
104104
105105 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-
111106 // start from the `let mut _ = _::default();` and look at all the following
112107 // 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+ } ;
118141
119142 // find all "later statement"'s where the fields of the binding set as
120143 // Default::default() get reassigned, unless the reassignment refers to the original binding
@@ -154,49 +177,45 @@ impl LateLintPass<'_> for Default {
154177 // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
155178 // construction using `Ty { fields, ..Default::default() }`
156179 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 ) ) ;
165185
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 ( ", " ) ;
175195
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)
182199 } 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+ } ;
185205
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) ;
200219 }
201220 }
202221 }
@@ -217,38 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
217236 }
218237}
219238
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-
252239/// Returns the reassigned field and the assigning expression (right-hand side of assign).
253240fn field_reassigned_by_stmt < ' tcx > ( this : & Stmt < ' tcx > , binding_name : Symbol ) -> Option < ( Ident , & ' tcx Expr < ' tcx > ) > {
254241 if_chain ! {
@@ -268,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
268255 }
269256 }
270257}
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