mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-24 05:33:27 +00:00
Check for both signed and unsigned constant expressions
This commit is contained in:
parent
c5e8487d63
commit
e74fe4362a
2 changed files with 30 additions and 7 deletions
|
@ -66,8 +66,7 @@ fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx
|
||||||
// a % b => [a]
|
// a % b => [a]
|
||||||
let exprs = exprs_with_selected_binop_peeled(cast_op);
|
let exprs = exprs_with_selected_binop_peeled(cast_op);
|
||||||
for expr in exprs {
|
for expr in exprs {
|
||||||
let ty = cx.typeck_results().expr_ty(expr);
|
match expr_sign(cx, expr, None) {
|
||||||
match expr_sign(cx, expr, ty) {
|
|
||||||
Sign::Negative => negative_count += 1,
|
Sign::Negative => negative_count += 1,
|
||||||
Sign::Uncertain => uncertain_count += 1,
|
Sign::Uncertain => uncertain_count += 1,
|
||||||
Sign::ZeroOrPositive => (),
|
Sign::ZeroOrPositive => (),
|
||||||
|
@ -85,7 +84,11 @@ fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_const_int_eval<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into<Option<Ty<'cx>>>) -> Option<i128> {
|
fn get_const_signed_int_eval<'cx>(
|
||||||
|
cx: &LateContext<'cx>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
ty: impl Into<Option<Ty<'cx>>>,
|
||||||
|
) -> Option<i128> {
|
||||||
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
|
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
|
||||||
|
|
||||||
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
|
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
|
||||||
|
@ -96,6 +99,22 @@ fn get_const_int_eval<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_const_unsigned_int_eval<'cx>(
|
||||||
|
cx: &LateContext<'cx>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
ty: impl Into<Option<Ty<'cx>>>,
|
||||||
|
) -> Option<u128> {
|
||||||
|
let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
|
||||||
|
|
||||||
|
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
|
||||||
|
&& let ty::Uint(_ity) = *ty.kind()
|
||||||
|
{
|
||||||
|
return Some(n);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
enum Sign {
|
enum Sign {
|
||||||
ZeroOrPositive,
|
ZeroOrPositive,
|
||||||
Negative,
|
Negative,
|
||||||
|
@ -104,9 +123,12 @@ enum Sign {
|
||||||
|
|
||||||
fn expr_sign<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into<Option<Ty<'cx>>>) -> Sign {
|
fn expr_sign<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into<Option<Ty<'cx>>>) -> Sign {
|
||||||
// Try evaluate this expr first to see if it's positive
|
// Try evaluate this expr first to see if it's positive
|
||||||
if let Some(val) = get_const_int_eval(cx, expr, ty) {
|
if let Some(val) = get_const_signed_int_eval(cx, expr, ty) {
|
||||||
return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative };
|
return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative };
|
||||||
}
|
}
|
||||||
|
if let Some(_val) = get_const_unsigned_int_eval(cx, expr, None) {
|
||||||
|
return Sign::ZeroOrPositive;
|
||||||
|
}
|
||||||
|
|
||||||
// Calling on methods that always return non-negative values.
|
// Calling on methods that always return non-negative values.
|
||||||
if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind {
|
if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind {
|
||||||
|
@ -144,12 +166,13 @@ fn expr_sign<'cx>(cx: &LateContext<'cx>, expr: &Expr<'_>, ty: impl Into<Option<T
|
||||||
/// Otherwise, returns [`Sign::Uncertain`].
|
/// Otherwise, returns [`Sign::Uncertain`].
|
||||||
fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'_>) -> Sign {
|
fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'_>) -> Sign {
|
||||||
let base_sign = expr_sign(cx, base, None);
|
let base_sign = expr_sign(cx, base, None);
|
||||||
let exponent_val = get_const_int_eval(cx, exponent, None);
|
|
||||||
|
// Rust's integer pow() functions take an unsigned exponent.
|
||||||
|
let exponent_val = get_const_unsigned_int_eval(cx, exponent, None);
|
||||||
let exponent_is_even = exponent_val.map(|val| val % 2 == 0);
|
let exponent_is_even = exponent_val.map(|val| val % 2 == 0);
|
||||||
|
|
||||||
match (base_sign, exponent_is_even) {
|
match (base_sign, exponent_is_even) {
|
||||||
// Non-negative bases always return non-negative results, ignoring overflow.
|
// Non-negative bases always return non-negative results, ignoring overflow.
|
||||||
// This is because Rust's integer pow() functions take an unsigned exponent.
|
|
||||||
(Sign::ZeroOrPositive, _) => Sign::ZeroOrPositive,
|
(Sign::ZeroOrPositive, _) => Sign::ZeroOrPositive,
|
||||||
|
|
||||||
// Any base raised to an even exponent is non-negative.
|
// Any base raised to an even exponent is non-negative.
|
||||||
|
|
|
@ -600,5 +600,5 @@ error: casting `i32` to `u32` may lose the sign of the value
|
||||||
LL | (a.abs() * b.pow(2) / c.abs()) as u32
|
LL | (a.abs() * b.pow(2) / c.abs()) as u32
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to 77 previous errors
|
error: aborting due to 76 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue