]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/mem_replace.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / mem_replace.rs
1 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
2 use clippy_utils::msrvs::{self, Msrv};
3 use clippy_utils::source::{snippet, snippet_with_applicability};
4 use clippy_utils::sugg::Sugg;
5 use clippy_utils::ty::is_non_aggregate_primitive_type;
6 use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators};
7 use if_chain::if_chain;
8 use rustc_errors::Applicability;
9 use rustc_hir::LangItem::OptionNone;
10 use rustc_hir::{Expr, ExprKind};
11 use rustc_lint::{LateContext, LateLintPass};
12 use rustc_middle::lint::in_external_macro;
13 use rustc_session::{declare_tool_lint, impl_lint_pass};
14 use rustc_span::source_map::Span;
15 use rustc_span::symbol::sym;
16
17 declare_clippy_lint! {
18 /// ### What it does
19 /// Checks for `mem::replace()` on an `Option` with
20 /// `None`.
21 ///
22 /// ### Why is this bad?
23 /// `Option` already has the method `take()` for
24 /// taking its current value (Some(..) or None) and replacing it with
25 /// `None`.
26 ///
27 /// ### Example
28 /// ```rust
29 /// use std::mem;
30 ///
31 /// let mut an_option = Some(0);
32 /// let replaced = mem::replace(&mut an_option, None);
33 /// ```
34 /// Is better expressed with:
35 /// ```rust
36 /// let mut an_option = Some(0);
37 /// let taken = an_option.take();
38 /// ```
39 #[clippy::version = "1.31.0"]
40 pub MEM_REPLACE_OPTION_WITH_NONE,
41 style,
42 "replacing an `Option` with `None` instead of `take()`"
43 }
44
45 declare_clippy_lint! {
46 /// ### What it does
47 /// Checks for `mem::replace(&mut _, mem::uninitialized())`
48 /// and `mem::replace(&mut _, mem::zeroed())`.
49 ///
50 /// ### Why is this bad?
51 /// This will lead to undefined behavior even if the
52 /// value is overwritten later, because the uninitialized value may be
53 /// observed in the case of a panic.
54 ///
55 /// ### Example
56 /// ```
57 /// use std::mem;
58 ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
59 ///
60 /// #[allow(deprecated, invalid_value)]
61 /// fn myfunc (v: &mut Vec<i32>) {
62 /// let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
63 /// let new_v = may_panic(taken_v); // undefined behavior on panic
64 /// mem::forget(mem::replace(v, new_v));
65 /// }
66 /// ```
67 ///
68 /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
69 /// at the cost of either lazily creating a replacement value or aborting
70 /// on panic, to ensure that the uninitialized value cannot be observed.
71 #[clippy::version = "1.39.0"]
72 pub MEM_REPLACE_WITH_UNINIT,
73 correctness,
74 "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
75 }
76
77 declare_clippy_lint! {
78 /// ### What it does
79 /// Checks for `std::mem::replace` on a value of type
80 /// `T` with `T::default()`.
81 ///
82 /// ### Why is this bad?
83 /// `std::mem` module already has the method `take` to
84 /// take the current value and replace it with the default value of that type.
85 ///
86 /// ### Example
87 /// ```rust
88 /// let mut text = String::from("foo");
89 /// let replaced = std::mem::replace(&mut text, String::default());
90 /// ```
91 /// Is better expressed with:
92 /// ```rust
93 /// let mut text = String::from("foo");
94 /// let taken = std::mem::take(&mut text);
95 /// ```
96 #[clippy::version = "1.42.0"]
97 pub MEM_REPLACE_WITH_DEFAULT,
98 style,
99 "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
100 }
101
102 impl_lint_pass!(MemReplace =>
103 [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
104
105 fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_span: Span) {
106 // Since this is a late pass (already type-checked),
107 // and we already know that the second argument is an
108 // `Option`, we do not need to check the first
109 // argument's type. All that's left is to get
110 // the replacee's expr after peeling off the `&mut`
111 let sugg_expr = peel_ref_operators(cx, dest);
112 let mut applicability = Applicability::MachineApplicable;
113 span_lint_and_sugg(
114 cx,
115 MEM_REPLACE_OPTION_WITH_NONE,
116 expr_span,
117 "replacing an `Option` with `None`",
118 "consider `Option::take()` instead",
119 format!(
120 "{}.take()",
121 Sugg::hir_with_context(cx, sugg_expr, expr_span.ctxt(), "", &mut applicability).maybe_par()
122 ),
123 applicability,
124 );
125 }
126
127 fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
128 if_chain! {
129 // check if replacement is mem::MaybeUninit::uninit().assume_init()
130 if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id);
131 if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id);
132 then {
133 let mut applicability = Applicability::MachineApplicable;
134 span_lint_and_sugg(
135 cx,
136 MEM_REPLACE_WITH_UNINIT,
137 expr_span,
138 "replacing with `mem::MaybeUninit::uninit().assume_init()`",
139 "consider using",
140 format!(
141 "std::ptr::read({})",
142 snippet_with_applicability(cx, dest.span, "", &mut applicability)
143 ),
144 applicability,
145 );
146 return;
147 }
148 }
149
150 if_chain! {
151 if let ExprKind::Call(repl_func, []) = src.kind;
152 if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
153 if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
154 then {
155 if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
156 let mut applicability = Applicability::MachineApplicable;
157 span_lint_and_sugg(
158 cx,
159 MEM_REPLACE_WITH_UNINIT,
160 expr_span,
161 "replacing with `mem::uninitialized()`",
162 "consider using",
163 format!(
164 "std::ptr::read({})",
165 snippet_with_applicability(cx, dest.span, "", &mut applicability)
166 ),
167 applicability,
168 );
169 } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
170 !cx.typeck_results().expr_ty(src).is_primitive() {
171 span_lint_and_help(
172 cx,
173 MEM_REPLACE_WITH_UNINIT,
174 expr_span,
175 "replacing with `mem::zeroed()`",
176 None,
177 "consider using a default value or the `take_mut` crate instead",
178 );
179 }
180 }
181 }
182 }
183
184 fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
185 // disable lint for primitives
186 let expr_type = cx.typeck_results().expr_ty_adjusted(src);
187 if is_non_aggregate_primitive_type(expr_type) {
188 return;
189 }
190 if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
191 span_lint_and_then(
192 cx,
193 MEM_REPLACE_WITH_DEFAULT,
194 expr_span,
195 "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
196 |diag| {
197 if !expr_span.from_expansion() {
198 let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
199
200 diag.span_suggestion(
201 expr_span,
202 "consider using",
203 suggestion,
204 Applicability::MachineApplicable,
205 );
206 }
207 },
208 );
209 }
210 }
211
212 pub struct MemReplace {
213 msrv: Msrv,
214 }
215
216 impl MemReplace {
217 #[must_use]
218 pub fn new(msrv: Msrv) -> Self {
219 Self { msrv }
220 }
221 }
222
223 impl<'tcx> LateLintPass<'tcx> for MemReplace {
224 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
225 if_chain! {
226 // Check that `expr` is a call to `mem::replace()`
227 if let ExprKind::Call(func, [dest, src]) = expr.kind;
228 if let ExprKind::Path(ref func_qpath) = func.kind;
229 if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
230 if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
231 then {
232 // Check that second argument is `Option::None`
233 if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
234 check_replace_option_with_none(cx, dest, expr.span);
235 } else if self.msrv.meets(msrvs::MEM_TAKE) {
236 check_replace_with_default(cx, src, dest, expr.span);
237 }
238 check_replace_with_uninit(cx, src, dest, expr.span);
239 }
240 }
241 }
242 extract_msrv_attr!(LateContext);
243 }