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}
;
5 any_parent_is_automatically_derived
, contains_name
, get_parent_expr
, is_from_proc_macro
, match_def_path
, paths
,
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}
;
14 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
15 use rustc_span
::symbol
::{Ident, Symbol}
;
18 declare_clippy_lint
! {
20 /// Checks for literal calls to `Default::default()`.
22 /// ### Why is this bad?
23 /// It's easier for the reader if the name of the type is used, rather than the
24 /// generic `Default`.
28 /// let s: String = Default::default();
33 /// let s = String::default();
35 #[clippy::version = "pre 1.29.0"]
36 pub DEFAULT_TRAIT_ACCESS
,
38 "checks for literal calls to `Default::default()`"
41 declare_clippy_lint
! {
43 /// Checks for immediate reassignment of fields initialized
44 /// with Default::default().
46 /// ### Why is this bad?
47 ///It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
49 /// ### Known problems
50 /// Assignments to patterns that are of tuple type are not linted.
54 /// # #[derive(Default)]
55 /// # struct A { i: i32 }
56 /// let mut a: A = Default::default();
62 /// # #[derive(Default)]
63 /// # struct A { i: i32 }
66 /// .. Default::default()
69 #[clippy::version = "1.49.0"]
70 pub FIELD_REASSIGN_WITH_DEFAULT
,
72 "binding initialized with Default should have its fields set in the initializer"
77 // Spans linted by `field_reassign_with_default`.
78 reassigned_linted
: FxHashSet
<Span
>,
81 impl_lint_pass
!(Default
=> [DEFAULT_TRAIT_ACCESS
, FIELD_REASSIGN_WITH_DEFAULT
]);
83 impl<'tcx
> LateLintPass
<'tcx
> for Default
{
84 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
86 if !expr
.span
.from_expansion();
87 // Avoid cases already linted by `field_reassign_with_default`
88 if !self.reassigned_linted
.contains(&expr
.span
);
89 if let ExprKind
::Call(path
, ..) = expr
.kind
;
90 if !any_parent_is_automatically_derived(cx
.tcx
, expr
.hir_id
);
91 if let ExprKind
::Path(ref qpath
) = path
.kind
;
92 if let Some(def_id
) = cx
.qpath_res(qpath
, path
.hir_id
).opt_def_id();
93 if match_def_path(cx
, def_id
, &paths
::DEFAULT_TRAIT_METHOD
);
94 if !is_update_syntax_base(cx
, expr
);
95 // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
96 if let QPath
::Resolved(None
, _path
) = qpath
;
97 let expr_ty
= cx
.typeck_results().expr_ty(expr
);
98 if let ty
::Adt(def
, ..) = expr_ty
.kind();
99 if !is_from_proc_macro(cx
, expr
);
101 // TODO: Work out a way to put "whatever the imported way of referencing
102 // this type in this file" rather than a fully-qualified type.
103 let replacement
= format
!("{}::default()", cx
.tcx
.def_path_str(def
.did()));
106 DEFAULT_TRAIT_ACCESS
,
108 &format
!("calling `{}` is more clear than this expression", replacement
),
111 Applicability
::Unspecified
, // First resolve the TODO above
117 #[expect(clippy::too_many_lines)]
118 fn check_block(&mut self, cx
: &LateContext
<'tcx
>, block
: &Block
<'tcx
>) {
119 // start from the `let mut _ = _::default();` and look at all the following
120 // statements, see if they re-assign the fields of the binding
121 let stmts_head
= match block
.stmts
{
122 // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
123 [head @
.., _
] if !head
.is_empty() => head
,
126 for (stmt_idx
, stmt
) in stmts_head
.iter().enumerate() {
127 // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
128 // `default` method of the `Default` trait, and store statement index in current block being
129 // checked and the name of the bound variable
130 let (local
, variant
, binding_name
, binding_type
, span
) = if_chain
! {
131 // only take `let ...` statements
132 if let StmtKind
::Local(local
) = stmt
.kind
;
133 if let Some(expr
) = local
.init
;
134 if !any_parent_is_automatically_derived(cx
.tcx
, expr
.hir_id
);
135 if !expr
.span
.from_expansion();
136 // only take bindings to identifiers
137 if let PatKind
::Binding(_
, binding_id
, ident
, _
) = local
.pat
.kind
;
138 // only when assigning `... = Default::default()`
139 if is_expr_default(expr
, cx
);
140 let binding_type
= cx
.typeck_results().node_type(binding_id
);
141 if let Some(adt
) = binding_type
.ty_adt_def();
143 let variant
= adt
.non_enum_variant();
144 if adt
.did().is_local() || !variant
.is_field_list_non_exhaustive();
145 let module_did
= cx
.tcx
.parent_module(stmt
.hir_id
);
149 .all(|field
| field
.vis
.is_accessible_from(module_did
, cx
.tcx
));
150 let all_fields_are_copy
= variant
154 is_copy(cx
, cx
.tcx
.type_of(field
.did
))
156 if !has_drop(cx
, binding_type
) || all_fields_are_copy
;
158 (local
, variant
, ident
.name
, binding_type
, expr
.span
)
164 // find all "later statement"'s where the fields of the binding set as
165 // Default::default() get reassigned, unless the reassignment refers to the original binding
166 let mut first_assign
= None
;
167 let mut assigned_fields
= Vec
::new();
168 let mut cancel_lint
= false;
169 for consecutive_statement
in &block
.stmts
[stmt_idx
+ 1..] {
170 // find out if and which field was set by this `consecutive_statement`
171 if let Some((field_ident
, assign_rhs
)) = field_reassigned_by_stmt(consecutive_statement
, binding_name
) {
172 // interrupt and cancel lint if assign_rhs references the original binding
173 if contains_name(binding_name
, assign_rhs
) {
178 // if the field was previously assigned, replace the assignment, otherwise insert the assignment
179 if let Some(prev
) = assigned_fields
181 .find(|(field_name
, _
)| field_name
== &field_ident
.name
)
183 *prev
= (field_ident
.name
, assign_rhs
);
185 assigned_fields
.push((field_ident
.name
, assign_rhs
));
188 // also set first instance of error for help message
189 if first_assign
.is_none() {
190 first_assign
= Some(consecutive_statement
);
193 // interrupt if no field was assigned, since we only want to look at consecutive statements
199 // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
200 // construction using `Ty { fields, ..Default::default() }`
201 if !assigned_fields
.is_empty() && !cancel_lint
{
202 // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
203 let ext_with_default
= !variant
206 .all(|field
| assigned_fields
.iter().any(|(a
, _
)| a
== &field
.name
));
208 let field_list
= assigned_fields
210 .map(|(field
, rhs
)| {
211 // extract and store the assigned value for help message
212 let value_snippet
= snippet_with_macro_callsite(cx
, rhs
.span
, "..");
213 format
!("{}: {}", field
, value_snippet
)
215 .collect
::<Vec
<String
>>()
218 // give correct suggestion if generics are involved (see #6944)
219 let binding_type
= if_chain
! {
220 if let ty
::Adt(adt_def
, substs
) = binding_type
.kind();
221 if !substs
.is_empty();
223 let adt_def_ty_name
= cx
.tcx
.item_name(adt_def
.did());
224 let generic_args
= substs
.iter().collect
::<Vec
<_
>>();
225 let tys_str
= generic_args
227 .map(ToString
::to_string
)
230 format
!("{}::<{}>", adt_def_ty_name
, &tys_str
)
232 binding_type
.to_string()
236 let sugg
= if ext_with_default
{
237 if field_list
.is_empty() {
238 format
!("{}::default()", binding_type
)
240 format
!("{} {{ {}, ..Default::default() }}", binding_type
, field_list
)
243 format
!("{} {{ {} }}", binding_type
, field_list
)
246 // span lint once per statement that binds default
249 FIELD_REASSIGN_WITH_DEFAULT
,
250 first_assign
.unwrap().span
,
251 "field assignment outside of initializer for an instance created with Default::default()",
254 "consider initializing the variable with `{}` and removing relevant reassignments",
258 self.reassigned_linted
.insert(span
);
264 /// Checks if the given expression is the `default` method belonging to the `Default` trait.
265 fn is_expr_default
<'tcx
>(expr
: &'tcx Expr
<'tcx
>, cx
: &LateContext
<'tcx
>) -> bool
{
267 if let ExprKind
::Call(fn_expr
, _
) = &expr
.kind
;
268 if let ExprKind
::Path(qpath
) = &fn_expr
.kind
;
269 if let Res
::Def(_
, def_id
) = cx
.qpath_res(qpath
, fn_expr
.hir_id
);
271 // right hand side of assignment is `Default::default`
272 match_def_path(cx
, def_id
, &paths
::DEFAULT_TRAIT_METHOD
)
279 /// Returns the reassigned field and the assigning expression (right-hand side of assign).
280 fn field_reassigned_by_stmt
<'tcx
>(this
: &Stmt
<'tcx
>, binding_name
: Symbol
) -> Option
<(Ident
, &'tcx Expr
<'tcx
>)> {
282 // only take assignments
283 if let StmtKind
::Semi(later_expr
) = this
.kind
;
284 if let ExprKind
::Assign(assign_lhs
, assign_rhs
, _
) = later_expr
.kind
;
285 // only take assignments to fields where the left-hand side field is a field of
286 // the same binding as the previous statement
287 if let ExprKind
::Field(binding
, field_ident
) = assign_lhs
.kind
;
288 if let ExprKind
::Path(QPath
::Resolved(_
, path
)) = binding
.kind
;
289 if let Some(second_binding_name
) = path
.segments
.last();
290 if second_binding_name
.ident
.name
== binding_name
;
292 Some((field_ident
, assign_rhs
))
299 /// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
300 fn is_update_syntax_base
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) -> bool
{
302 if let Some(parent
) = get_parent_expr(cx
, expr
);
303 if let ExprKind
::Struct(_
, _
, Some(base
)) = parent
.kind
;
305 base
.hir_id
== expr
.hir_id