Skip to content

Commit 51aaba6

Browse files
navhxFrednet
authored andcommitted
cast_possible_truncation Suggest TryFrom when truncation possible
1 parent a95286b commit 51aaba6

File tree

4 files changed

+170
-4
lines changed

4 files changed

+170
-4
lines changed

clippy_lints/src/casts/cast_possible_truncation.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use clippy_utils::consts::{constant, Constant};
2-
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
33
use clippy_utils::expr_or_init;
4+
use clippy_utils::source::snippet;
45
use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
6+
use rustc_ast::ast;
7+
use rustc_attr::IntType;
8+
use rustc_errors::{Applicability, SuggestionStyle};
59
use rustc_hir::def::{DefKind, Res};
610
use rustc_hir::{BinOpKind, Expr, ExprKind};
711
use rustc_lint::LateContext;
@@ -139,7 +143,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
139143
);
140144
return;
141145
}
142-
format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
146+
format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
143147
},
144148

145149
(ty::Float(_), true) => {
@@ -153,5 +157,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
153157
_ => return,
154158
};
155159

156-
span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
160+
let snippet = snippet(cx, expr.span, "x");
161+
let name_of_cast_from = snippet.split(" as").next().unwrap_or("x");
162+
let suggestion = format!("{cast_to}::try_from({name_of_cast_from})");
163+
164+
span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
165+
diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...");
166+
diag.span_suggestion_with_style(
167+
expr.span,
168+
"... or use `try_from` and handle the error accordingly",
169+
suggestion,
170+
Applicability::Unspecified,
171+
// always show the suggestion in a separate line
172+
SuggestionStyle::ShowAlways,
173+
);
174+
});
157175
}

