@@ -4,6 +4,7 @@ use rustc_hir::def::Res;
44use rustc_data_structures:: fx:: FxHashMap ;
55use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
66use rustc_span:: symbol:: { Ident , Symbol } ;
7+ use rustc_middle:: ty:: { Adt , TyS } ;
78
89use rustc_lint:: { LateContext , LateLintPass } ;
910use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -44,11 +45,11 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
4445 // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
4546 // `default` method of the `Default` trait. and store statement index in current block being
4647 // 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) ;
4849
4950 // start from the `let mut _ = _::default();` and look at all the following
5051 // 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 {
5253 // last statement of block cannot trigger the lint
5354 if stmt_idx == block. stmts . len ( ) - 1 {
5455 break ;
@@ -71,9 +72,9 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
7172 if let Some ( ( field_ident, assign_rhs) ) = field_reassigned_by_stmt ( consequtive_statement, & binding_name) {
7273 // extract and store the assigned value for help message
7374 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 ) ;
7778
7879 // also set first instance of error for help message
7980 if first_assign. is_none ( ) {
@@ -88,8 +89,13 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
8889 if !assigned_fields. is_empty ( ) {
8990 // take the original assignment as span
9091 let stmt = & block. stmts [ stmt_idx] ;
92+
9193 if let StmtKind :: Local ( preceding_local) = & stmt. kind {
9294 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+
9399 let ty_snippet = snippet ( cx, ty. span , "_" ) ;
94100
95101 let field_list = assigned_fields
@@ -98,7 +104,11 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
98104 . collect :: < Vec < String > > ( )
99105 . join ( ", " ) ;
100106
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+ } ;
102112
103113 // span lint once per statement that binds default
104114 span_lint_and_note (
@@ -109,14 +119,16 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
109119 Some ( preceding_local. span ) ,
110120 & format ! ( "consider initializing the variable with `{}`" , sugg) ,
111121 ) ;
122+
112123 }
113124 }
114125 }
115126 }
116127 }
117128}
118129
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 > ) > {
120132 block
121133 . stmts
122134 . iter ( )
@@ -135,7 +147,9 @@ fn enumerate_bindings_using_default(cx: &LateContext<'_>, block: &Block<'_>) ->
135147 // right hand side of assignment is `Default::default`
136148 if match_def_path( cx, def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
137149 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) )
139153 }
140154 else {
141155 None
@@ -173,3 +187,15 @@ fn field_reassigned_by_stmt<'hir>(this: &Stmt<'hir>, binding_name: &Symbol) -> O
173187 }
174188 }
175189}
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