]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
Update upstream source from tag 'upstream/1.70.0+dfsg1'
[rustc.git] / src / tools / clippy / clippy_lints / src / unnecessary_struct_initialization.rs
CommitLineData
353b0b11
FG
1use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy};
2use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp};
3use rustc_lint::{LateContext, LateLintPass};
4use rustc_session::{declare_lint_pass, declare_tool_lint};
5
6declare_clippy_lint! {
7 /// ### What it does
8 /// Checks for initialization of a `struct` by copying a base without setting
9 /// any field.
10 ///
11 /// ### Why is this bad?
12 /// Readability suffers from unnecessary struct building.
13 ///
14 /// ### Example
15 /// ```rust
16 /// struct S { s: String }
17 ///
18 /// let a = S { s: String::from("Hello, world!") };
19 /// let b = S { ..a };
20 /// ```
21 /// Use instead:
22 /// ```rust
23 /// struct S { s: String }
24 ///
25 /// let a = S { s: String::from("Hello, world!") };
26 /// let b = a;
27 /// ```
28 ///
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,
34 nursery,
35 "struct built from a base that can be written mode concisely"
36}
37declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]);
38
39impl 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()
45 {
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.
49 return;
50 }
51
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.
54 return;
55 }
56 }
57
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))
62 {
63 // `*base` cannot be used instead of the struct in the general case if it is not Copy.
64 return;
65 }
66
67 span_lint_and_sugg(
68 cx,
69 UNNECESSARY_STRUCT_INITIALIZATION,
70 expr.span,
71 "unnecessary struct building",
72 "replace with",
73 snippet(cx, base.span, "..").into_owned(),
74 rustc_errors::Applicability::MachineApplicable,
75 );
76 }
77 }
78}
79
80fn 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)
83 {
84 matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..))
85 } else {
86 true
87 }
88}