about summary refs log tree commit diff
diff options
context:
space:
mode:
authornavh <navarre.hebb@gmail.com>2022-12-06 19:05:22 +0100
committerxFrednet <xFrednet@gmail.com>2023-01-12 12:16:06 +0100
commit51aaba6e8cf50289d55f53ac85aab4c95d601439 (patch)
tree60aaefa9488f3a3819786777c890e41121fe6cef
parenta95286b8521a83b93e3ce2c2f3d2b015948acaad (diff)
downloadrust-51aaba6e8cf50289d55f53ac85aab4c95d601439.tar.gz
rust-51aaba6e8cf50289d55f53ac85aab4c95d601439.zip
`cast_possible_truncation` Suggest TryFrom when truncation possible
-rw-r--r--clippy_lints/src/casts/cast_possible_truncation.rs24
-rw-r--r--clippy_lints/src/casts/mod.rs2
-rw-r--r--tests/ui/cast.stderr95
-rw-r--r--tests/ui/cast_size.stderr53
4 files changed, 170 insertions, 4 deletions
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index a6376484914..d9898aeb92c 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -1,7 +1,11 @@
 use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::expr_or_init;
+use clippy_utils::source::snippet;
 use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
+use rustc_ast::ast;
+use rustc_attr::IntType;
+use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -139,7 +143,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
                 );
                 return;
             }
-            format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
+            format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
         },
 
         (ty::Float(_), true) => {
@@ -153,5 +157,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         _ => return,
     };
 
-    span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
+    let snippet = snippet(cx, expr.span, "x");
+    let name_of_cast_from = snippet.split(" as").next().unwrap_or("x");
+    let suggestion = format!("{cast_to}::try_from({name_of_cast_from})");
+
+    span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
+        diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...");
+        diag.span_suggestion_with_style(
+            expr.span,
+            "... or use `try_from` and handle the error accordingly",
+            suggestion,
+            Applicability::Unspecified,
+            // always show the suggestion in a separate line
+            SuggestionStyle::ShowAlways,
+        );
+    });
 }
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index 161e3a698e9..38b1c5c1c7e 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -85,7 +85,7 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// In some problem domains, it is good practice to avoid
     /// truncation. This lint can be activated to help assess where additional
-    /// checks could be beneficial.
+    /// checks could be beneficial, and suggests implementing TryFrom trait.
     ///
     /// ### Example
     /// ```rust
diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr
index 0c63b4af308..eceb135d62b 100644
--- a/tests/ui/cast.stderr
+++ b/tests/ui/cast.stderr
@@ -42,13 +42,24 @@ error: casting `f32` to `i32` may truncate the value
 LL |     1f32 as i32;
    |     ^^^^^^^^^^^
    |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
    = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     i32::try_from(1f32);
+   |     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `f32` to `u32` may truncate the value
   --> $DIR/cast.rs:25:5
    |
 LL |     1f32 as u32;
    |     ^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     u32::try_from(1f32);
+   |     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `f32` to `u32` may lose the sign of the value
   --> $DIR/cast.rs:25:5
@@ -63,30 +74,60 @@ error: casting `f64` to `f32` may truncate the value
    |
 LL |     1f64 as f32;
    |     ^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     f32::try_from(1f64);
+   |     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `i32` to `i8` may truncate the value
   --> $DIR/cast.rs:27:5
    |
 LL |     1i32 as i8;
    |     ^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     i8::try_from(1i32);
+   |     ~~~~~~~~~~~~~~~~~~
 
 error: casting `i32` to `u8` may truncate the value
   --> $DIR/cast.rs:28:5
    |
 LL |     1i32 as u8;
    |     ^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     u8::try_from(1i32);
+   |     ~~~~~~~~~~~~~~~~~~
 
 error: casting `f64` to `isize` may truncate the value
   --> $DIR/cast.rs:29:5
    |
 LL |     1f64 as isize;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     isize::try_from(1f64);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `f64` to `usize` may truncate the value
   --> $DIR/cast.rs:30:5
    |
 LL |     1f64 as usize;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     usize::try_from(1f64);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `f64` to `usize` may lose the sign of the value
   --> $DIR/cast.rs:30:5
