Skip to content

Commit d2a6ec2

Browse files
committed
take into account reborrowing when inserting &mut in sugg
1 parent 2748ab9 commit d2a6ec2

File tree

4 files changed

+132
-29
lines changed

4 files changed

+132
-29
lines changed

clippy_lints/src/methods/drain_collect.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_hir::Path;
1111
use rustc_hir::QPath;
1212
use rustc_lint::LateContext;
1313
use rustc_middle::query::Key;
14-
use rustc_middle::ty::Ty;
14+
use rustc_middle::ty;
1515
use rustc_span::sym;
1616
use rustc_span::Symbol;
1717

@@ -21,7 +21,7 @@ use rustc_span::Symbol;
2121
/// ^^^^^^^^^ ^^^^^^ true
2222
/// `vec![1,2].drain(..).collect::<HashSet<_>>()`
2323
/// ^^^^^^^^^ ^^^^^^^^^^ false
24-
fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool {
24+
fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: ty::Ty<'_>, recv: ty::Ty<'_>, sym: Symbol) -> bool {
2525
if let Some(expr_adt_did) = expr.ty_adt_id()
2626
&& let Some(recv_adt_did) = recv.ty_adt_id()
2727
{
@@ -32,21 +32,33 @@ fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>,
3232
}
3333

3434
/// Checks `std::{vec::Vec, collections::VecDeque}`.
35-
fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool {
35+
fn check_vec(
36+
cx: &LateContext<'_>,
37+
args: &[Expr<'_>],
38+
expr: ty::Ty<'_>,
39+
recv: ty::Ty<'_>,
40+
recv_path: &Path<'_>,
41+
) -> bool {
3642
(types_match_diagnostic_item(cx, expr, recv, sym::Vec)
3743
|| types_match_diagnostic_item(cx, expr, recv, sym::VecDeque))
3844
&& matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path)))
3945
}
4046

4147
/// Checks `std::string::String`
42-
fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool {
48+
fn check_string(
49+
cx: &LateContext<'_>,
50+
args: &[Expr<'_>],
51+
expr: ty::Ty<'_>,
52+
recv: ty::Ty<'_>,
53+
recv_path: &Path<'_>,
54+
) -> bool {
4355
is_type_lang_item(cx, expr, LangItem::String)
4456
&& is_type_lang_item(cx, recv, LangItem::String)
4557
&& matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path)))
4658
}
4759

