Skip to content

Commit fa9b85d

Browse files
committed
Auto merge of #4881 - krishna-veerareddy:issue-4871-use-mem-take, r=flip1995
Use `mem::take` instead of `mem::replace` when applicable `std::mem::take` can be used to replace a value of type `T` with `T::default()` instead of `std::mem::replace`. Fixes issue #4871 changelog: Added lint for [`mem_replace_with_default`]
2 parents 611bd89 + 42e4595 commit fa9b85d

11 files changed

+249
-74
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,7 @@ Released 2018-09-13
11731173
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
11741174
[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
11751175
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
1176+
[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
11761177
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
11771178
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
11781179
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 342 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 343 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
599599
&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
600600
&mem_forget::MEM_FORGET,
601601
&mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
602+
&mem_replace::MEM_REPLACE_WITH_DEFAULT,
602603
&mem_replace::MEM_REPLACE_WITH_UNINIT,
603604
&methods::CHARS_LAST_CMP,
604605
&methods::CHARS_NEXT_CMP,
@@ -1182,6 +1183,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
11821183
LintId::of(&matches::SINGLE_MATCH),
11831184
LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
11841185
LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
1186+
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
11851187
LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT),
11861188
LintId::of(&methods::CHARS_LAST_CMP),
11871189
LintId::of(&methods::CHARS_NEXT_CMP),
@@ -1369,6 +1371,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
13691371
LintId::of(&matches::MATCH_WILD_ERR_ARM),
13701372
LintId::of(&matches::SINGLE_MATCH),
13711373
LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
1374+
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
13721375
LintId::of(&methods::CHARS_LAST_CMP),
13731376
LintId::of(&methods::INTO_ITER_ON_REF),
13741377
LintId::of(&methods::ITER_CLONED_COLLECT),

clippy_lints/src/mem_replace.rs

+132-65
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use crate::utils::{
2-
match_def_path, match_qpath, paths, snippet_with_applicability, span_help_and_lint, span_lint_and_sugg,
2+
in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_help_and_lint,
3+
span_lint_and_sugg, span_lint_and_then,
34
};
45
use if_chain::if_chain;
56
use rustc::declare_lint_pass;
67
use rustc::hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
7-
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
8+
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintPass};
89
use rustc_errors::Applicability;
910
use rustc_session::declare_tool_lint;
11+
use syntax::source_map::Span;
1012