@@ -143,18 +184,36 @@ error: casting `i64` to `i8` may truncate the value
    |
 LL |     (-99999999999i64).min(1) as i8; // should be linted because signed
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     i8::try_from((-99999999999i64).min(1)); // should be linted because signed
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `u8` may truncate the value
   --> $DIR/cast.rs:120:5
    |
 LL |     999999u64.clamp(0, 256) as u8; // should still be linted
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     u8::try_from(999999u64.clamp(0, 256)); // should still be linted
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E2` to `u8` may truncate the value
   --> $DIR/cast.rs:141:21
    |
 LL |             let _ = self as u8;
    |                     ^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |             let _ = u8::try_from(self);
+   |                     ~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E2::B` to `u8` will truncate the value
   --> $DIR/cast.rs:142:21
@@ -169,6 +228,12 @@ error: casting `main::E5` to `i8` may truncate the value
    |
 LL |             let _ = self as i8;
    |                     ^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |             let _ = i8::try_from(self);
+   |                     ~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E5::A` to `i8` will truncate the value
   --> $DIR/cast.rs:179:21
@@ -181,30 +246,60 @@ error: casting `main::E6` to `i16` may truncate the value
    |
 LL |             let _ = self as i16;
    |                     ^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |             let _ = i16::try_from(self);
+   |                     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
   --> $DIR/cast.rs:208:21
    |
 LL |             let _ = self as usize;
    |                     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |             let _ = usize::try_from(self);
+   |                     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `main::E10` to `u16` may truncate the value
   --> $DIR/cast.rs:249:21
    |
 LL |             let _ = self as u16;
    |                     ^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |             let _ = u16::try_from(self);
+   |                     ~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `u8` may truncate the value
   --> $DIR/cast.rs:257:13
    |
 LL |     let c = (q >> 16) as u8;
    |             ^^^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     let c = u8::try_from((q >> 16));
+   |             ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `u8` may truncate the value
   --> $DIR/cast.rs:260:13
    |
 LL |     let c = (q / 1000) as u8;
    |             ^^^^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     let c = u8::try_from((q / 1000));
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 33 previous errors
 
diff --git a/tests/ui/cast_size.stderr b/tests/ui/cast_size.stderr
index 95552f2e285..8acf26049f4 100644
--- a/tests/ui/cast_size.stderr
+++ b/tests/ui/cast_size.stderr
@@ -4,7 +4,12 @@ error: casting `isize` to `i8` may truncate the value
 LL |     1isize as i8;
    |     ^^^^^^^^^^^^
    |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
    = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     i8::try_from(1isize);
+   |     ~~~~~~~~~~~~~~~~~~~~
 
 error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
   --> $DIR/cast_size.rs:15:5
@@ -37,24 +42,48 @@ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wi
    |
 LL |     1isize as i32;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     i32::try_from(1isize);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
   --> $DIR/cast_size.rs:20:5
    |
 LL |     1isize as u32;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     u32::try_from(1isize);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
   --> $DIR/cast_size.rs:21:5
    |
 LL |     1usize as u32;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     u32::try_from(1usize);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
   --> $DIR/cast_size.rs:22:5
    |
 LL |     1usize as i32;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     i32::try_from(1usize);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
   --> $DIR/cast_size.rs:22:5
@@ -69,18 +98,36 @@ error: casting `i64` to `isize` may truncate the value on targets with 32-bit wi
    |
 LL |     1i64 as isize;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     isize::try_from(1i64);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
   --> $DIR/cast_size.rs:25:5
    |
 LL |     1i64 as usize;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     usize::try_from(1i64);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
   --> $DIR/cast_size.rs:26:5
    |
 LL |     1u64 as isize;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     isize::try_from(1u64);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
   --> $DIR/cast_size.rs:26:5
@@ -93,6 +140,12 @@ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wi
    |
 LL |     1u64 as usize;
    |     ^^^^^^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+   |
+LL |     usize::try_from(1u64);
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
   --> $DIR/cast_size.rs:28:5