4860
/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`.
49-
fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> {
61+
fn check_collections(cx: &LateContext<'_>, expr: ty::Ty<'_>, recv: ty::Ty<'_>) -> Option<&'static str> {
5062
types_match_diagnostic_item(cx, expr, recv, sym::HashSet)
5163
.then_some("HashSet")
5264
.or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap"))
@@ -55,13 +67,14 @@ fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option
5567

5668
pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) {
5769
let expr_ty = cx.typeck_results().expr_ty(expr);
58-
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
70+
let recv_ty = cx.typeck_results().expr_ty(recv);
71+
let recv_ty_no_refs = recv_ty.peel_refs();
5972

6073
if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind
61-
&& let Some(typename) = check_vec(cx, args, expr_ty, recv_ty, recv_path)
74+
&& let Some(typename) = check_vec(cx, args, expr_ty, recv_ty_no_refs, recv_path)
6275
.then_some("Vec")
63-
.or_else(|| check_string(cx, args, expr_ty, recv_ty, recv_path).then_some("String"))
64-
.or_else(|| check_collections(cx, expr_ty, recv_ty))
76+
.or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
77+
.or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs))
6578
{
6679
let recv = snippet(cx, recv.span, "<expr>");
6780
span_lint_and_sugg(
@@ -70,7 +83,10 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re
7083
expr.span,
7184
&format!("you seem to be trying to move all elements into a new `{typename}`"),
7285
"consider using `mem::take`",
73-
format!("std::mem::take(&mut {recv})"),
86+
match recv_ty.kind() {
87+
ty::Ref(..) => format!("std::mem::take({recv})"),
88+
_ => format!("std::mem::take(&mut {recv})"),
89+
},
7490
Applicability::MachineApplicable,
7591
);
7692
}

tests/ui/drain_collect.fixed

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//@run-rustfix
2+
3+
#![deny(clippy::drain_collect)]
4+
#![allow(dead_code)]
5+
6+
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
7+
8+
fn binaryheap(b: &mut BinaryHeap<i32>) -> BinaryHeap<i32> {
9+
std::mem::take(b)
10+
}
11+
12+
fn binaryheap_dont_lint(b: &mut BinaryHeap<i32>) -> HashSet<i32> {
13+
b.drain().collect()
14+
}
15+
16+
fn hashmap(b: &mut HashMap<i32, i32>) -> HashMap<i32, i32> {
17+
std::mem::take(b)
18+
}
19+
20+
fn hashmap_dont_lint(b: &mut HashMap<i32, i32>) -> Vec<(i32, i32)> {
21+
b.drain().collect()
22+
}
23+
24+
fn hashset(b: &mut HashSet<i32>) -> HashSet<i32> {
25+
std::mem::take(b)
26+
}
27+
28+
fn hashset_dont_lint(b: &mut HashSet<i32>) -> Vec<i32> {
29+
b.drain().collect()
30+
}
31+
32+
fn vecdeque(b: &mut VecDeque<i32>) -> VecDeque<i32> {
33+
std::mem::take(b)
34+
}
35+
36+
fn vecdeque_dont_lint(b: &mut VecDeque<i32>) -> HashSet<i32> {
37+
b.drain(..).collect()
38+
}
39+
40+
fn vec(b: &mut Vec<i32>) -> Vec<i32> {
41+
std::mem::take(b)
42+
}
43+
44+
fn vec2(b: &mut Vec<i32>) -> Vec<i32> {
45+
std::mem::take(b)
46+
}
47+
48+
fn vec3(b: &mut Vec<i32>) -> Vec<i32> {
49+
std::mem::take(b)
50+
}
51+
52+
fn vec4(b: &mut Vec<i32>) -> Vec<i32> {
53+
std::mem::take(b)
54+
}
55+
56+
fn vec_no_reborrow() -> Vec<i32> {
57+
let mut b = vec![1, 2, 3];
58+
std::mem::take(&mut b)
59+
}
60+
61+
fn vec_dont_lint(b: &mut Vec<i32>) -> HashSet<i32> {
62+
b.drain(..).collect()
63+
}
64+
65+
fn string(b: &mut String) -> String {
66+
std::mem::take(b)
67+
}
68+
69+
fn string_dont_lint(b: &mut String) -> HashSet<char> {
70+
b.drain(..).collect()
71+
}
72+
73+
fn main() {}

tests/ui/drain_collect.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
//@run-rustfix
2+
13
#![deny(clippy::drain_collect)]
4+
#![allow(dead_code)]
25

36
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
47

@@ -50,6 +53,11 @@ fn vec4(b: &mut Vec<i32>) -> Vec<i32> {
5053
b.drain(0..b.len()).collect()
5154
}
5255

56+
fn vec_no_reborrow() -> Vec<i32> {
57+
let mut b = vec![1, 2, 3];
58+
b.drain(..).collect()
59+
}
60+
5361
fn vec_dont_lint(b: &mut Vec<i32>) -> HashSet<i32> {
5462
b.drain(..).collect()
5563
}

tests/ui/drain_collect.stderr

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,68 @@
11
error: you seem to be trying to move all elements into a new `BinaryHeap`
2-
--> $DIR/drain_collect.rs:6:5
2+
--> $DIR/drain_collect.rs:9:5
33
|
44
LL | b.drain().collect()
5-
| ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
5+
| ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
66
|
77
note: the lint level is defined here
8-
--> $DIR/drain_collect.rs:1:9
8+
--> $DIR/drain_collect.rs:3:9
99
|
1010
LL | #![deny(clippy::drain_collect)]
1111
| ^^^^^^^^^^^^^^^^^^^^^
1212

1313
error: you seem to be trying to move all elements into a new `HashMap`
14-
--> $DIR/drain_collect.rs:14:5
14+
--> $DIR/drain_collect.rs:17:5
1515
|
1616
LL | b.drain().collect()
17-
| ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
17+
| ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
1818

1919
error: you seem to be trying to move all elements into a new `HashSet`
20-
--> $DIR/drain_collect.rs:22:5
20+
--> $DIR/drain_collect.rs:25:5
2121
|
2222
LL | b.drain().collect()
23-
| ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
23+
| ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
2424

2525
error: you seem to be trying to move all elements into a new `Vec`
26-
--> $DIR/drain_collect.rs:30:5
26+
--> $DIR/drain_collect.rs:33:5
2727
|
2828
LL | b.drain(..).collect()
29-
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
29+
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
3030

3131
error: you seem to be trying to move all elements into a new `Vec`
32-
--> $DIR/drain_collect.rs:38:5
32+
--> $DIR/drain_collect.rs:41:5
3333
|
3434
LL | b.drain(..).collect()
35-
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
35+
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
3636

3737
error: you seem to be trying to move all elements into a new `Vec`
38-
--> $DIR/drain_collect.rs:42:5
38+
--> $DIR/drain_collect.rs:45:5
3939
|
4040
LL | b.drain(0..).collect()
41-
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
41+
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
4242

4343
error: you seem to be trying to move all elements into a new `Vec`
44-
--> $DIR/drain_collect.rs:46:5
44+
--> $DIR/drain_collect.rs:49:5
4545
|
4646
LL | b.drain(..b.len()).collect()
47-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
47+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
4848

4949
error: you seem to be trying to move all elements into a new `Vec`
50-
--> $DIR/drain_collect.rs:50:5
50+
--> $DIR/drain_collect.rs:53:5
5151
|
5252
LL | b.drain(0..b.len()).collect()
53-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
5454

55-
error: you seem to be trying to move all elements into a new `String`
55+
error: you seem to be trying to move all elements into a new `Vec`
5656
--> $DIR/drain_collect.rs:58:5
5757
|
5858
LL | b.drain(..).collect()
5959
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)`
6060

61-
error: aborting due to 9 previous errors
61+
error: you seem to be trying to move all elements into a new `String`
62+
--> $DIR/drain_collect.rs:66:5
63+
|
64+
LL | b.drain(..).collect()
65+
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)`
66+
67+
error: aborting due to 10 previous errors
6268

0 commit comments

Comments
 (0)