mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-12 18:07:40 +00:00
Auto merge of #8763 - arieluy:manual_range_contains, r=xFrednet
Support negative ints in manual_range_contains fixes: #8721 changelog: Fixes issue where ranges containing ints with different signs would be incorrect due to comparing as unsigned.
This commit is contained in:
commit
1594e986ea
@ -207,7 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
|
|||||||
extract_msrv_attr!(LateContext);
|
extract_msrv_attr!(LateContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) {
|
fn check_possible_range_contains(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
op: BinOpKind,
|
||||||
|
left: &Expr<'_>,
|
||||||
|
right: &Expr<'_>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
) {
|
||||||
if in_constant(cx, expr.hir_id) {
|
if in_constant(cx, expr.hir_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -219,21 +225,19 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
|
|||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
// value, name, order (higher/lower), inclusiveness
|
// value, name, order (higher/lower), inclusiveness
|
||||||
if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) =
|
if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) {
|
||||||
(check_range_bounds(cx, l), check_range_bounds(cx, r))
|
|
||||||
{
|
|
||||||
// we only lint comparisons on the same name and with different
|
// we only lint comparisons on the same name and with different
|
||||||
// direction
|
// direction
|
||||||
if lid != rid || lord == rord {
|
if l.id != r.id || l.ord == r.ord {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
|
let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val);
|
||||||
if combine_and && ord == Some(rord) {
|
if combine_and && ord == Some(r.ord) {
|
||||||
// order lower bound and upper bound
|
// order lower bound and upper bound
|
||||||
let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less {
|
let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less {
|
||||||
(lval_span, rval_span, linc, rinc)
|
(l.val_span, r.val_span, l.inc, r.inc)
|
||||||
} else {
|
} else {
|
||||||
(rval_span, lval_span, rinc, linc)
|
(r.val_span, l.val_span, r.inc, l.inc)
|
||||||
};
|
};
|
||||||
// we only lint inclusive lower bounds
|
// we only lint inclusive lower bounds
|
||||||
if !l_inc {
|
if !l_inc {
|
||||||
@ -245,7 +249,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
|
|||||||
("Range", "..")
|
("Range", "..")
|
||||||
};
|
};
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
|
let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
|
||||||
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
|
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
|
||||||
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
|
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
|
||||||
let space = if lo.ends_with('.') { " " } else { "" };
|
let space = if lo.ends_with('.') { " " } else { "" };
|
||||||
@ -258,13 +262,13 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
|
|||||||
format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
|
format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
} else if !combine_and && ord == Some(lord) {
|
} else if !combine_and && ord == Some(l.ord) {
|
||||||
// `!_.contains(_)`
|
// `!_.contains(_)`
|
||||||
// order lower bound and upper bound
|
// order lower bound and upper bound
|
||||||
let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less {
|
let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less {
|
||||||
(lval_span, rval_span, linc, rinc)
|
(l.val_span, r.val_span, l.inc, r.inc)
|
||||||
} else {
|
} else {
|
||||||
(rval_span, lval_span, rinc, linc)
|
(r.val_span, l.val_span, r.inc, l.inc)
|
||||||
};
|
};
|
||||||
if l_inc {
|
if l_inc {
|
||||||
return;
|
return;
|
||||||
@ -275,7 +279,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
|
|||||||
("RangeInclusive", "..=")
|
("RangeInclusive", "..=")
|
||||||
};
|
};
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
|
let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
|
||||||
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
|
let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
|
||||||
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
|
let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
|
||||||
let space = if lo.ends_with('.') { " " } else { "" };
|
let space = if lo.ends_with('.') { " " } else { "" };
|
||||||
@ -292,7 +296,20 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> {
|
struct RangeBounds<'a> {
|
||||||
|
val: Constant,
|
||||||
|
expr: &'a Expr<'a>,
|
||||||
|
id: HirId,
|
||||||
|
name_span: Span,
|
||||||
|
val_span: Span,
|
||||||
|
ord: Ordering,
|
||||||
|
inc: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes a binary expression such as x <= 2 as input
|
||||||
|
// Breaks apart into various pieces, such as the value of the number,
|
||||||
|
// hir id of the variable, and direction/inclusiveness of the operator
|
||||||
|
fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
|
||||||
if let ExprKind::Binary(ref op, l, r) = ex.kind {
|
if let ExprKind::Binary(ref op, l, r) = ex.kind {
|
||||||
let (inclusive, ordering) = match op.node {
|
let (inclusive, ordering) = match op.node {
|
||||||
BinOpKind::Gt => (false, Ordering::Greater),
|
BinOpKind::Gt => (false, Ordering::Greater),
|
||||||
@ -303,11 +320,27 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant,
|
|||||||
};
|
};
|
||||||
if let Some(id) = path_to_local(l) {
|
if let Some(id) = path_to_local(l) {
|
||||||
if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
|
if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
|
||||||
return Some((c, id, l.span, r.span, ordering, inclusive));
|
return Some(RangeBounds {
|
||||||
|
val: c,
|
||||||
|
expr: r,
|
||||||
|
id,
|
||||||
|
name_span: l.span,
|
||||||
|
val_span: r.span,
|
||||||
|
ord: ordering,
|
||||||
|
inc: inclusive,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if let Some(id) = path_to_local(r) {
|
} else if let Some(id) = path_to_local(r) {
|
||||||
if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
|
if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
|
||||||
return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
|
return Some(RangeBounds {
|
||||||
|
val: c,
|
||||||
|
expr: l,
|
||||||
|
id,
|
||||||
|
name_span: r.span,
|
||||||
|
val_span: l.span,
|
||||||
|
ord: ordering.reverse(),
|
||||||
|
inc: inclusive,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,12 +130,10 @@ impl Constant {
|
|||||||
match (left, right) {
|
match (left, right) {
|
||||||
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
|
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
|
||||||
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
|
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
|
||||||
(&Self::Int(l), &Self::Int(r)) => {
|
(&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() {
|
||||||
if let ty::Int(int_ty) = *cmp_type.kind() {
|
ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))),
|
||||||
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
|
ty::Uint(_) => Some(l.cmp(&r)),
|
||||||
} else {
|
_ => bug!("Not an int type"),
|
||||||
Some(l.cmp(&r))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
|
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
|
||||||
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
|
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#[allow(clippy::short_circuit_statement)]
|
#[allow(clippy::short_circuit_statement)]
|
||||||
#[allow(clippy::unnecessary_operation)]
|
#[allow(clippy::unnecessary_operation)]
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 9_u32;
|
let x = 9_i32;
|
||||||
|
|
||||||
// order shouldn't matter
|
// order shouldn't matter
|
||||||
(8..12).contains(&x);
|
(8..12).contains(&x);
|
||||||
@ -43,6 +43,12 @@ fn main() {
|
|||||||
let y = 3.;
|
let y = 3.;
|
||||||
(0. ..1.).contains(&y);
|
(0. ..1.).contains(&y);
|
||||||
!(0. ..=1.).contains(&y);
|
!(0. ..=1.).contains(&y);
|
||||||
|
|
||||||
|
// handle negatives #8721
|
||||||
|
(-10..=10).contains(&x);
|
||||||
|
x >= 10 && x <= -10;
|
||||||
|
(-3. ..=3.).contains(&y);
|
||||||
|
y >= 3. && y <= -3.;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix #6373
|
// Fix #6373
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#[allow(clippy::short_circuit_statement)]
|
#[allow(clippy::short_circuit_statement)]
|
||||||
#[allow(clippy::unnecessary_operation)]
|
#[allow(clippy::unnecessary_operation)]
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 9_u32;
|
let x = 9_i32;
|
||||||
|
|
||||||
// order shouldn't matter
|
// order shouldn't matter
|
||||||
x >= 8 && x < 12;
|
x >= 8 && x < 12;
|
||||||
@ -43,6 +43,12 @@ fn main() {
|
|||||||
let y = 3.;
|
let y = 3.;
|
||||||
y >= 0. && y < 1.;
|
y >= 0. && y < 1.;
|
||||||
y < 0. || y > 1.;
|
y < 0. || y > 1.;
|
||||||
|
|
||||||
|
// handle negatives #8721
|
||||||
|
x >= -10 && x <= 10;
|
||||||
|
x >= 10 && x <= -10;
|
||||||
|
y >= -3. && y <= 3.;
|
||||||
|
y >= 3. && y <= -3.;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix #6373
|
// Fix #6373
|
||||||
|
@ -84,5 +84,17 @@ error: manual `!RangeInclusive::contains` implementation
|
|||||||
LL | y < 0. || y > 1.;
|
LL | y < 0. || y > 1.;
|
||||||
| ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
|
| ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
|
||||||
|
|
||||||
error: aborting due to 14 previous errors
|
error: manual `RangeInclusive::contains` implementation
|
||||||
|
--> $DIR/range_contains.rs:48:5
|
||||||
|
|
|
||||||
|
LL | x >= -10 && x <= 10;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
|
||||||
|
|
||||||
|
error: manual `RangeInclusive::contains` implementation
|
||||||
|
--> $DIR/range_contains.rs:50:5
|
||||||
|
|
|
||||||
|
LL | y >= -3. && y <= 3.;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
|
||||||
|
|
||||||
|
error: aborting due to 16 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user