Skip to content

Commit 8b54f1e

Browse files
committed
Auto merge of #6016 - giraffate:restrict_same_item_push, r=ebroto
Restrict `same_item_push` to suppress false positives It only emits a lint when the pushed item is a literal, a constant and an immutable binding that are initialized with those, as discussed in #5997 (review). Fix #5985 changelog: Restrict `same_item_push` to literals, constants and immutable bindings that are initialized with those. r? `@ebroto`
2 parents 8d6cf3a + 1d8ae3a commit 8b54f1e

File tree

3 files changed

+150
-86
lines changed

3 files changed

+150
-86
lines changed

clippy_lints/src/loops.rs

+55-36
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,27 @@ fn detect_same_item_push<'tcx>(
11311131
body: &'tcx Expr<'_>,
11321132
_: &'tcx Expr<'_>,
11331133
) {
1134+
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
1135+
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
1136+
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
1137+
1138+
span_lint_and_help(
1139+
cx,
1140+
SAME_ITEM_PUSH,
1141+
vec.span,
1142+
"it looks like the same item is being pushed into this Vec",
1143+
None,
1144+
&format!(
1145+
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
1146+
item_str, vec_str, item_str
1147+
),
1148+
)
1149+
}
1150+
1151+
if !matches!(pat.kind, PatKind::Wild) {
1152+
return;
1153+
}
1154+
11341155
// Determine whether it is safe to lint the body
11351156
let mut same_item_push_visitor = SameItemPushVisitor {
11361157
should_lint: true,
@@ -1149,43 +1170,41 @@ fn detect_same_item_push<'tcx>(
11491170
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
11501171
{
11511172
// Make sure that the push does not involve possibly mutating values
1152-
if let PatKind::Wild = pat.kind {
1153-
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
1154-
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
1155-
if let ExprKind::Path(ref qpath) = pushed_item.kind {
1156-
if_chain! {
1157-
if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id);
1158-
let node = cx.tcx.hir().get(hir_id);
1159-
if let Node::Binding(pat) = node;
1160-
if let PatKind::Binding(bind_ann, ..) = pat.kind;
1161-
if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
1162-
then {
1163-
span_lint_and_help(
1164-
cx,
1165-
SAME_ITEM_PUSH,
1166-
vec.span,
1167-
"it looks like the same item is being pushed into this Vec",
1168-
None,
1169-
&format!(
1170-
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
1171-
item_str, vec_str, item_str
1172-
),
1173-
)
1174-
}
1173+
match pushed_item.kind {
1174+
ExprKind::Path(ref qpath) => {
1175+
match qpath_res(cx, qpath, pushed_item.hir_id) {
1176+
// immutable bindings that are initialized with literal or constant
1177+
Res::Local(hir_id) => {
1178+
if_chain! {
1179+
let node = cx.tcx.hir().get(hir_id);
1180+
if let Node::Binding(pat) = node;
1181+
if let PatKind::Binding(bind_ann, ..) = pat.kind;
1182+
if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
1183+
let parent_node = cx.tcx.hir().get_parent_node(hir_id);
1184+
if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
1185+
if let Some(init) = parent_let_expr.init;
1186+
then {
1187+
match init.kind {
1188+
// immutable bindings that are initialized with literal
1189+
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
1190+
// immutable bindings that are initialized with constant
1191+
ExprKind::Path(ref path) => {
1192+
if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) {
1193+
emit_lint(cx, vec, pushed_item);
1194+
}
1195+
}
1196+
_ => {},
1197+
}
1198+
}
1199+
}
1200+
},
1201+
// constant
1202+
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
1203+
_ => {},
11751204
}
1176-
} else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
1177-
span_lint_and_help(
1178-
cx,
1179-
SAME_ITEM_PUSH,
1180-
vec.span,
1181-
"it looks like the same item is being pushed into this Vec",
1182-
None,
1183-
&format!(
1184-
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
1185-
item_str, vec_str, item_str
1186-
),
1187-
)
1188-
}
1205+
},
1206+
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
1207+
_ => {},
11891208
}
11901209
}
11911210
}

