about summary refs log tree commit diff
diff options
context:
space:
mode:
authorteor <teor@riseup.net>2024-01-16 18:03:35 +1000
committerteor <teor@riseup.net>2024-02-20 07:51:21 +1000
commite74fe4362ac6b79751aa1ddd72dfb256b006fff6 (patch)
tree1a07f9ec58e57ca2e7c9541a132941e11e800b4a
parentc5e8487d63290fc38e6261c45425662be9279a36 (diff)
downloadrust-e74fe4362ac6b79751aa1ddd72dfb256b006fff6.tar.gz
rust-e74fe4362ac6b79751aa1ddd72dfb256b006fff6.zip
Check for both signed and unsigned constant expressions
-rw-r--r--clippy_lints/src/casts/cast_sign_loss.rs35
-rw-r--r--tests/ui/cast.stderr2
2 files changed, 30 insertions, 7 deletions
diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs
index ba8222fc0cd..530fb0dcde0 100644
--- a/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/clippy_lints/src/casts/cast_sign_loss.rs
@@ -66,8 +66,7 @@ fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx
             // a % b => [a]
             let exprs = exprs_with_selected_binop_peeled(cast_op);
             for expr in exprs {
-                let ty = cx.typeck_results().expr_ty(expr);
-                match expr_sign(cx, expr, ty) {
+                match expr_sign(cx, expr, None) {
                     Sign::Negative => negative_count += 1,
                     Sign::Uncertain => uncertain_count += 1,
                     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));
 
     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
 }
 
+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 {
     ZeroOrPositive,
     Negative,
@@ -104,9 +123,12 @@ enum 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
-    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 };
     }
+    if let Some(_val) = get_const_unsigned_int_eval(cx, expr, None) {
+        return Sign::ZeroOrPositive;
+    }
 
     // Calling on methods that always return non-negative values.
     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`].
 fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'_>) -> Sign {
     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);
 
     match (base_sign, exponent_is_even) {
         // 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,
 
         // Any base raised to an even exponent is non-negative.
diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr
index 0eb3b95733a..aaa6b4183e9 100644
--- a/tests/ui/cast.stderr
+++ b/tests/ui/cast.stderr
@@ -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
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 77 previous errors
+error: aborting due to 76 previous errors