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 20, 2022
1 parent 967f172 commit fe05e24
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 30 deletions.
21 changes: 18 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_sugg};
use clippy_utils::expr_or_init;
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;
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,18 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
_ => return,
};

span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
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");

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,
);
}
14 changes: 12 additions & 2 deletions src/docs/cast_possible_truncation.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@ 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
```
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.
32 changes: 16 additions & 16 deletions tests/ui/cast.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +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()`
|
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`

error: casting `f32` to `u32` may truncate the value
--> $DIR/cast.rs:25:5
|
LL | 1f32 as u32;
| ^^^^^^^^^^^
| ^^^^^^^^^^^ 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 @@ -62,31 +62,31 @@ error: casting `f64` to `f32` may truncate the value
--> $DIR/cast.rs:26:5
|
LL | 1f64 as f32;
| ^^^^^^^^^^^
| ^^^^^^^^^^^ 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: 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: 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: 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: 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 @@ -142,19 +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: 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: 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: 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 @@ -168,7 +168,7 @@ error: casting `main::E5` to `i8` may truncate the value
--> $DIR/cast.rs:178:21
|
LL | let _ = self as i8;
| ^^^^^^^^^^
| ^^^^^^^^^^ 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 @@ -180,31 +180,31 @@ error: casting `main::E6` to `i16` may truncate the value
--> $DIR/cast.rs:193:21
|
LL | let _ = self as i16;
| ^^^^^^^^^^^
| ^^^^^^^^^^^ 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: 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: 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: 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: avoid silent truncation by using: `u8::try_from((q / 1000)).unwrap()`

error: aborting due to 33 previous errors

18 changes: 9 additions & 9 deletions tests/ui/cast_size.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: casting `isize` to `i8` may truncate the value
--> $DIR/cast_size.rs:12:5
|
LL | 1isize as i8;
| ^^^^^^^^^^^^
| ^^^^^^^^^^^^ help: avoid silent truncation by using: `i8::try_from(1isize).unwrap()`
|
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`

Expand Down Expand Up @@ -36,25 +36,25 @@ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wi
--> $DIR/cast_size.rs:19:5
|
LL | 1isize as i32;
| ^^^^^^^^^^^^^
| ^^^^^^^^^^^^^ help: avoid silent truncation by using: `i32::try_from(1isize).unwrap()`

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: avoid silent truncation by using: `u32::try_from(1isize).unwrap()`

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: avoid silent truncation by using: `u32::try_from(1usize).unwrap()`

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: avoid silent truncation by using: `i32::try_from(1usize).unwrap()`

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 @@ -68,19 +68,19 @@ error: casting `i64` to `isize` may truncate the value on targets with 32-bit wi
--> $DIR/cast_size.rs:24:5
|
LL | 1i64 as isize;
| ^^^^^^^^^^^^^
| ^^^^^^^^^^^^^ help: avoid silent truncation by using: `isize::try_from(1i64).unwrap()`

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: avoid silent truncation by using: `usize::try_from(1i64).unwrap()`

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: avoid silent truncation by using: `isize::try_from(1u64).unwrap()`

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 @@ -92,7 +92,7 @@ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wi
--> $DIR/cast_size.rs:27:5
|
LL | 1u64 as usize;
| ^^^^^^^^^^^^^
| ^^^^^^^^^^^^^ help: avoid silent truncation by using: `usize::try_from(1u64).unwrap()`

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 fe05e24

Please sign in to comment.