2 mod cast_possible_truncation;
3 mod cast_possible_wrap;
4 mod cast_precision_loss;
5 mod cast_ptr_alignment;
9 mod fn_to_numeric_cast;
10 mod fn_to_numeric_cast_with_truncation;
15 use clippy_utils::is_hir_ty_cfg_dependant;
16 use rustc_hir::{Expr, ExprKind};
17 use rustc_lint::{LateContext, LateLintPass, LintContext};
18 use rustc_middle::lint::in_external_macro;
19 use rustc_semver::RustcVersion;
20 use rustc_session::{declare_tool_lint, impl_lint_pass};
22 declare_clippy_lint! {
23 /// **What it does:** Checks for casts from any numerical to a float type where
24 /// the receiving type cannot store all values from the original type without
25 /// rounding errors. This possible rounding is to be expected, so this lint is
26 /// `Allow` by default.
28 /// Basically, this warns on casting any integer with 32 or more bits to `f32`
29 /// or any 64-bit integer to `f64`.
31 /// **Why is this bad?** It's not bad at all. But in some applications it can be
32 /// helpful to know where precision loss can take place. This lint can help find
33 /// those places in the code.
35 /// **Known problems:** None.
42 pub CAST_PRECISION_LOSS,
44 "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
47 declare_clippy_lint! {
48 /// **What it does:** Checks for casts from a signed to an unsigned numerical
49 /// type. In this case, negative values wrap around to large positive values,
50 /// which can be quite surprising in practice. However, as the cast works as
51 /// defined, this lint is `Allow` by default.
53 /// **Why is this bad?** Possibly surprising results. You can activate this lint
54 /// as a one-time check to see where numerical wrapping can arise.
56 /// **Known problems:** None.
61 /// y as u128; // will return 18446744073709551615
65 "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
68 declare_clippy_lint! {
69 /// **What it does:** Checks for casts between numerical types that may
70 /// truncate large values. This is expected behavior, so the cast is `Allow` by
73 /// **Why is this bad?** In some problem domains, it is good practice to avoid
74 /// truncation. This lint can be activated to help assess where additional
75 /// checks could be beneficial.
77 /// **Known problems:** None.
81 /// fn as_u8(x: u64) -> u8 {
85 pub CAST_POSSIBLE_TRUNCATION,
87 "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
90 declare_clippy_lint! {
91 /// **What it does:** Checks for casts from an unsigned type to a signed type of
92 /// the same size. Performing such a cast is a 'no-op' for the compiler,
93 /// i.e., nothing is changed at the bit level, and the binary representation of
94 /// the value is reinterpreted. This can cause wrapping if the value is too big
95 /// for the target signed type. However, the cast works as defined, so this lint
96 /// is `Allow` by default.
98 /// **Why is this bad?** While such a cast is not bad in itself, the results can
99 /// be surprising when this is not the intended behavior, as demonstrated by the
102 /// **Known problems:** None.
106 /// u32::MAX as i32; // will yield a value of `-1`
108 pub CAST_POSSIBLE_WRAP,
110 "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
113 declare_clippy_lint! {
114 /// **What it does:** Checks for casts between numerical types that may
115 /// be replaced by safe conversion functions.
117 /// **Why is this bad?** Rust's `as` keyword will perform many kinds of
118 /// conversions, including silently lossy conversions. Conversion functions such
119 /// as `i32::from` will only perform lossless conversions. Using the conversion
120 /// functions prevents conversions from turning into silent lossy conversions if
121 /// the types of the input expressions ever change, and make it easier for
122 /// people reading the code to know that the conversion is lossless.
124 /// **Known problems:** None.
128 /// fn as_u64(x: u8) -> u64 {
133 /// Using `::from` would look like this:
136 /// fn as_u64(x: u8) -> u64 {
142 "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
145 declare_clippy_lint! {
146 /// **What it does:** Checks for casts to the same type, casts of int literals to integer types
147 /// and casts of float literals to float types.
149 /// **Why is this bad?** It's just unnecessary.
151 /// **Known problems:** None.
155 /// let _ = 2i32 as i32;
156 /// let _ = 0.5 as f32;
165 pub UNNECESSARY_CAST,
167 "cast to the same type, e.g., `x as i32` where `x: i32`"
170 declare_clippy_lint! {
171 /// **What it does:** Checks for casts, using `as` or `pointer::cast`,
172 /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
174 /// **Why is this bad?** Dereferencing the resulting pointer may be undefined
177 /// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
178 /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
179 /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
183 /// let _ = (&1u8 as *const u8) as *const u16;
184 /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
186 /// (&1u8 as *const u8).cast::<u16>();
187 /// (&mut 1u8 as *mut u8).cast::<u16>();
189 pub CAST_PTR_ALIGNMENT,
191 "cast from a pointer to a more-strictly-aligned pointer"
194 declare_clippy_lint! {
195 /// **What it does:** Checks for casts of function pointers to something other than usize
197 /// **Why is this bad?**
198 /// Casting a function pointer to anything other than usize/isize is not portable across
199 /// architectures, because you end up losing bits if the target type is too small or end up with a
200 /// bunch of extra bits that waste space and add more instructions to the final binary than
201 /// strictly necessary for the problem
203 /// Casting to isize also doesn't make sense since there are no signed addresses.
209 /// fn fun() -> i32 { 1 }
210 /// let a = fun as i64;
213 /// fn fun2() -> i32 { 1 }
214 /// let a = fun2 as usize;
216 pub FN_TO_NUMERIC_CAST,
218 "casting a function pointer to a numeric type other than usize"
221 declare_clippy_lint! {
222 /// **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to
225 /// **Why is this bad?**
226 /// Such a cast discards some bits of the function's address. If this is intended, it would be more
227 /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
228 /// a comment) to perform the truncation.
234 /// fn fn1() -> i16 {
237 /// let _ = fn1 as i32;
239 /// // Better: Cast to usize first, then comment with the reason for the truncation
240 /// fn fn2() -> i16 {
243 /// let fn_ptr = fn2 as usize;
244 /// let fn_ptr_truncated = fn_ptr as i32;
246 pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
248 "casting a function pointer to a numeric type not wide enough to store the address"
251 declare_clippy_lint! {
252 /// **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.
254 /// **Why is this bad?** It’s basically guaranteed to be undefined behaviour.
255 /// `UnsafeCell` is the only way to obtain aliasable data that is considered
258 /// **Known problems:** None.
264 /// *(r as *const _ as *mut _) += 1;
269 /// Instead consider using interior mutability types.
272 /// use std::cell::UnsafeCell;
274 /// fn x(r: &UnsafeCell<i32>) {
282 "a cast of reference to a mutable pointer"
285 declare_clippy_lint! {
286 /// **What it does:** Checks for expressions where a character literal is cast
287 /// to `u8` and suggests using a byte literal instead.
289 /// **Why is this bad?** In general, casting values to smaller types is
290 /// error-prone and should be avoided where possible. In the particular case of
291 /// converting a character literal to u8, it is easy to avoid by just using a
292 /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
293 /// than `'a' as u8`.
295 /// **Known problems:** None.
302 /// A better version, using the byte literal:
309 "casting a character literal to `u8` truncates"
312 declare_clippy_lint! {
313 /// **What it does:**
314 /// Checks for `as` casts between raw pointers without changing its mutability,
315 /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
317 /// **Why is this bad?**
318 /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
319 /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
321 /// **Known problems:** None.
326 /// let ptr: *const u32 = &42_u32;
327 /// let mut_ptr: *mut u32 = &mut 42_u32;
328 /// let _ = ptr as *const i32;
329 /// let _ = mut_ptr as *mut i32;
333 /// let ptr: *const u32 = &42_u32;
334 /// let mut_ptr: *mut u32 = &mut 42_u32;
335 /// let _ = ptr.cast::<i32>();
336 /// let _ = mut_ptr.cast::<i32>();
340 "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
344 msrv: Option<RustcVersion>,
349 pub fn new(msrv: Option<RustcVersion>) -> Self {
354 impl_lint_pass!(Casts => [
357 CAST_POSSIBLE_TRUNCATION,
364 FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
369 impl<'tcx> LateLintPass<'tcx> for Casts {
370 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
371 if expr.span.from_expansion() {
375 if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
376 if is_hir_ty_cfg_dependant(cx, cast_to) {
379 let (cast_from, cast_to) = (
380 cx.typeck_results().expr_ty(cast_expr),
381 cx.typeck_results().expr_ty(expr),
384 if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
388 fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
389 fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
390 if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
391 cast_possible_truncation::check(cx, expr, cast_from, cast_to);
392 cast_possible_wrap::check(cx, expr, cast_from, cast_to);
393 cast_precision_loss::check(cx, expr, cast_from, cast_to);
394 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
395 cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
399 cast_ref_to_mut::check(cx, expr);
400 cast_ptr_alignment::check(cx, expr);
401 char_lit_as_u8::check(cx, expr);
402 ptr_as_ptr::check(cx, expr, &self.msrv);
405 extract_msrv_attr!(LateContext);