]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/default.rs
cde27d3ad2a0ce3def12b28ab0e5ebfddb640489
[rust.git] / clippy_lints / src / default.rs
1 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
2 use clippy_utils::source::snippet_with_macro_callsite;
3 use clippy_utils::ty::{has_drop, is_copy};
4 use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
5 use if_chain::if_chain;
6 use rustc_data_structures::fx::FxHashSet;
7 use rustc_errors::Applicability;
8 use rustc_hir::def::Res;
9 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
10 use rustc_lint::{LateContext, LateLintPass};
11 use rustc_middle::ty;
12 use rustc_session::{declare_tool_lint, impl_lint_pass};
13 use rustc_span::symbol::{Ident, Symbol};
14 use rustc_span::Span;
15
16 declare_clippy_lint! {
17     /// ### What it does
18     /// Checks for literal calls to `Default::default()`.
19     ///
20     /// ### Why is this bad?
21     /// It's more clear to the reader to use the name of the type whose default is
22     /// being gotten than the generic `Default`.
23     ///
24     /// ### Example
25     /// ```rust
26     /// // Bad
27     /// let s: String = Default::default();
28     ///
29     /// // Good
30     /// let s = String::default();
31     /// ```
32     pub DEFAULT_TRAIT_ACCESS,
33     pedantic,
34     "checks for literal calls to `Default::default()`"
35 }
36
37 declare_clippy_lint! {
38     /// ### What it does
39     /// Checks for immediate reassignment of fields initialized
40     /// with Default::default().
41     ///
42     /// ### Why is this bad?
43     ///It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
44     ///
45     /// ### Known problems
46     /// Assignments to patterns that are of tuple type are not linted.
47     ///
48     /// ### Example
49     /// Bad:
50     /// ```
51     /// # #[derive(Default)]
52     /// # struct A { i: i32 }
53     /// let mut a: A = Default::default();
54     /// a.i = 42;
55     /// ```
56     /// Use instead:
57     /// ```
58     /// # #[derive(Default)]
59     /// # struct A { i: i32 }
60     /// let a = A {
61     ///     i: 42,
62     ///     .. Default::default()
63     /// };
64     /// ```
65     pub FIELD_REASSIGN_WITH_DEFAULT,
66     style,
67     "binding initialized with Default should have its fields set in the initializer"
68 }
69
70 #[derive(Default)]
71 pub struct Default {
72     // Spans linted by `field_reassign_with_default`.
73     reassigned_linted: FxHashSet<Span>,
74 }
75
76 impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
77
78 impl LateLintPass<'_> for Default {
79     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
80         if_chain! {
81             if !in_macro(expr.span);
82             // Avoid cases already linted by `field_reassign_with_default`
83             if !self.reassigned_linted.contains(&expr.span);
84             if let ExprKind::Call(path, ..) = expr.kind;
85             if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
86             if let ExprKind::Path(ref qpath) = path.kind;
87             if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
88             if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
89             // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
90             if let QPath::Resolved(None, _path) = qpath;
91             let expr_ty = cx.typeck_results().expr_ty(expr);
92             if let ty::Adt(def, ..) = expr_ty.kind();
93             then {
94                 // TODO: Work out a way to put "whatever the imported way of referencing
95                 // this type in this file" rather than a fully-qualified type.
96                 let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
97                 span_lint_and_sugg(
98                     cx,
99                     DEFAULT_TRAIT_ACCESS,
100                     expr.span,
101                     &format!("calling `{}` is more clear than this expression", replacement),
102                     "try",
103                     replacement,
104                     Applicability::Unspecified, // First resolve the TODO above
105                 );
106             }
107         }
108     }
109
110     #[allow(clippy::too_many_lines)]
111     fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
112         // start from the `let mut _ = _::default();` and look at all the following
113         // statements, see if they re-assign the fields of the binding
114         let stmts_head = match block.stmts {
115             // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
116             [head @ .., _] if !head.is_empty() => head,
117             _ => return,
118         };
119         for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
120             // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
121             // `default` method of the `Default` trait, and store statement index in current block being
122             // checked and the name of the bound variable
123             let (local, variant, binding_name, binding_type, span) = if_chain! {
124                 // only take `let ...` statements
125                 if let StmtKind::Local(local) = stmt.kind;
126                 if let Some(expr) = local.init;
127                 if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
128                 if !in_macro(expr.span);
129                 // only take bindings to identifiers
130                 if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
131                 // only when assigning `... = Default::default()`
132                 if is_expr_default(expr, cx);
133                 let binding_type = cx.typeck_results().node_type(binding_id);
134                 if let Some(adt) = binding_type.ty_adt_def();
135                 if adt.is_struct();
136                 let variant = adt.non_enum_variant();
137                 if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
138                 let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
139                 if variant
140                     .fields
141                     .iter()
142                     .all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
143                 let all_fields_are_copy = variant
144                     .fields
145                     .iter()
146                     .all(|field| {
147                         is_copy(cx, cx.tcx.type_of(field.did))
148                     });
149                 if !has_drop(cx, binding_type) || all_fields_are_copy;
150                 then {
151                     (local, variant, ident.name, binding_type, expr.span)
152                 } else {
153                     continue;
154                 }
155             };
156
157             // find all "later statement"'s where the fields of the binding set as
158             // Default::default() get reassigned, unless the reassignment refers to the original binding
159             let mut first_assign = None;
160             let mut assigned_fields = Vec::new();
161             let mut cancel_lint = false;
162             for consecutive_statement in &block.stmts[stmt_idx + 1..] {
163                 // find out if and which field was set by this `consecutive_statement`
164                 if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
165                     // interrupt and cancel lint if assign_rhs references the original binding
166                     if contains_name(binding_name, assign_rhs) {
167                         cancel_lint = true;
168                         break;
169                     }
170
171                     // if the field was previously assigned, replace the assignment, otherwise insert the assignment
172                     if let Some(prev) = assigned_fields
173                         .iter_mut()
174                         .find(|(field_name, _)| field_name == &field_ident.name)
175                     {
176                         *prev = (field_ident.name, assign_rhs);
177                     } else {
178                         assigned_fields.push((field_ident.name, assign_rhs));
179                     }
180
181                     // also set first instance of error for help message
182                     if first_assign.is_none() {
183                         first_assign = Some(consecutive_statement);
184                     }
185                 }
186                 // interrupt if no field was assigned, since we only want to look at consecutive statements
187                 else {
188                     break;
189                 }
190             }
191
192             // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
193             // construction using `Ty { fields, ..Default::default() }`
194             if !assigned_fields.is_empty() && !cancel_lint {
195                 // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
196                 let ext_with_default = !variant
197                     .fields
198                     .iter()
199                     .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
200
201                 let field_list = assigned_fields
202                     .into_iter()
203                     .map(|(field, rhs)| {
204                         // extract and store the assigned value for help message
205                         let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
206                         format!("{}: {}", field, value_snippet)
207                     })
208                     .collect::<Vec<String>>()
209                     .join(", ");
210
211                 // give correct suggestion if generics are involved (see #6944)
212                 let binding_type = if_chain! {
213                     if let ty::Adt(adt_def, substs) = binding_type.kind();
214                     if !substs.is_empty();
215                     then {
216                         let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
217                         let generic_args = substs.iter().collect::<Vec<_>>();
218                         let tys_str = generic_args
219                             .iter()
220                             .map(ToString::to_string)
221                             .collect::<Vec<_>>()
222                             .join(", ");
223                         format!("{}::<{}>", adt_def_ty_name, &tys_str)
224                     } else {
225                         binding_type.to_string()
226                     }
227                 };
228
229                 let sugg = if ext_with_default {
230                     if field_list.is_empty() {
231                         format!("{}::default()", binding_type)
232                     } else {
233                         format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
234                     }
235                 } else {
236                     format!("{} {{ {} }}", binding_type, field_list)
237                 };
238
239                 // span lint once per statement that binds default
240                 span_lint_and_note(
241                     cx,
242                     FIELD_REASSIGN_WITH_DEFAULT,
243                     first_assign.unwrap().span,
244                     "field assignment outside of initializer for an instance created with Default::default()",
245                     Some(local.span),
246                     &format!(
247                         "consider initializing the variable with `{}` and removing relevant reassignments",
248                         sugg
249                     ),
250                 );
251                 self.reassigned_linted.insert(span);
252             }
253         }
254     }
255 }
256
257 /// Checks if the given expression is the `default` method belonging to the `Default` trait.
258 fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
259     if_chain! {
260         if let ExprKind::Call(fn_expr, _) = &expr.kind;
261         if let ExprKind::Path(qpath) = &fn_expr.kind;
262         if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
263         then {
264             // right hand side of assignment is `Default::default`
265             match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
266         } else {
267             false
268         }
269     }
270 }
271
272 /// Returns the reassigned field and the assigning expression (right-hand side of assign).
273 fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
274     if_chain! {
275         // only take assignments
276         if let StmtKind::Semi(later_expr) = this.kind;
277         if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind;
278         // only take assignments to fields where the left-hand side field is a field of
279         // the same binding as the previous statement
280         if let ExprKind::Field(binding, field_ident) = assign_lhs.kind;
281         if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
282         if let Some(second_binding_name) = path.segments.last();
283         if second_binding_name.ident.name == binding_name;
284         then {
285             Some((field_ident, assign_rhs))
286         } else {
287             None
288         }
289     }
290 }