1
1
use crate :: utils:: { match_def_path, paths, qpath_res, snippet, span_lint_and_note} ;
2
2
use if_chain:: if_chain;
3
3
use rustc_hir:: def:: Res ;
4
- use rustc_hir:: { Block , ExprKind , PatKind , QPath , StmtKind } ;
5
- use rustc_span:: symbol:: Symbol ;
4
+ use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
5
+ use rustc_span:: symbol:: { Ident , Symbol } ;
6
6
7
7
use rustc_lint:: { LateContext , LateLintPass } ;
8
8
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -11,7 +11,7 @@ declare_clippy_lint! {
11
11
/// **What it does:** Checks for immediate reassignment of fields initialized
12
12
/// with Default::default().
13
13
///
14
- /// **Why is this bad?**It's more idiomatic to use [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
14
+ /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
15
15
///
16
16
/// **Known problems:** None.
17
17
///
@@ -39,36 +39,13 @@ declare_lint_pass!(FieldReassignWithDefault => [FIELD_REASSIGN_WITH_DEFAULT]);
39
39
40
40
impl LateLintPass < ' _ > for FieldReassignWithDefault {
41
41
fn check_block ( & mut self , cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
42
- // store statement index and name of binding for all statements like
43
- // `let mut binding = Default::default();`
44
- let binding_statements_using_default: Vec < ( usize , Symbol ) > = block
45
- . stmts
46
- . iter ( )
47
- . enumerate ( )
48
- . filter_map ( |( idx, stmt) | {
49
- if_chain ! {
50
- // only take `let ...` statements
51
- if let StmtKind :: Local ( ref local) = stmt. kind;
52
- // only take bindings to identifiers
53
- if let PatKind :: Binding ( _, _, ident, _) = local. pat. kind;
54
- // only when assigning `... = Default::default()`
55
- if let Some ( ref expr) = local. init;
56
- if let ExprKind :: Call ( ref fn_expr, _) = & expr. kind;
57
- if let ExprKind :: Path ( qpath) = & fn_expr. kind;
58
- if let Res :: Def ( _, def_id) = qpath_res( cx, qpath, fn_expr. hir_id) ;
59
- // right hand side of assignment is `Default::default`
60
- if match_def_path( cx, def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
61
- then {
62
- Some ( ( idx, ident. name) )
63
- }
64
- else {
65
- None
66
- }
67
- }
68
- } )
69
- . collect :: < Vec < _ > > ( ) ;
70
42
71
- // start from the `let mut binding = Default::default();` and look at all the following
43
+ // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
44
+ // `default` method of the `Default` trait. and store statement index in current block being
45
+ // checked and the name of the bound variable
46
+ let binding_statements_using_default: Vec < ( usize , Symbol ) > = enumerate_bindings_using_default ( cx, block) ;
47
+
48
+ // start from the `let mut _ = _::default();` and look at all the following
72
49
// statements, see if they re-assign the fields of the binding
73
50
for ( stmt_idx, binding_name) in binding_statements_using_default {
74
51
// last statement of block cannot trigger the lint
@@ -80,40 +57,24 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
80
57
// Default::default() get reassigned
81
58
let mut first_assign = None ;
82
59
let mut assigned_fields = vec ! [ ] ;
83
- for post_defassign_stmt in & block. stmts [ stmt_idx + 1 ..] {
60
+ for consequtive_statement in & block. stmts [ stmt_idx + 1 ..] {
84
61
// interrupt if the statement is a let binding (`Local`) that shadows the original
85
62
// binding
86
- if let StmtKind :: Local ( local) = & post_defassign_stmt. kind {
87
- if let PatKind :: Binding ( _, _, ident, _) = local. pat . kind {
88
- if ident. name == binding_name {
89
- break ;
90
- }
91
- }
63
+ if stmt_shadows_binding ( consequtive_statement, & binding_name) {
64
+ break ;
92
65
}
93
66
// statement kinds other than `StmtKind::Local` are valid, because they cannot
94
67
// shadow a binding
95
68
else {
96
- if_chain ! {
97
- // only take assignments
98
- if let StmtKind :: Semi ( ref later_expr) = post_defassign_stmt. kind;
99
- if let ExprKind :: Assign ( ref assign_lhs, ref assign_rhs, _) = later_expr. kind;
100
- // only take assignments to fields where the left-hand side field is a field of
101
- // the same binding as the previous statement
102
- if let ExprKind :: Field ( ref binding, later_field_ident) = assign_lhs. kind;
103
- if let ExprKind :: Path ( ref qpath) = binding. kind;
104
- if let QPath :: Resolved ( _, path) = qpath;
105
- if let Some ( second_binding_name) = path. segments. last( ) ;
106
- if second_binding_name. ident. name == binding_name;
107
- then {
108
- // extract and store the name of the field and the assigned value for help message
109
- let field = later_field_ident. name;
110
- let value_snippet = snippet( cx, assign_rhs. span, ".." ) ;
111
- assigned_fields. push( ( field, value_snippet) ) ;
69
+ // find out if and which field was set by this `consequtive_statement`
70
+ if let Some ( ( field_ident, assign_rhs) ) = field_reassigned_by_stmt ( consequtive_statement, & binding_name) {
71
+ // extract and store the assigned value for help message
72
+ let value_snippet = snippet ( cx, assign_rhs. span , ".." ) ;
73
+ assigned_fields. push ( ( field_ident. name , value_snippet) ) ;
112
74
113
- // also set first instance of error for help message
114
- if first_assign. is_none( ) {
115
- first_assign = Some ( post_defassign_stmt) ;
116
- }
75
+ // also set first instance of error for help message
76
+ if first_assign. is_none ( ) {
77
+ first_assign = Some ( consequtive_statement) ;
117
78
}
118
79
}
119
80
}
@@ -122,7 +83,7 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
122
83
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
123
84
// immutable construction using `Ty { fields, ..Default::default() }`
124
85
if !assigned_fields. is_empty ( ) {
125
- // take the preceding statement as span
86
+ // take the original assignment as span
126
87
let stmt = & block. stmts [ stmt_idx] ;
127
88
if let StmtKind :: Local ( preceding_local) = & stmt. kind {
128
89
if let Some ( ty) = & preceding_local. ty {
@@ -151,3 +112,61 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
151
112
}
152
113
}
153
114
}
115
+
116
+ fn enumerate_bindings_using_default ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) -> Vec < ( usize , Symbol ) > {
117
+ block
118
+ . stmts
119
+ . iter ( )
120
+ . enumerate ( )
121
+ . filter_map ( |( idx, stmt) | {
122
+ if_chain ! {
123
+ // only take `let ...` statements
124
+ if let StmtKind :: Local ( ref local) = stmt. kind;
125
+ // only take bindings to identifiers
126
+ if let PatKind :: Binding ( _, _, ident, _) = local. pat. kind;
127
+ // only when assigning `... = Default::default()`
128
+ if let Some ( ref expr) = local. init;
129
+ if let ExprKind :: Call ( ref fn_expr, _) = & expr. kind;
130
+ if let ExprKind :: Path ( qpath) = & fn_expr. kind;
131
+ if let Res :: Def ( _, def_id) = qpath_res( cx, qpath, fn_expr. hir_id) ;
132
+ // right hand side of assignment is `Default::default`
133
+ if match_def_path( cx, def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
134
+ then {
135
+ Some ( ( idx, ident. name) )
136
+ }
137
+ else {
138
+ None
139
+ }
140
+ }
141
+ } ) . collect ( )
142
+ }
143
+
144
+ fn stmt_shadows_binding ( this : & Stmt < ' _ > , shadowed : & Symbol ) -> bool {
145
+ if let StmtKind :: Local ( local) = & this. kind {
146
+ if let PatKind :: Binding ( _, _, ident, _) = local. pat . kind {
147
+ return & ident. name == shadowed;
148
+ }
149
+ }
150
+ false
151
+ }
152
+
153
+ /// Returns the reassigned field and the assigning expression (right-hand side of assign).
154
+ fn field_reassigned_by_stmt < ' hir > ( this : & Stmt < ' hir > , binding_name : & Symbol ) -> Option < ( Ident , & ' hir Expr < ' hir > ) > {
155
+ if_chain ! {
156
+ // only take assignments
157
+ if let StmtKind :: Semi ( ref later_expr) = this. kind;
158
+ if let ExprKind :: Assign ( ref assign_lhs, ref assign_rhs, _) = later_expr. kind;
159
+ // only take assignments to fields where the left-hand side field is a field of
160
+ // the same binding as the previous statement
161
+ if let ExprKind :: Field ( ref binding, field_ident) = assign_lhs. kind;
162
+ if let ExprKind :: Path ( ref qpath) = binding. kind;
163
+ if let QPath :: Resolved ( _, path) = qpath;
164
+ if let Some ( second_binding_name) = path. segments. last( ) ;
165
+ if & second_binding_name. ident. name == binding_name;
166
+ then {
167
+ Some ( ( field_ident, assign_rhs) )
168
+ } else {
169
+ None
170
+ }
171
+ }
172
+ }
0 commit comments