Skip to content

Commit 6c28f07

Browse files
committed
recognize contains calls on ranges
1 parent affde93 commit 6c28f07

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

clippy_lints/src/transmute/eager_transmute.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,27 @@ fn range_fully_contained(from: WrappingRange, to: WrappingRange) -> bool {
3636
/// * `x < 4 && x > 1`
3737
/// * `x.field < 4 && x.field > 1` (given `x.field`)
3838
/// * `x.field < 4 && unrelated()`
39+
/// * `(1..=3).contains(&x)`
3940
fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_>) -> bool {
4041
match expr.kind {
4142
ExprKind::Binary(_, lhs, rhs) => {
4243
binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs)
4344
},
45+
ExprKind::MethodCall(path, receiver, [arg], _)
46+
if path.ident.name == sym!(contains)
47+
// ... `contains` called on some kind of range
48+
&& let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def()
49+
&& let lang_items = cx.tcx.lang_items()
50+
&& [
51+
lang_items.range_from_struct(),
52+
lang_items.range_inclusive_struct(),
53+
lang_items.range_struct(),
54+
lang_items.range_to_inclusive_struct(),
55+
lang_items.range_to_struct()
56+
].into_iter().any(|did| did == Some(receiver_adt.did())) =>
57+
{
58+
eq_expr_value(cx, local_expr, arg.peel_borrows())
59+
},
4460
_ => eq_expr_value(cx, local_expr, expr),
4561
}
4662
}

tests/ui/eager_transmute.fixed

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ fn f(op: u8, op2: Data, unrelated: u8) {
4242

4343
// don't lint: wrong variable
4444
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op) });
45+
46+
// range contains checks
47+
let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
48+
let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) });
49+
let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
50+
let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) });
51+
let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
52+
let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
53+
54+
// unrelated binding in contains
55+
let _: Option<Opcode> = (1..=3)
56+
.contains(&unrelated)
57+
.then_some(unsafe { std::mem::transmute(op) });
4558
}
4659

4760
unsafe fn f2(op: u8) {

tests/ui/eager_transmute.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ fn f(op: u8, op2: Data, unrelated: u8) {
4242

4343
// don't lint: wrong variable
4444
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op) });
45+
46+
// range contains checks
47+
let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
48+
let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(unsafe { std::mem::transmute(op) });
49+
let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
50+
let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem::transmute(op) });
51+
let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
52+
let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
53+
54+
// unrelated binding in contains
55+
let _: Option<Opcode> = (1..=3)
56+
.contains(&unrelated)
57+
.then_some(unsafe { std::mem::transmute(op) });
4558
}
4659

4760
unsafe fn f2(op: u8) {

tests/ui/eager_transmute.stderr

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,73 @@ LL | let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| uns
7878
| ~~~~ ++
7979

8080
error: this transmute is always evaluated eagerly, even if the condition is false
81-
--> $DIR/eager_transmute.rs:48:24
81+
--> $DIR/eager_transmute.rs:47:70
82+
|
83+
LL | let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
84+
| ^^^^^^^^^^^^^^^^^^^^^^^
85+
|
86+
help: consider using `bool::then` to only transmute if the condition holds
87+
|
88+
LL | let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
89+
| ~~~~ ++
90+
91+
error: this transmute is always evaluated eagerly, even if the condition is false
92+
--> $DIR/eager_transmute.rs:48:83
93+
|
94+
LL | let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(unsafe { std::mem::transmute(op) });
95+
| ^^^^^^^^^^^^^^^^^^^^^^^
96+
|
97+
help: consider using `bool::then` to only transmute if the condition holds
98+
|
99+
LL | let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) });
100+
| ~~~~ ++
101+
102+
error: this transmute is always evaluated eagerly, even if the condition is false
103+
--> $DIR/eager_transmute.rs:49:69
104+
|
105+
LL | let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
106+
| ^^^^^^^^^^^^^^^^^^^^^^^
107+
|
108+
help: consider using `bool::then` to only transmute if the condition holds
109+
|
110+
LL | let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
111+
| ~~~~ ++
112+
113+
error: this transmute is always evaluated eagerly, even if the condition is false
114+
--> $DIR/eager_transmute.rs:50:68
115+
|
116+
LL | let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem::transmute(op) });
117+
| ^^^^^^^^^^^^^^^^^^^^^^^
118+
|
119+
help: consider using `bool::then` to only transmute if the condition holds
120+
|
121+
LL | let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) });
122+
| ~~~~ ++
123+
124+
error: this transmute is always evaluated eagerly, even if the condition is false
125+
--> $DIR/eager_transmute.rs:51:68
126+
|
127+
LL | let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
128+
| ^^^^^^^^^^^^^^^^^^^^^^^
129+
|
130+
help: consider using `bool::then` to only transmute if the condition holds
131+
|
132+
LL | let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
133+
| ~~~~ ++
134+
135+
error: this transmute is always evaluated eagerly, even if the condition is false
136+
--> $DIR/eager_transmute.rs:52:69
137+
|
138+
LL | let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
139+
| ^^^^^^^^^^^^^^^^^^^^^^^
140+
|
141+
help: consider using `bool::then` to only transmute if the condition holds
142+
|
143+
LL | let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
144+
| ~~~~ ++
145+
146+
error: this transmute is always evaluated eagerly, even if the condition is false
147+
--> $DIR/eager_transmute.rs:61:24
82148
|
83149
LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op));
84150
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -89,7 +155,7 @@ LL | (op < 4).then(|| std::mem::transmute::<_, Opcode>(op));
89155
| ~~~~ ++
90156

91157
error: this transmute is always evaluated eagerly, even if the condition is false
92-
--> $DIR/eager_transmute.rs:77:60
158+
--> $DIR/eager_transmute.rs:90:60
93159
|
94160
LL | let _: Option<NonZeroU8> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) });
95161
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +166,7 @@ LL | let _: Option<NonZeroU8> = (v1 > 0).then(|| unsafe { std::mem::transmut
100166
| ~~~~ ++
101167

102168
error: this transmute is always evaluated eagerly, even if the condition is false
103-
--> $DIR/eager_transmute.rs:83:86
169+
--> $DIR/eager_transmute.rs:96:86
104170
|
105171
LL | let _: Option<NonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
106172
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -111,7 +177,7 @@ LL | let _: Option<NonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then(|| u
111177
| ~~~~ ++
112178

113179
error: this transmute is always evaluated eagerly, even if the condition is false
114-
--> $DIR/eager_transmute.rs:89:93
180+
--> $DIR/eager_transmute.rs:102:93
115181
|
116182
LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
117183
| ^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,5 +187,5 @@ help: consider using `bool::then` to only transmute if the condition holds
121187
LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) });
122188
| ~~~~ ++
123189

124-
error: aborting due to 11 previous errors
190+
error: aborting due to 17 previous errors
125191

0 commit comments

Comments
 (0)