about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Macleod <alex@macleod.io>2022-04-11 18:54:44 +0100
committerAlex Macleod <alex@macleod.io>2022-04-11 18:54:44 +0100
commit4a21082da22f6ff48a77f1ded2a6b5f02f31aa78 (patch)
tree4363dbd7914fb345e16f903f252621a480a3554f
parentc7e68638431b2a6639fcbeb44de790619de3b9b1 (diff)
downloadrust-4a21082da22f6ff48a77f1ded2a6b5f02f31aa78.tar.gz
rust-4a21082da22f6ff48a77f1ded2a6b5f02f31aa78.zip
Fix subtraction overflow in `cast_possible_truncation`
-rw-r--r--clippy_lints/src/casts/cast_possible_truncation.rs20
-rw-r--r--tests/ui/cast.rs14
-rw-r--r--tests/ui/cast.stderr14
3 files changed, 33 insertions, 15 deletions
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index 421bd6f53f7..64f87c80f8d 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
         ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
         ExprKind::Binary(op, left, right) => match op.node {
             BinOpKind::Div => {
-                apply_reductions(cx, nbits, left, signed)
-                    - (if signed {
-                        0 // let's be conservative here
-                    } else {
-                        // by dividing by 1, we remove 0 bits, etc.
-                        get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
-                    })
+                apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
+                    // let's be conservative here
+                    0
+                } else {
+                    // by dividing by 1, we remove 0 bits, etc.
+                    get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
+                })
             },
             BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
                 .unwrap_or(u64::max_value())
                 .min(apply_reductions(cx, nbits, left, signed)),
-            BinOpKind::Shr => {
-                apply_reductions(cx, nbits, left, signed)
-                    - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
-            },
+            BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
+                .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
             _ => nbits,
         },
         ExprKind::MethodCall(method, [left, right], _) => {
diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs
index cf85a5ca931..e6031e9adae 100644
--- a/tests/ui/cast.rs
+++ b/tests/ui/cast.rs
@@ -1,13 +1,13 @@
 #![feature(repr128)]
 #![allow(incomplete_features)]
-
-#[warn(
+#![warn(
     clippy::cast_precision_loss,
     clippy::cast_possible_truncation,
     clippy::cast_sign_loss,
     clippy::cast_possible_wrap
 )]
-#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+
 fn main() {
     // Test clippy::cast_precision_loss
     let x0 = 1i32;
@@ -252,3 +252,11 @@ fn main() {
         }
     }
 }
+
+fn avoid_subtract_overflow(q: u32) {
+    let c = (q >> 16) as u8;
+    c as usize;
+
+    let c = (q / 1000) as u8;
+    c as usize;
+}
diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr
index 7a68c0984f1..0c63b4af308 100644
--- a/tests/ui/cast.stderr
+++ b/tests/ui/cast.stderr
@@ -194,5 +194,17 @@ error: casting `main::E10` to `u16` may truncate the value
 LL |             let _ = self as u16;
    |                     ^^^^^^^^^^^
 
-error: aborting due to 31 previous errors
+error: casting `u32` to `u8` may truncate the value
+  --> $DIR/cast.rs:257:13
+   |
+LL |     let c = (q >> 16) as u8;
+   |             ^^^^^^^^^^^^^^^
+
+error: casting `u32` to `u8` may truncate the value
+  --> $DIR/cast.rs:260:13
+   |
+LL |     let c = (q / 1000) as u8;
+   |             ^^^^^^^^^^^^^^^^
+
+error: aborting due to 33 previous errors