Skip to content

Commit b9ac8b5

Browse files
author
Henri Lunnikivi
committed
Refactor and prettify
1 parent b92fac4 commit b9ac8b5

File tree

1 file changed

+79
-60
lines changed

1 file changed

+79
-60
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::utils::{match_def_path, paths, qpath_res, snippet, span_lint_and_note};
22
use if_chain::if_chain;
33
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};
66

77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -11,7 +11,7 @@ declare_clippy_lint! {
1111
/// **What it does:** Checks for immediate reassignment of fields initialized
1212
/// with Default::default().
1313
///
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).
1515
///
1616
/// **Known problems:** None.
1717
///
@@ -39,36 +39,13 @@ declare_lint_pass!(FieldReassignWithDefault => [FIELD_REASSIGN_WITH_DEFAULT]);
3939

4040
impl LateLintPass<'_> for FieldReassignWithDefault {
4141
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<_>>();
7042

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
7249
// statements, see if they re-assign the fields of the binding
7350
for (stmt_idx, binding_name) in binding_statements_using_default {
7451
// last statement of block cannot trigger the lint
@@ -80,40 +57,24 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
8057
// Default::default() get reassigned
8158
let mut first_assign = None;
8259
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..] {
8461
// interrupt if the statement is a let binding (`Local`) that shadows the original
8562
// 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;
9265
}
9366
// statement kinds other than `StmtKind::Local` are valid, because they cannot
9467
// shadow a binding
9568
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));
11274

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);
11778
}
11879
}
11980
}
@@ -122,7 +83,7 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
12283
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
12384
// immutable construction using `Ty { fields, ..Default::default() }`
12485
if !assigned_fields.is_empty() {
125-
// take the preceding statement as span
86+
// take the original assignment as span
12687
let stmt = &block.stmts[stmt_idx];
12788
if let StmtKind::Local(preceding_local) = &stmt.kind {
12889
if let Some(ty) = &preceding_local.ty {
@@ -151,3 +112,61 @@ impl LateLintPass<'_> for FieldReassignWithDefault {
151112
}
152113
}
153114
}
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

Comments
 (0)