Skip to content

Commit 1594e98

Browse files
committed
Auto merge of rust-lang#8763 - arieluy:manual_range_contains, r=xFrednet
Support negative ints in manual_range_contains fixes: rust-lang#8721 changelog: Fixes issue where ranges containing ints with different signs would be incorrect due to comparing as unsigned.
2 parents bbe6e94 + 2aa63c9 commit 1594e98

File tree

5 files changed

+83
-28
lines changed

5 files changed

+83
-28
lines changed

clippy_lints/src/ranges.rs

+52-19
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
207207
extract_msrv_attr!(LateContext);
208208
}
209209

210-
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) {
210+
fn check_possible_range_contains(
211+
cx: &LateContext<'_>,
212+
op: BinOpKind,
213+
left: &Expr<'_>,
214+
right: &Expr<'_>,
215+
expr: &Expr<'_>,
216+
) {
211217
if in_constant(cx, expr.hir_id) {
212218
return;
213219
}
@@ -219,21 +225,19 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
219225
_ => return,
220226
};
221227
// value, name, order (higher/lower), inclusiveness
222-
if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) =
223-
(check_range_bounds(cx, l), check_range_bounds(cx, r))
224-
{
228+
if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) {
225229
// we only lint comparisons on the same name and with different
226230
// direction
227-
if lid != rid || lord == rord {
231+
if l.id != r.id || l.ord == r.ord {
228232
return;
229233
}
230-
let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
231-
if combine_and && ord == Some(rord) {
234+
let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val);
235+
if combine_and && ord == Some(r.ord) {
232236
// order lower bound and upper bound
233-
let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less {
234-
(lval_span, rval_span, linc, rinc)
237+
let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less {
238+
(l.val_span, r.val_span, l.inc, r.inc)
235239
} else {
236-
(rval_span, lval_span, rinc, linc)
240+
(r.val_span, l.val_span, r.inc, l.inc)
237241
};
238242
// we only lint inclusive lower bounds
239243
if !l_inc {
@@ -245,7 +249,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
245249
("Range", "..")
246250
};
247251
let mut applicability = Applicability::MachineApplicable;
248-
let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
252+
let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
249253
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
250254
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
251255
let space = if lo.ends_with('.') { " " } else { "" };
@@ -258,13 +262,13 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
258262
format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
259263
applicability,
260264
);
261-
} else if !combine_and && ord == Some(lord) {
265+
} else if !combine_and && ord == Some(l.ord) {
262266
// `!_.contains(_)`
263267
// order lower bound and upper bound
264-
let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less {
265-
(lval_span, rval_span, linc, rinc)
268+
let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less {
269+
(l.val_span, r.val_span, l.inc, r.inc)
266270
} else {
267-
(rval_span, lval_span, rinc, linc)
271+
(r.val_span, l.val_span, r.inc, l.inc)
268272
};
269273
if l_inc {
270274
return;
@@ -275,7 +279,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
275279
("RangeInclusive", "..=")
276280
};
277281
let mut applicability = Applicability::MachineApplicable;
278-
let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
282+
let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
279283
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
280284
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
281285
let space = if lo.ends_with('.') { " " } else { "" };
@@ -292,7 +296,20 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
292296
}
293297
}
294298

295-
fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> {
299+
struct RangeBounds<'a> {
300+
val: Constant,
301+
expr: &'a Expr<'a>,
302+
id: HirId,
303+
name_span: Span,
304+
val_span: Span,
305+
ord: Ordering,
306+
inc: bool,
307+
}
308+
309+
// Takes a binary expression such as x <= 2 as input
310+
// Breaks apart into various pieces, such as the value of the number,
311+
// hir id of the variable, and direction/inclusiveness of the operator
312+
fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
296313
if let ExprKind::Binary(ref op, l, r) = ex.kind {
297314
let (inclusive, ordering) = match op.node {
298315
BinOpKind::Gt => (false, Ordering::Greater),
@@ -303,11 +320,27 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant,
303320
};
304321
if let Some(id) = path_to_local(l) {
305322
if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
306-
return Some((c, id, l.span, r.span, ordering, inclusive));
323+
return Some(RangeBounds {
324+
val: c,
325+
expr: r,
326+
id,
327+
name_span: l.span,
328+
val_span: r.span,
329+
ord: ordering,
330+
inc: inclusive,
331+
});
307332
}
308333
} else if let Some(id) = path_to_local(r) {
309334
if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
310-
return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
335+
return Some(RangeBounds {
336+
val: c,
337+
expr: l,
338+
id,
339+
name_span: r.span,
340+
val_span: l.span,
341+
ord: ordering.reverse(),
342+
inc: inclusive,
343+
});
311344
}
312345
}
313346
}

clippy_utils/src/consts.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,10 @@ impl Constant {
130130
match (left, right) {
131131
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
132132
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
133-
(&Self::Int(l), &Self::Int(r)) => {
134-
if let ty::Int(int_ty) = *cmp_type.kind() {
135-
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
136-
} else {
137-
Some(l.cmp(&r))
138-
}
133+
(&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() {
134+
ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))),
135+
ty::Uint(_) => Some(l.cmp(&r)),
136+
_ => bug!("Not an int type"),
139137
},
140138
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
141139
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),

tests/ui/range_contains.fixed

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#[allow(clippy::short_circuit_statement)]
77
#[allow(clippy::unnecessary_operation)]
88
fn main() {
9-
let x = 9_u32;
9+
let x = 9_i32;
1010

1111
// order shouldn't matter
1212
(8..12).contains(&x);
@@ -43,6 +43,12 @@ fn main() {
4343
let y = 3.;
4444
(0. ..1.).contains(&y);
4545
!(0. ..=1.).contains(&y);
46+
47+
// handle negatives #8721
48+
(-10..=10).contains(&x);
49+
x >= 10 && x <= -10;
50+
(-3. ..=3.).contains(&y);
51+
y >= 3. && y <= -3.;
4652
}
4753

4854
// Fix #6373

tests/ui/range_contains.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#[allow(clippy::short_circuit_statement)]
77
#[allow(clippy::unnecessary_operation)]
88
fn main() {
9-
let x = 9_u32;
9+
let x = 9_i32;
1010

1111
// order shouldn't matter
1212
x >= 8 && x < 12;
@@ -43,6 +43,12 @@ fn main() {
4343
let y = 3.;
4444
y >= 0. && y < 1.;
4545
y < 0. || y > 1.;
46+
47+
// handle negatives #8721
48+
x >= -10 && x <= 10;
49+
x >= 10 && x <= -10;
50+
y >= -3. && y <= 3.;
51+
y >= 3. && y <= -3.;
4652
}
4753

4854
// Fix #6373

tests/ui/range_contains.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,17 @@ error: manual `!RangeInclusive::contains` implementation
8484
LL | y < 0. || y > 1.;
8585
| ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
8686

87-
error: aborting due to 14 previous errors
87+
error: manual `RangeInclusive::contains` implementation
88+
--> $DIR/range_contains.rs:48:5
89+
|
90+
LL | x >= -10 && x <= 10;
91+
| ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
92+
93+
error: manual `RangeInclusive::contains` implementation
94+
--> $DIR/range_contains.rs:50:5
95+
|
96+
LL | y >= -3. && y <= 3.;
97+
| ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
98+
99+
error: aborting due to 16 previous errors
88100

0 commit comments

Comments
 (0)