clippy_lints/src/casts/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ declare_clippy_lint! {
8585
/// ### Why is this bad?
8686
/// In some problem domains, it is good practice to avoid
8787
/// truncation. This lint can be activated to help assess where additional
88-
/// checks could be beneficial.
88+
/// checks could be beneficial, and suggests implementing TryFrom trait.
8989
///
9090
/// ### Example
9191
/// ```rust

tests/ui/cast.stderr

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,24 @@ error: casting `f32` to `i32` may truncate the value
4242
LL | 1f32 as i32;
4343
| ^^^^^^^^^^^
4444
|
45+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
4546
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
47+
help: ... or use `try_from` and handle the error accordingly
48+
|
49+
LL | i32::try_from(1f32);
50+
| ~~~~~~~~~~~~~~~~~~~
4651

4752
error: casting `f32` to `u32` may truncate the value
4853
--> $DIR/cast.rs:25:5
4954
|
5055
LL | 1f32 as u32;
5156
| ^^^^^^^^^^^
57+
|
58+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
59+
help: ... or use `try_from` and handle the error accordingly
60+
|
61+
LL | u32::try_from(1f32);
62+
| ~~~~~~~~~~~~~~~~~~~
5263

5364
error: casting `f32` to `u32` may lose the sign of the value
5465
--> $DIR/cast.rs:25:5
@@ -63,30 +74,60 @@ error: casting `f64` to `f32` may truncate the value
6374
|
6475
LL | 1f64 as f32;
6576
| ^^^^^^^^^^^
77+
|
78+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
79+
help: ... or use `try_from` and handle the error accordingly
80+
|
81+
LL | f32::try_from(1f64);
82+
| ~~~~~~~~~~~~~~~~~~~
6683

6784
error: casting `i32` to `i8` may truncate the value
6885
--> $DIR/cast.rs:27:5
6986
|
7087
LL | 1i32 as i8;
7188
| ^^^^^^^^^^
89+
|
90+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
91+
help: ... or use `try_from` and handle the error accordingly
92+
|
93+
LL | i8::try_from(1i32);
94+
| ~~~~~~~~~~~~~~~~~~
7295

7396
error: casting `i32` to `u8` may truncate the value
7497
--> $DIR/cast.rs:28:5
7598
|
7699
LL | 1i32 as u8;
77100
| ^^^^^^^^^^
101+
|
102+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
103+
help: ... or use `try_from` and handle the error accordingly
104+
|
105+
LL | u8::try_from(1i32);
106+
| ~~~~~~~~~~~~~~~~~~
78107

79108
error: casting `f64` to `isize` may truncate the value
80109
--> $DIR/cast.rs:29:5
81110
|
82111
LL | 1f64 as isize;
83112
| ^^^^^^^^^^^^^
113+
|
114+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
115+
help: ... or use `try_from` and handle the error accordingly
116+
|
117+
LL | isize::try_from(1f64);
118+
| ~~~~~~~~~~~~~~~~~~~~~
84119

85120
error: casting `f64` to `usize` may truncate the value
86121
--> $DIR/cast.rs:30:5
87122
|
88123
LL | 1f64 as usize;
89124
| ^^^^^^^^^^^^^
125+
|
126+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
127+
help: ... or use `try_from` and handle the error accordingly
128+
|
129+
LL | usize::try_from(1f64);
130+
| ~~~~~~~~~~~~~~~~~~~~~
90131

91132
error: casting `f64` to `usize` may lose the sign of the value
92133
--> $DIR/cast.rs:30:5
@@ -143,18 +184,36 @@ error: casting `i64` to `i8` may truncate the value
143184
|
144185
LL | (-99999999999i64).min(1) as i8; // should be linted because signed
145186
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
187+
|
188+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
189+
help: ... or use `try_from` and handle the error accordingly
190+
|
191+
LL | i8::try_from((-99999999999i64).min(1)); // should be linted because signed
192+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146193

147194
error: casting `u64` to `u8` may truncate the value
148195
--> $DIR/cast.rs:120:5
149196
|
150197
LL | 999999u64.clamp(0, 256) as u8; // should still be linted
151198
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
199+
|
200+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
201+
help: ... or use `try_from` and handle the error accordingly
202+
|
203+
LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted
204+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
152205

153206
error: casting `main::E2` to `u8` may truncate the value
154207
--> $DIR/cast.rs:141:21
155208
|
156209
LL | let _ = self as u8;
157210
| ^^^^^^^^^^
211+
|
212+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
213+
help: ... or use `try_from` and handle the error accordingly
214+
|
215+
LL | let _ = u8::try_from(self);
216+
| ~~~~~~~~~~~~~~~~~~
158217

159218
error: casting `main::E2::B` to `u8` will truncate the value
160219
--> $DIR/cast.rs:142:21
@@ -169,6 +228,12 @@ error: casting `main::E5` to `i8` may truncate the value
169228
|
170229
LL | let _ = self as i8;
171230
| ^^^^^^^^^^
231+
|
232+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
233+
help: ... or use `try_from` and handle the error accordingly
234+
|
235+
LL | let _ = i8::try_from(self);
236+
| ~~~~~~~~~~~~~~~~~~
172237

173238
error: casting `main::E5::A` to `i8` will truncate the value
174239
--> $DIR/cast.rs:179:21
@@ -181,30 +246,60 @@ error: casting `main::E6` to `i16` may truncate the value
181246
|
182247
LL | let _ = self as i16;
183248
| ^^^^^^^^^^^
249+
|
250+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
251+
help: ... or use `try_from` and handle the error accordingly
252+
|
253+
LL | let _ = i16::try_from(self);
254+
| ~~~~~~~~~~~~~~~~~~~
184255

185256
error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
186257
--> $DIR/cast.rs:208:21
187258
|
188259
LL | let _ = self as usize;
189260
| ^^^^^^^^^^^^^
261+
|
262+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
263+
help: ... or use `try_from` and handle the error accordingly
264+
|
265+
LL | let _ = usize::try_from(self);
266+
| ~~~~~~~~~~~~~~~~~~~~~
190267

191268
error: casting `main::E10` to `u16` may truncate the value
192269
--> $DIR/cast.rs:249:21
193270
|
194271
LL | let _ = self as u16;
195272
| ^^^^^^^^^^^
273+
|
274+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
275+
help: ... or use `try_from` and handle the error accordingly
276+
|
277+
LL | let _ = u16::try_from(self);
278+
| ~~~~~~~~~~~~~~~~~~~
196279

197280
error: casting `u32` to `u8` may truncate the value
198281
--> $DIR/cast.rs:257:13
199282
|
200283
LL | let c = (q >> 16) as u8;
201284
| ^^^^^^^^^^^^^^^
285+
|
286+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
287+
help: ... or use `try_from` and handle the error accordingly
288+
|
289+
LL | let c = u8::try_from((q >> 16));
290+
| ~~~~~~~~~~~~~~~~~~~~~~~
202291

203292
error: casting `u32` to `u8` may truncate the value
204293
--> $DIR/cast.rs:260:13
205294
|
206295
LL | let c = (q / 1000) as u8;
207296
| ^^^^^^^^^^^^^^^^
297+
|
298+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
299+
help: ... or use `try_from` and handle the error accordingly
300+
|
301+
LL | let c = u8::try_from((q / 1000));
302+
| ~~~~~~~~~~~~~~~~~~~~~~~~
208303

209304
error: aborting due to 33 previous errors
210305

tests/ui/cast_size.stderr

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ error: casting `isize` to `i8` may truncate the value
44
LL | 1isize as i8;
55
| ^^^^^^^^^^^^
66
|
7+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
78
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
9+
help: ... or use `try_from` and handle the error accordingly
10+
|
11+
LL | i8::try_from(1isize);
12+
| ~~~~~~~~~~~~~~~~~~~~
813

914
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)
1015
--> $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
3742
|
3843
LL | 1isize as i32;
3944
| ^^^^^^^^^^^^^
45+
|
46+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
47+
help: ... or use `try_from` and handle the error accordingly
48+
|
49+
LL | i32::try_from(1isize);
50+
| ~~~~~~~~~~~~~~~~~~~~~
4051