1113
declare_clippy_lint! {
1214
/// **What it does:** Checks for `mem::replace()` on an `Option` with
@@ -67,80 +69,145 @@ declare_clippy_lint! {
6769
"`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
6870
}
6971

72+
declare_clippy_lint! {
73+
/// **What it does:** Checks for `std::mem::replace` on a value of type
74+
/// `T` with `T::default()`.
75+
///
76+
/// **Why is this bad?** `std::mem` module already has the method `take` to
77+
/// take the current value and replace it with the default value of that type.
78+
///
79+
/// **Known problems:** None.
80+
///
81+
/// **Example:**
82+
/// ```rust
83+
/// let mut text = String::from("foo");
84+
/// let replaced = std::mem::replace(&mut text, String::default());
85+
/// ```
86+
/// Is better expressed with:
87+
/// ```rust
88+
/// let mut text = String::from("foo");
89+
/// let taken = std::mem::take(&mut text);
90+
/// ```
91+
pub MEM_REPLACE_WITH_DEFAULT,
92+
style,
93+
"replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
94+
}
95+
7096
declare_lint_pass!(MemReplace =>
71-
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT]);
97+
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
98+
99+
fn check_replace_option_with_none(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
100+
if let ExprKind::Path(ref replacement_qpath) = src.kind {
101+
// Check that second argument is `Option::None`
102+
if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
103+
// Since this is a late pass (already type-checked),
104+
// and we already know that the second argument is an
105+
// `Option`, we do not need to check the first
106+
// argument's type. All that's left is to get
107+
// replacee's path.
108+
let replaced_path = match dest.kind {
109+
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => {
110+
if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind {
111+
replaced_path
112+
} else {
113+
return;
114+
}
115+
},
116+
ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
117+
_ => return,
118+
};
119+
120+
let mut applicability = Applicability::MachineApplicable;
121+
span_lint_and_sugg(
122+
cx,
123+
MEM_REPLACE_OPTION_WITH_NONE,
124+
expr_span,
125+
"replacing an `Option` with `None`",
126+
"consider `Option::take()` instead",
127+
format!(
128+
"{}.take()",
129+
snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
130+
),
131+
applicability,
132+
);
133+
}
134+
}
135+
}
136+
137+
fn check_replace_with_uninit(cx: &LateContext<'_, '_>, src: &Expr<'_>, expr_span: Span) {
138+
if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind {
139+
if_chain! {
140+
if repl_args.is_empty();
141+
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
142+
if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
143+
then {
144+
if match_def_path(cx, repl_def_id, &paths::MEM_UNINITIALIZED) {
145+
span_help_and_lint(
146+
cx,
147+
MEM_REPLACE_WITH_UNINIT,
148+
expr_span,
149+
"replacing with `mem::uninitialized()`",
150+
"consider using the `take_mut` crate instead",
151+
);
152+
} else if match_def_path(cx, repl_def_id, &paths::MEM_ZEROED) &&
153+
!cx.tables.expr_ty(src).is_primitive() {
154+
span_help_and_lint(
155+
cx,
156+
MEM_REPLACE_WITH_UNINIT,
157+
expr_span,
158+
"replacing with `mem::zeroed()`",
159+
"consider using a default value or the `take_mut` crate instead",
160+
);
161+
}
162+
}
163+
}
164+
}
165+
}
166+
167+
fn check_replace_with_default(cx: &LateContext<'_, '_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
168+
if let ExprKind::Call(ref repl_func, _) = src.kind {
169+
if_chain! {
170+
if !in_external_macro(cx.tcx.sess, expr_span);
171+
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
172+
if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
173+
if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
174+
then {
175+
span_lint_and_then(
176+
cx,
177+
MEM_REPLACE_WITH_DEFAULT,
178+
expr_span,
179+
"replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
180+
|db| {
181+
if !in_macro(expr_span) {
182+
let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
183+
184+
db.span_suggestion(
185+
expr_span,
186+
"consider using",
187+
suggestion,
188+
Applicability::MachineApplicable
189+
);
190+
}
191+
}
192+
);
193+
}
194+
}
195+
}
196+
}
72197

