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