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
;
17 declare_clippy_lint
! {
19 /// Checks for `mem::replace()` on an `Option` with
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
31 /// let mut an_option = Some(0);
32 /// let replaced = mem::replace(&mut an_option, None);
34 /// Is better expressed with:
36 /// let mut an_option = Some(0);
37 /// let taken = an_option.take();
39 #[clippy::version = "1.31.0"]
40 pub MEM_REPLACE_OPTION_WITH_NONE
,
42 "replacing an `Option` with `None` instead of `take()`"
45 declare_clippy_lint
! {
47 /// Checks for `mem::replace(&mut _, mem::uninitialized())`
48 /// and `mem::replace(&mut _, mem::zeroed())`.
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.
58 ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
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));
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
,
74 "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
77 declare_clippy_lint
! {
79 /// Checks for `std::mem::replace` on a value of type
80 /// `T` with `T::default()`.
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.
88 /// let mut text = String::from("foo");
89 /// let replaced = std::mem::replace(&mut text, String::default());
91 /// Is better expressed with:
93 /// let mut text = String::from("foo");
94 /// let taken = std::mem::take(&mut text);
96 #[clippy::version = "1.42.0"]
97 pub MEM_REPLACE_WITH_DEFAULT
,
99 "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
102 impl_lint_pass
!(MemReplace
=>
103 [MEM_REPLACE_OPTION_WITH_NONE
, MEM_REPLACE_WITH_UNINIT
, MEM_REPLACE_WITH_DEFAULT
]);
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
;
115 MEM_REPLACE_OPTION_WITH_NONE
,
117 "replacing an `Option` with `None`",
118 "consider `Option::take()` instead",
121 Sugg
::hir_with_context(cx
, sugg_expr
, expr_span
.ctxt(), "", &mut applicability
).maybe_par()
127 fn check_replace_with_uninit(cx
: &LateContext
<'_
>, src
: &Expr
<'_
>, dest
: &Expr
<'_
>, expr_span
: Span
) {
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
);
133 let mut applicability
= Applicability
::MachineApplicable
;
136 MEM_REPLACE_WITH_UNINIT
,
138 "replacing with `mem::MaybeUninit::uninit().assume_init()`",
141 "std::ptr::read({})",
142 snippet_with_applicability(cx
, dest
.span
, "", &mut applicability
)
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();
155 if cx
.tcx
.is_diagnostic_item(sym
::mem_uninitialized
, repl_def_id
) {
156 let mut applicability
= Applicability
::MachineApplicable
;
159 MEM_REPLACE_WITH_UNINIT
,
161 "replacing with `mem::uninitialized()`",
164 "std::ptr::read({})",
165 snippet_with_applicability(cx
, dest
.span
, "", &mut applicability
)
169 } else if cx
.tcx
.is_diagnostic_item(sym
::mem_zeroed
, repl_def_id
) &&
170 !cx
.typeck_results().expr_ty(src
).is_primitive() {
173 MEM_REPLACE_WITH_UNINIT
,
175 "replacing with `mem::zeroed()`",
177 "consider using a default value or the `take_mut` crate instead",
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
) {
190 if is_default_equivalent(cx
, src
) && !in_external_macro(cx
.tcx
.sess
, expr_span
) {
193 MEM_REPLACE_WITH_DEFAULT
,
195 "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
197 if !expr_span
.from_expansion() {
198 let suggestion
= format
!("std::mem::take({})", snippet(cx
, dest
.span
, ""));
200 diag
.span_suggestion(
204 Applicability
::MachineApplicable
,
212 pub struct MemReplace
{
218 pub fn new(msrv
: Msrv
) -> Self {
223 impl<'tcx
> LateLintPass
<'tcx
> for MemReplace
{
224 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
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
);
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
);
238 check_replace_with_uninit(cx
, src
, dest
, expr
.span
);
242 extract_msrv_attr
!(LateContext
);