1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::source
::snippet
;
3 use clippy_utils
::ty
::is_copy
;
4 use clippy_utils
::{get_parent_expr, path_to_local}
;
5 use rustc_hir
::{BindingMode, Expr, ExprKind, Node, PatKind, UnOp}
;
6 use rustc_lint
::{LateContext, LateLintPass}
;
7 use rustc_session
::declare_lint_pass
;
11 /// Checks for initialization of a `struct` by copying a base without setting
14 /// ### Why is this bad?
15 /// Readability suffers from unnecessary struct building.
19 /// struct S { s: String }
21 /// let a = S { s: String::from("Hello, world!") };
22 /// let b = S { ..a };
26 /// struct S { s: String }
28 /// let a = S { s: String::from("Hello, world!") };
32 /// ### Known Problems
33 /// Has false positives when the base is a place expression that cannot be
34 /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547).
35 #[clippy::version = "1.70.0"]
36 pub UNNECESSARY_STRUCT_INITIALIZATION
,
38 "struct built from a base that can be written mode concisely"
40 declare_lint_pass
!(UnnecessaryStruct
=> [UNNECESSARY_STRUCT_INITIALIZATION
]);
42 impl LateLintPass
<'_
> for UnnecessaryStruct
{
43 fn check_expr(&mut self, cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) {
44 if let ExprKind
::Struct(_
, &[], Some(base
)) = expr
.kind
{
45 if let Some(parent
) = get_parent_expr(cx
, expr
)
46 && let parent_ty
= cx
.typeck_results().expr_ty_adjusted(parent
)
47 && parent_ty
.is_any_ptr()
49 if is_copy(cx
, cx
.typeck_results().expr_ty(expr
)) && path_to_local(base
).is_some() {
50 // When the type implements `Copy`, a reference to the new struct works on the
51 // copy. Using the original would borrow it.
55 if parent_ty
.is_mutable_ptr() && !is_mutable(cx
, base
) {
56 // The original can be used in a mutable reference context only if it is mutable.
61 // TODO: do not propose to replace *XX if XX is not Copy
62 if let ExprKind
::Unary(UnOp
::Deref
, target
) = base
.kind
63 && matches
!(target
.kind
, ExprKind
::Path(..))
64 && !is_copy(cx
, cx
.typeck_results().expr_ty(expr
))
66 // `*base` cannot be used instead of the struct in the general case if it is not Copy.
72 UNNECESSARY_STRUCT_INITIALIZATION
,
74 "unnecessary struct building",
76 snippet(cx
, base
.span
, "..").into_owned(),
77 rustc_errors
::Applicability
::MachineApplicable
,
83 fn is_mutable(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
84 if let Some(hir_id
) = path_to_local(expr
)
85 && let Node
::Pat(pat
) = cx
.tcx
.hir_node(hir_id
)
87 matches
!(pat
.kind
, PatKind
::Binding(BindingMode
::MUT
, ..))