73198
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
74199
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
75200
if_chain! {
76201
// Check that `expr` is a call to `mem::replace()`
77202
if let ExprKind::Call(ref func, ref func_args) = expr.kind;
78-
if func_args.len() == 2;
79203
if let ExprKind::Path(ref func_qpath) = func.kind;
80204
if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
81205
if match_def_path(cx, def_id, &paths::MEM_REPLACE);
82-
83-
// Check that second argument is `Option::None`
206+
if let [dest, src] = &**func_args;
84207
then {
85-
if let ExprKind::Path(ref replacement_qpath) = func_args[1].kind {
86-
if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
87-
88-
// Since this is a late pass (already type-checked),
89-
// and we already know that the second argument is an
90-
// `Option`, we do not need to check the first
91-
// argument's type. All that's left is to get
92-
// replacee's path.
93-
let replaced_path = match func_args[0].kind {
94-
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => {
95-
if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind {
96-
replaced_path
97-
} else {
98-
return
99-
}
100-
},
101-
ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
102-
_ => return,
103-
};
104-
105-
let mut applicability = Applicability::MachineApplicable;
106-
span_lint_and_sugg(
107-
cx,
108-
MEM_REPLACE_OPTION_WITH_NONE,
109-
expr.span,
110-
"replacing an `Option` with `None`",
111-
"consider `Option::take()` instead",
112-
format!("{}.take()", snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)),
113-
applicability,
114-
);
115-
}
116-
}
117-
if let ExprKind::Call(ref repl_func, ref repl_args) = func_args[1].kind {
118-
if_chain! {
119-
if repl_args.is_empty();
120-
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
121-
if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
122-
then {
123-
if match_def_path(cx, repl_def_id, &paths::MEM_UNINITIALIZED) {
124-
span_help_and_lint(
125-
cx,
126-
MEM_REPLACE_WITH_UNINIT,
127-
expr.span,
128-
"replacing with `mem::uninitialized()`",
129-
"consider using the `take_mut` crate instead",
130-
);
131-
} else if match_def_path(cx, repl_def_id, &paths::MEM_ZEROED) &&
132-
!cx.tables.expr_ty(&func_args[1]).is_primitive() {
133-
span_help_and_lint(
134-
cx,
135-
MEM_REPLACE_WITH_UNINIT,
136-
expr.span,
137-
"replacing with `mem::zeroed()`",
138-
"consider using a default value or the `take_mut` crate instead",
139-
);
140-
}
141-
}
142-
}
143-
}
208+
check_replace_option_with_none(cx, src, dest, expr.span);
209+
check_replace_with_uninit(cx, src, expr.span);
210+
check_replace_with_default(cx, src, dest, expr.span);
144211
}
145212
}
146213
}

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 342] = [
9+
pub const ALL_LINTS: [Lint; 343] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1099,6 +1099,13 @@ pub const ALL_LINTS: [Lint; 342] = [
10991099
deprecation: None,
11001100
module: "mem_replace",
11011101
},
1102+
Lint {
1103+
name: "mem_replace_with_default",
1104+
group: "style",
1105+
desc: "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`",
1106+
deprecation: None,
1107+
module: "mem_replace",
1108+
},
11021109
Lint {
11031110
name: "mem_replace_with_uninit",
11041111
group: "correctness",

tests/ui/auxiliary/macro_rules.rs

+7
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,10 @@ macro_rules! string_add {
3939
let z = y + "...";
4040
};
4141
}
42+
43+
#[macro_export]
44+
macro_rules! take_external {
45+
($s:expr) => {
46+
std::mem::replace($s, Default::default())
47+
};
48+
}

tests/ui/mem_replace.fixed

+20-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,31 @@
99

1010
// run-rustfix
1111
#![allow(unused_imports)]
12-
#![warn(clippy::all, clippy::style, clippy::mem_replace_option_with_none)]
12+
#![warn(
13+
clippy::all,
14+
clippy::style,
15+
clippy::mem_replace_option_with_none,
16+
clippy::mem_replace_with_default
17+
)]
1318

1419
use std::mem;
1520

16-
fn main() {
21+
fn replace_option_with_none() {
1722
let mut an_option = Some(1);
1823
let _ = an_option.take();
1924
let an_option = &mut Some(1);
2025
let _ = an_option.take();
2126
}
27+
28+
fn replace_with_default() {
29+
let mut s = String::from("foo");
30+
let _ = std::mem::take(&mut s);
31+
let s = &mut String::from("foo");
32+
let _ = std::mem::take(s);
33+
let _ = std::mem::take(s);
34+
}
35+
36+
fn main() {
37+
replace_option_with_none();
38+
replace_with_default();
39+
}

tests/ui/mem_replace.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,31 @@
99

1010
// run-rustfix
1111
#![allow(unused_imports)]
12-
#![warn(clippy::all, clippy::style, clippy::mem_replace_option_with_none)]
12+
#![warn(
13+
clippy::all,
14+
clippy::style,
15+
clippy::mem_replace_option_with_none,
16+
clippy::mem_replace_with_default
17+
)]
1318

1419
use std::mem;
1520

16-
fn main() {
21+
fn replace_option_with_none() {
1722
let mut an_option = Some(1);
1823
let _ = mem::replace(&mut an_option, None);
1924
let an_option = &mut Some(1);
2025
let _ = mem::replace(an_option, None);
2126
}
27+
28+
fn replace_with_default() {
29+
let mut s = String::from("foo");
30+
let _ = std::mem::replace(&mut s, String::default());
31+
let s = &mut String::from("foo");
32+
let _ = std::mem::replace(s, String::default());
33+
let _ = std::mem::replace(s, Default::default());
34+
}
35+
36+
fn main() {
37+
replace_option_with_none();
38+
replace_with_default();
39+
}

0 commit comments

Comments
 (0)