]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/default.rs
Merge commit '4911ab124c481430672a3833b37075e6435ec34d' into clippyup
[rust.git] / clippy_lints / src / default.rs
1 use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet};
2 use crate::utils::{span_lint_and_note, span_lint_and_sugg};
3 use if_chain::if_chain;
4 use rustc_data_structures::fx::FxHashSet;
5 use rustc_errors::Applicability;
6 use rustc_hir::def::Res;
7 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
8 use rustc_lint::{LateContext, LateLintPass};
9 use rustc_middle::ty::{self, Adt, Ty};
10 use rustc_session::{declare_tool_lint, impl_lint_pass};
11 use rustc_span::symbol::{Ident, Symbol};
12 use rustc_span::Span;
13
14 declare_clippy_lint! {
15     /// **What it does:** Checks for literal calls to `Default::default()`.
16     ///
17     /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
18     /// being gotten than the generic `Default`.
19     ///
20     /// **Known problems:** None.
21     ///
22     /// **Example:**
23     /// ```rust
24     /// // Bad
25     /// let s: String = Default::default();
26     ///
27     /// // Good
28     /// let s = String::default();
29     /// ```
30     pub DEFAULT_TRAIT_ACCESS,
31     pedantic,
32     "checks for literal calls to `Default::default()`"
33 }
34
35 declare_clippy_lint! {
36     /// **What it does:** Checks for immediate reassignment of fields initialized
37     /// with Default::default().
38     ///
39     /// **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).
40     ///
41     /// **Known problems:** Assignments to patterns that are of tuple type are not linted.
42     ///
43     /// **Example:**
44     /// Bad:
45     /// ```
46     /// # #[derive(Default)]
47     /// # struct A { i: i32 }
48     /// let mut a: A = Default::default();
49     /// a.i = 42;
50     /// ```
51     /// Use instead:
52     /// ```
53     /// # #[derive(Default)]
54     /// # struct A { i: i32 }
55     /// let a = A {
56     ///     i: 42,
57     ///     .. Default::default()
58     /// };
59     /// ```
60     pub FIELD_REASSIGN_WITH_DEFAULT,
61     style,
62     "binding initialized with Default should have its fields set in the initializer"
63 }
64
65 #[derive(Default)]
66 pub struct Default {
67     // Spans linted by `field_reassign_with_default`.
68     reassigned_linted: FxHashSet<Span>,
69 }
70
71 impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
72
73 impl LateLintPass<'_> for Default {
74     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
75         if_chain! {
76             // Avoid cases already linted by `field_reassign_with_default`
77             if !self.reassigned_linted.contains(&expr.span);
78             if let ExprKind::Call(ref path, ..) = expr.kind;
79             if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
80             if let ExprKind::Path(ref qpath) = path.kind;
81             if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
82             if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
83             // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
84             if let QPath::Resolved(None, _path) = qpath;
85             then {
86                 let expr_ty = cx.typeck_results().expr_ty(expr);
87                 if let ty::Adt(def, ..) = expr_ty.kind() {
88                     // TODO: Work out a way to put "whatever the imported way of referencing
89                     // this type in this file" rather than a fully-qualified type.
90                     let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
91                     span_lint_and_sugg(
92                         cx,
93                         DEFAULT_TRAIT_ACCESS,
94                         expr.span,
95                         &format!("calling `{}` is more clear than this expression", replacement),
96                         "try",
97                         replacement,
98                         Applicability::Unspecified, // First resolve the TODO above
99                     );
100                 }
101             }
102         }
103     }
104
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         // start from the `let mut _ = _::default();` and look at all the following
112         // 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             }
118
119             // find all "later statement"'s where the fields of the binding set as
120             // Default::default() get reassigned, unless the reassignment refers to the original binding
121             let mut first_assign = None;
122             let mut assigned_fields = Vec::new();
123             let mut cancel_lint = false;
124             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                 // 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                 {
134                     // interrupt and cancel lint if assign_rhs references the original binding
135                     if contains_name(binding_name, assign_rhs) {
136                         cancel_lint = true;
137                         break;
138                     }
139
140                     // if the field was previously assigned, replace the assignment, otherwise insert the assignment
141                     if let Some(prev) = assigned_fields
142                         .iter_mut()
143                         .find(|(field_name, _)| field_name == &field_ident.name)
144                     {
145                         *prev = (field_ident.name, assign_rhs);
146                     } else {
147                         assigned_fields.push((field_ident.name, assign_rhs));
148                     }
149
150                     // also set first instance of error for help message
151                     if first_assign.is_none() {
152                         first_assign = Some(consecutive_statement);
153                     }
154                 }
155                 // interrupt also if no field was assigned, since we only want to look at consecutive statements
156                 else {
157                     break;
158                 }
159             }
160
161             // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
162             // construction using `Ty { fields, ..Default::default() }`
163             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<'_>)>>();
173
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));
178
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                         }
195                     } else {
196                         format!("{} {{ {} }}", binding_type, field_list)
197                     };
198
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                 }
213             }
214         }
215     }
216 }
217
218 /// Checks if the given expression is the `default` method belonging to the `Default` trait.
219 fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
220     if_chain! {
221         if let ExprKind::Call(ref fn_expr, _) = &expr.kind;
222         if let ExprKind::Path(qpath) = &fn_expr.kind;
223         if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id);
224         then {
225             // right hand side of assignment is `Default::default`
226             match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
227         } else {
228             false
229         }
230     }
231 }
232
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 /// Returns the reassigned field and the assigning expression (right-hand side of assign).
275 fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
276     if_chain! {
277         // only take assignments
278         if let StmtKind::Semi(ref later_expr) = this.kind;
279         if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind;
280         // only take assignments to fields where the left-hand side field is a field of
281         // the same binding as the previous statement
282         if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind;
283         if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
284         if let Some(second_binding_name) = path.segments.last();
285         if second_binding_name.ident.name == binding_name;
286         then {
287             Some((field_ident, assign_rhs))
288         } else {
289             None
290         }
291     }
292 }
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 }