Skip to content

Commit

Permalink
rust-lang#9231 suggest TryFrom when truncation possible
Browse files Browse the repository at this point in the history
  • Loading branch information
navh committed Oct 21, 2022
1 parent b72e451 commit 3f1becb
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 5 deletions.
22 changes: 19 additions & 3 deletions clippy_lints/src/casts/cast_possible_truncation.rs
Original file line number Diff line number Diff line change
@@ -1,9 +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;
Expand Down Expand Up @@ -145,7 +147,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) => {
Expand All @@ -159,5 +161,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,
);
});
}
2 changes: 1 addition & 1 deletion clippy_lints/src/casts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/docs/cast_possible_truncation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ default.
### 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
```
Expand Down
95 changes: 95 additions & 0 deletions tests/ui/cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

53 changes: 53 additions & 0 deletions tests/ui/cast_size.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 3f1becb

Please sign in to comment.