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 264b2c2 commit bf1d08e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 191 deletions.
33 changes: 16 additions & 17 deletions clippy_lints/src/casts/cast_possible_truncation.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::expr_or_init;
use clippy_utils::source::snippet;
use clippy_utils::source::snippet_with_applicability;
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_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
Expand Down Expand Up @@ -161,19 +161,18 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
_ => return,
};

let snippet = snippet(cx, expr.span, "x");
let mut applicability = Applicability::MachineApplicable;

let snippet = snippet_with_applicability(cx, expr.span, "x", &mut applicability);
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,
);
});

span_lint_and_sugg(
cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
&msg,
"avoid silent truncation by using",
format!("{cast_to}::try_from({name_of_cast_from}).unwrap()"),
applicability,
);
}
12 changes: 11 additions & 1 deletion src/docs/cast_possible_truncation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,14 @@ checks could be beneficial, and suggests implementing TryFrom trait.
fn as_u8(x: u64) -> u8 {
x as u8
}
```
```

will silently truncate requiring detection and handling after the fact.

```
fn as_u8(x: u64) -> u8 {
u8::try_from(x).unwrap()
}
```

does not, but can now panic.
127 changes: 16 additions & 111 deletions tests/ui/cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,15 @@ error: casting `f32` to `i32` may truncate the value
--> $DIR/cast.rs:24:5
|
LL | 1f32 as i32;
| ^^^^^^^^^^^
| ^^^^^^^^^^^ help: avoid silent truncation by using: `i32::try_from(1f32).unwrap()`
|
= 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);
| ~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^ help: avoid silent truncation by using: `u32::try_from(1f32).unwrap()`

error: casting `f32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:25:5
Expand All @@ -73,61 +62,31 @@ error: casting `f64` to `f32` may truncate the value
--> $DIR/cast.rs:26:5
|
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);
| ~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^ help: avoid silent truncation by using: `f32::try_from(1f64).unwrap()`

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);
| ~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^ help: avoid silent truncation by using: `i8::try_from(1i32).unwrap()`

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);
| ~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^ help: avoid silent truncation by using: `u8::try_from(1i32).unwrap()`

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);
| ~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^ help: avoid silent truncation by using: `isize::try_from(1f64).unwrap()`

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);
| ~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^ help: avoid silent truncation by using: `usize::try_from(1f64).unwrap()`

error: casting `f64` to `usize` may lose the sign of the value
--> $DIR/cast.rs:30:5
Expand Down Expand Up @@ -183,37 +142,19 @@ error: casting `i64` to `i8` may truncate the value
--> $DIR/cast.rs:108:5
|
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
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: avoid silent truncation by using: `i8::try_from((-99999999999i64).min(1)).unwrap()`

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
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: avoid silent truncation by using: `u8::try_from(999999u64.clamp(0, 256)).unwrap()`

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);
| ~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^ help: avoid silent truncation by using: `u8::try_from(self).unwrap()`

error: casting `main::E2::B` to `u8` will truncate the value
--> $DIR/cast.rs:142:21
Expand All @@ -227,13 +168,7 @@ error: casting `main::E5` to `i8` may truncate the value
--> $DIR/cast.rs:178:21
|
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);
| ~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^ help: avoid silent truncation by using: `i8::try_from(self).unwrap()`

error: casting `main::E5::A` to `i8` will truncate the value
--> $DIR/cast.rs:179:21
Expand All @@ -245,61 +180,31 @@ error: casting `main::E6` to `i16` may truncate the value
--> $DIR/cast.rs:193:21
|
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);
| ~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^ help: avoid silent truncation by using: `i16::try_from(self).unwrap()`

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);
| ~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^ help: avoid silent truncation by using: `usize::try_from(self).unwrap()`

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);
| ~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^ help: avoid silent truncation by using: `u16::try_from(self).unwrap()`

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));
| ~~~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^^^ help: avoid silent truncation by using: `u8::try_from((q >> 16)).unwrap()`

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));
| ~~~~~~~~~~~~~~~~~~~~~~~~
| ^^^^^^^^^^^^^^^^ help: avoid silent truncation by using: `u8::try_from((q / 1000)).unwrap()`

error: aborting due to 33 previous errors

0 comments on commit bf1d08e

Please sign in to comment.