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::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
4 use if_chain::if_chain;
5 use rustc_data_structures::fx::FxHashSet;
6 use rustc_errors::Applicability;
7 use rustc_hir::def::Res;
8 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
9 use rustc_lint::{LateContext, LateLintPass};
11 use rustc_session::{declare_tool_lint, impl_lint_pass};
12 use rustc_span::symbol::{Ident, Symbol};
15 declare_clippy_lint! {
17 /// Checks for literal calls to `Default::default()`.
19 /// ### Why is this bad?
20 /// It's more clear to the reader to use the name of the type whose default is
21 /// being gotten than the generic `Default`.
26 /// let s: String = Default::default();
29 /// let s = String::default();
31 pub DEFAULT_TRAIT_ACCESS,
33 "checks for literal calls to `Default::default()`"
36 declare_clippy_lint! {
38 /// Checks for immediate reassignment of fields initialized
39 /// with Default::default().
41 /// ### Why is this bad?
42 ///It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
44 /// ### Known problems
45 /// Assignments to patterns that are of tuple type are not linted.
50 /// # #[derive(Default)]
51 /// # struct A { i: i32 }
52 /// let mut a: A = Default::default();
57 /// # #[derive(Default)]
58 /// # struct A { i: i32 }
61 /// .. Default::default()
64 pub FIELD_REASSIGN_WITH_DEFAULT,
66 "binding initialized with Default should have its fields set in the initializer"
71 // Spans linted by `field_reassign_with_default`.
72 reassigned_linted: FxHashSet<Span>,
75 impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
77 impl LateLintPass<'_> for Default {
78 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
80 if !in_macro(expr.span);
81 // Avoid cases already linted by `field_reassign_with_default`
82 if !self.reassigned_linted.contains(&expr.span);
83 if let ExprKind::Call(path, ..) = expr.kind;
84 if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
85 if let ExprKind::Path(ref qpath) = path.kind;
86 if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
87 if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
88 // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
89 if let QPath::Resolved(None, _path) = qpath;
90 let expr_ty = cx.typeck_results().expr_ty(expr);
91 if let ty::Adt(def, ..) = expr_ty.kind();
93 // TODO: Work out a way to put "whatever the imported way of referencing
94 // this type in this file" rather than a fully-qualified type.
95 let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
100 &format!("calling `{}` is more clear than this expression", replacement),
103 Applicability::Unspecified, // First resolve the TODO above
109 #[allow(clippy::too_many_lines)]
110 fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
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 let stmts_head = match block.stmts {
114 // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
115 [head @ .., _] if !head.is_empty() => head,
118 for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
119 // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
120 // `default` method of the `Default` trait, and store statement index in current block being
121 // checked and the name of the bound variable
122 let (local, variant, binding_name, binding_type, span) = if_chain! {
123 // only take `let ...` statements
124 if let StmtKind::Local(local) = stmt.kind;
125 if let Some(expr) = local.init;
126 if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
127 if !in_macro(expr.span);
128 // only take bindings to identifiers
129 if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
130 // only when assigning `... = Default::default()`
131 if is_expr_default(expr, cx);
132 let binding_type = cx.typeck_results().node_type(binding_id);
133 if let Some(adt) = binding_type.ty_adt_def();
135 let variant = adt.non_enum_variant();
136 if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
137 let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
141 .all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
143 (local, variant, ident.name, binding_type, expr.span)
149 // find all "later statement"'s where the fields of the binding set as
150 // Default::default() get reassigned, unless the reassignment refers to the original binding
151 let mut first_assign = None;
152 let mut assigned_fields = Vec::new();
153 let mut cancel_lint = false;
154 for consecutive_statement in &block.stmts[stmt_idx + 1..] {
155 // find out if and which field was set by this `consecutive_statement`
156 if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
157 // interrupt and cancel lint if assign_rhs references the original binding
158 if contains_name(binding_name, assign_rhs) {
163 // if the field was previously assigned, replace the assignment, otherwise insert the assignment
164 if let Some(prev) = assigned_fields
166 .find(|(field_name, _)| field_name == &field_ident.name)
168 *prev = (field_ident.name, assign_rhs);
170 assigned_fields.push((field_ident.name, assign_rhs));
173 // also set first instance of error for help message
174 if first_assign.is_none() {
175 first_assign = Some(consecutive_statement);
178 // interrupt if no field was assigned, since we only want to look at consecutive statements
184 // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
185 // construction using `Ty { fields, ..Default::default() }`
186 if !assigned_fields.is_empty() && !cancel_lint {
187 // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
188 let ext_with_default = !variant
191 .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
193 let field_list = assigned_fields
195 .map(|(field, rhs)| {
196 // extract and store the assigned value for help message
197 let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
198 format!("{}: {}", field, value_snippet)
200 .collect::<Vec<String>>()
203 // give correct suggestion if generics are involved (see #6944)
204 let binding_type = if_chain! {
205 if let ty::Adt(adt_def, substs) = binding_type.kind();
206 if !substs.is_empty();
208 let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
209 let generic_args = substs.iter().collect::<Vec<_>>();
210 let tys_str = generic_args
212 .map(ToString::to_string)
215 format!("{}::<{}>", adt_def_ty_name, &tys_str)
217 binding_type.to_string()
221 let sugg = if ext_with_default {
222 if field_list.is_empty() {
223 format!("{}::default()", binding_type)
225 format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
228 format!("{} {{ {} }}", binding_type, field_list)
231 // span lint once per statement that binds default
234 FIELD_REASSIGN_WITH_DEFAULT,
235 first_assign.unwrap().span,
236 "field assignment outside of initializer for an instance created with Default::default()",
239 "consider initializing the variable with `{}` and removing relevant reassignments",
243 self.reassigned_linted.insert(span);
249 /// Checks if the given expression is the `default` method belonging to the `Default` trait.
250 fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
252 if let ExprKind::Call(fn_expr, _) = &expr.kind;
253 if let ExprKind::Path(qpath) = &fn_expr.kind;
254 if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
256 // right hand side of assignment is `Default::default`
257 match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
264 /// Returns the reassigned field and the assigning expression (right-hand side of assign).
265 fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
267 // only take assignments
268 if let StmtKind::Semi(later_expr) = this.kind;
269 if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind;
270 // only take assignments to fields where the left-hand side field is a field of
271 // the same binding as the previous statement
272 if let ExprKind::Field(binding, field_ident) = assign_lhs.kind;
273 if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
274 if let Some(second_binding_name) = path.segments.last();
275 if second_binding_name.ident.name == binding_name;
277 Some((field_ident, assign_rhs))