4152
error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
4253
--> $DIR/cast_size.rs:20:5
4354
|
4455
LL | 1isize as u32;
4556
| ^^^^^^^^^^^^^
57+
|
58+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
59+
help: ... or use `try_from` and handle the error accordingly
60+
|
61+
LL | u32::try_from(1isize);
62+
| ~~~~~~~~~~~~~~~~~~~~~
4663

4764
error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
4865
--> $DIR/cast_size.rs:21:5
4966
|
5067
LL | 1usize as u32;
5168
| ^^^^^^^^^^^^^
69+
|
70+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
71+
help: ... or use `try_from` and handle the error accordingly
72+
|
73+
LL | u32::try_from(1usize);
74+
| ~~~~~~~~~~~~~~~~~~~~~
5275

5376
error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
5477
--> $DIR/cast_size.rs:22:5
5578
|
5679
LL | 1usize as i32;
5780
| ^^^^^^^^^^^^^
81+
|
82+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
83+
help: ... or use `try_from` and handle the error accordingly
84+
|
85+
LL | i32::try_from(1usize);
86+
| ~~~~~~~~~~~~~~~~~~~~~
5887

5988
error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
6089
--> $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
6998
|
7099
LL | 1i64 as isize;
71100
| ^^^^^^^^^^^^^
101+
|
102+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
103+
help: ... or use `try_from` and handle the error accordingly
104+
|
105+
LL | isize::try_from(1i64);
106+
| ~~~~~~~~~~~~~~~~~~~~~
72107

73108
error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
74109
--> $DIR/cast_size.rs:25:5
75110
|
76111
LL | 1i64 as usize;
77112
| ^^^^^^^^^^^^^
113+
|
114+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
115+
help: ... or use `try_from` and handle the error accordingly
116+
|
117+
LL | usize::try_from(1i64);
118+
| ~~~~~~~~~~~~~~~~~~~~~
78119

79120
error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
80121
--> $DIR/cast_size.rs:26:5
81122
|
82123
LL | 1u64 as isize;
83124
| ^^^^^^^^^^^^^
125+
|
126+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
127+
help: ... or use `try_from` and handle the error accordingly
128+
|
129+
LL | isize::try_from(1u64);
130+
| ~~~~~~~~~~~~~~~~~~~~~
84131

85132
error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
86133
--> $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
93140
|
94141
LL | 1u64 as usize;
95142
| ^^^^^^^^^^^^^
143+
|
144+
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
145+
help: ... or use `try_from` and handle the error accordingly
146+
|
147+
LL | usize::try_from(1u64);
148+
| ~~~~~~~~~~~~~~~~~~~~~
96149

97150
error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
98151
--> $DIR/cast_size.rs:28:5

0 commit comments

Comments
 (0)