]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
Update upstream source from tag 'upstream/1.73.0+dfsg1'
[rustc.git] / src / tools / clippy / clippy_lints / src / unnecessary_struct_initialization.rs
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::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8
9 declare_clippy_lint! {
10 /// ### What it does
11 /// Checks for initialization of a `struct` by copying a base without setting
12 /// any field.
13 ///
14 /// ### Why is this bad?
15 /// Readability suffers from unnecessary struct building.
16 ///
17 /// ### Example
18 /// ```rust
19 /// struct S { s: String }
20 ///
21 /// let a = S { s: String::from("Hello, world!") };
22 /// let b = S { ..a };
23 /// ```
24 /// Use instead:
25 /// ```rust
26 /// struct S { s: String }
27 ///
28 /// let a = S { s: String::from("Hello, world!") };
29 /// let b = a;
30 /// ```
31 ///
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,
37 nursery,
38 "struct built from a base that can be written mode concisely"
39 }
40 declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]);
41
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()
48 {
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.
52 return;
53 }
54
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.
57 return;
58 }
59 }
60
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))
65 {
66 // `*base` cannot be used instead of the struct in the general case if it is not Copy.
67 return;
68 }
69
70 span_lint_and_sugg(
71 cx,
72 UNNECESSARY_STRUCT_INITIALIZATION,
73 expr.span,
74 "unnecessary struct building",
75 "replace with",
76 snippet(cx, base.span, "..").into_owned(),
77 rustc_errors::Applicability::MachineApplicable,
78 );
79 }
80 }
81 }
82
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().get(hir_id)
86 {
87 matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..))
88 } else {
89 true
90 }
91 }