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