1 use clippy_utils
::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy}
;
2 use rustc_hir
::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp}
;
3 use rustc_lint
::{LateContext, LateLintPass}
;
4 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
8 /// Checks for initialization of a `struct` by copying a base without setting
11 /// ### Why is this bad?
12 /// Readability suffers from unnecessary struct building.
16 /// struct S { s: String }
18 /// let a = S { s: String::from("Hello, world!") };
19 /// let b = S { ..a };
23 /// struct S { s: String }
25 /// let a = S { s: String::from("Hello, world!") };
29 /// ### Known Problems
30 /// Has false positives when the base is a place expression that cannot be
31 /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547).
32 #[clippy::version = "1.70.0"]
33 pub UNNECESSARY_STRUCT_INITIALIZATION
,
35 "struct built from a base that can be written mode concisely"
37 declare_lint_pass
!(UnnecessaryStruct
=> [UNNECESSARY_STRUCT_INITIALIZATION
]);
39 impl LateLintPass
<'_
> for UnnecessaryStruct
{
40 fn check_expr(&mut self, cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) {
41 if let ExprKind
::Struct(_
, &[], Some(base
)) = expr
.kind
{
42 if let Some(parent
) = get_parent_expr(cx
, expr
) &&
43 let parent_ty
= cx
.typeck_results().expr_ty_adjusted(parent
) &&
44 parent_ty
.is_any_ptr()
46 if is_copy(cx
, cx
.typeck_results().expr_ty(expr
)) && path_to_local(base
).is_some() {
47 // When the type implements `Copy`, a reference to the new struct works on the
48 // copy. Using the original would borrow it.
52 if parent_ty
.is_mutable_ptr() && !is_mutable(cx
, base
) {
53 // The original can be used in a mutable reference context only if it is mutable.
58 // TODO: do not propose to replace *XX if XX is not Copy
59 if let ExprKind
::Unary(UnOp
::Deref
, target
) = base
.kind
&&
60 matches
!(target
.kind
, ExprKind
::Path(..)) &&
61 !is_copy(cx
, cx
.typeck_results().expr_ty(expr
))
63 // `*base` cannot be used instead of the struct in the general case if it is not Copy.
69 UNNECESSARY_STRUCT_INITIALIZATION
,
71 "unnecessary struct building",
73 snippet(cx
, base
.span
, "..").into_owned(),
74 rustc_errors
::Applicability
::MachineApplicable
,
80 fn is_mutable(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
81 if let Some(hir_id
) = path_to_local(expr
) &&
82 let Node
::Pat(pat
) = cx
.tcx
.hir().get(hir_id
)
84 matches
!(pat
.kind
, PatKind
::Binding(BindingAnnotation
::MUT
, ..))