tests/ui/same_item_push.rs

+70-33
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![warn(clippy::same_item_push)]
22

3+
const VALUE: u8 = 7;
4+
35
fn mutate_increment(x: &mut u8) -> u8 {
46
*x += 1;
57
*x
@@ -9,65 +11,81 @@ fn increment(x: u8) -> u8 {
911
x + 1
1012
}
1113

12-
fn main() {
13-
// Test for basic case
14-
let mut spaces = Vec::with_capacity(10);
15-
for _ in 0..10 {
16-
spaces.push(vec![b' ']);
17-
}
14+
fn fun() -> usize {
15+
42
16+
}
1817

19-
let mut vec2: Vec<u8> = Vec::new();
18+
fn main() {
19+
// ** linted cases **
20+
let mut vec: Vec<u8> = Vec::new();
2021
let item = 2;
2122
for _ in 5..=20 {
22-
vec2.push(item);
23+
vec.push(item);
2324
}
2425

25-
let mut vec3: Vec<u8> = Vec::new();
26+
let mut vec: Vec<u8> = Vec::new();
2627
for _ in 0..15 {
2728
let item = 2;
28-
vec3.push(item);
29+
vec.push(item);
2930
}
3031

31-
let mut vec4: Vec<u8> = Vec::new();
32+
let mut vec: Vec<u8> = Vec::new();
3233
for _ in 0..15 {
33-
vec4.push(13);
34+
vec.push(13);
35+
}
36+
37+
let mut vec = Vec::new();
38+
for _ in 0..20 {
39+
vec.push(VALUE);
40+
}
41+
42+
let mut vec = Vec::new();
43+
let item = VALUE;
44+
for _ in 0..20 {
45+
vec.push(item);
46+
}
47+
48+
// ** non-linted cases **
49+
let mut spaces = Vec::with_capacity(10);
50+
for _ in 0..10 {
51+
spaces.push(vec![b' ']);
3452
}
3553

3654
// Suggestion should not be given as pushed variable can mutate
37-
let mut vec5: Vec<u8> = Vec::new();
55+
let mut vec: Vec<u8> = Vec::new();
3856
let mut item: u8 = 2;
3957
for _ in 0..30 {
40-
vec5.push(mutate_increment(&mut item));
58+
vec.push(mutate_increment(&mut item));
4159
}
4260

43-
let mut vec6: Vec<u8> = Vec::new();
61+
let mut vec: Vec<u8> = Vec::new();
4462
let mut item: u8 = 2;
4563
let mut item2 = &mut mutate_increment(&mut item);
4664
for _ in 0..30 {
47-
vec6.push(mutate_increment(item2));
65+
vec.push(mutate_increment(item2));
4866
}
4967

50-
let mut vec7: Vec<usize> = Vec::new();
68+
let mut vec: Vec<usize> = Vec::new();
5169
for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
52-
vec7.push(a);
70+
vec.push(a);
5371
}
5472

55-
let mut vec8: Vec<u8> = Vec::new();
73+
let mut vec: Vec<u8> = Vec::new();
5674
for i in 0..30 {
57-
vec8.push(increment(i));
75+
vec.push(increment(i));
5876
}
5977

60-
let mut vec9: Vec<u8> = Vec::new();
78+
let mut vec: Vec<u8> = Vec::new();
6179
for i in 0..30 {
62-
vec9.push(i + i * i);
80+
vec.push(i + i * i);
6381
}
6482

6583
// Suggestion should not be given as there are multiple pushes that are not the same
66-
let mut vec10: Vec<u8> = Vec::new();
84+
let mut vec: Vec<u8> = Vec::new();
6785
let item: u8 = 2;
6886
for _ in 0..30 {
69-
vec10.push(item);
70-
vec10.push(item * 2);
87+
vec.push(item);
88+
vec.push(item * 2);
7189
}
7290

7391
// Suggestion should not be given as Vec is not involved
@@ -82,23 +100,23 @@ fn main() {
82100
for i in 0..30 {
83101
vec_a.push(A { kind: i });
84102
}
85-
let mut vec12: Vec<u8> = Vec::new();
103+
let mut vec: Vec<u8> = Vec::new();
86104
for a in vec_a {
87-
vec12.push(2u8.pow(a.kind));
105+
vec.push(2u8.pow(a.kind));
88106
}
89107

90108
// Fix #5902
91-
let mut vec13: Vec<u8> = Vec::new();
109+
let mut vec: Vec<u8> = Vec::new();
92110
let mut item = 0;
93111
for _ in 0..10 {
94-
vec13.push(item);
112+
vec.push(item);
95113
item += 10;
96114
}
97115

98116
// Fix #5979
99-
let mut vec14: Vec<std::fs::File> = Vec::new();
117+
let mut vec: Vec<std::fs::File> = Vec::new();
100118
for _ in 0..10 {
101-
vec14.push(std::fs::File::open("foobar").unwrap());
119+
vec.push(std::fs::File::open("foobar").unwrap());
102120
}
103121
// Fix #5979
104122
#[derive(Clone)]
@@ -107,8 +125,27 @@ fn main() {
107125
trait T {}
108126
impl T for S {}
109127

110-
let mut vec15: Vec<Box<dyn T>> = Vec::new();
128+
let mut vec: Vec<Box<dyn T>> = Vec::new();
111129
for _ in 0..10 {
112-
vec15.push(Box::new(S {}));
130+
vec.push(Box::new(S {}));
131+
}
132+
133+
// Fix #5985
134+
let mut vec = Vec::new();
135+
let item = 42;
136+
let item = fun();
137+
for _ in 0..20 {
138+
vec.push(item);
139+
}
140+
141+
// Fix #5985
142+
let mut vec = Vec::new();
143+
let key = 1;
144+
for _ in 0..20 {
145+
let item = match key {
146+
1 => 10,
147+
_ => 0,
148+
};
149+
vec.push(item);
113150
}
114151
}

tests/ui/same_item_push.stderr

+25-17
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,43 @@
11
error: it looks like the same item is being pushed into this Vec
2-
--> $DIR/same_item_push.rs:16:9
2+
--> $DIR/same_item_push.rs:23:9
33
|
4-
LL | spaces.push(vec![b' ']);
5-
| ^^^^^^
4+
LL | vec.push(item);
5+
| ^^^
66
|
77
= note: `-D clippy::same-item-push` implied by `-D warnings`
8-
= help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' '])
8+
= help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
99

1010
error: it looks like the same item is being pushed into this Vec
11-
--> $DIR/same_item_push.rs:22:9
11+
--> $DIR/same_item_push.rs:29:9
1212
|
13-
LL | vec2.push(item);
14-
| ^^^^
13+
LL | vec.push(item);
14+
| ^^^
1515
|
16-
= help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item)
16+
= help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
1717

1818
error: it looks like the same item is being pushed into this Vec
19-
--> $DIR/same_item_push.rs:28:9
19+
--> $DIR/same_item_push.rs:34:9
2020
|
21-
LL | vec3.push(item);
22-
| ^^^^
21+
LL | vec.push(13);
22+
| ^^^
2323
|
24-
= help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item)
24+
= help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13)
2525

2626
error: it looks like the same item is being pushed into this Vec
27-
--> $DIR/same_item_push.rs:33:9
27+
--> $DIR/same_item_push.rs:39:9
2828
|
29-
LL | vec4.push(13);
30-
| ^^^^
29+
LL | vec.push(VALUE);
30+
| ^^^
3131
|
32-
= help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13)
32+
= help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE)
3333

34-
error: aborting due to 4 previous errors
34+
error: it looks like the same item is being pushed into this Vec
35+
--> $DIR/same_item_push.rs:45:9
36+
|
37+
LL | vec.push(item);
38+
| ^^^
39+
|
40+
= help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item)
41+
42+
error: aborting due to 5 previous errors
3543

0 commit comments

Comments
 (0)