1 mod cast_abs_to_unsigned;
2 mod cast_enum_constructor;
4 mod cast_possible_truncation;
5 mod cast_possible_wrap;
6 mod cast_precision_loss;
7 mod cast_ptr_alignment;
10 mod cast_slice_different_sizes;
12 mod fn_to_numeric_cast;
13 mod fn_to_numeric_cast_any;
14 mod fn_to_numeric_cast_with_truncation;
19 use clippy_utils::is_hir_ty_cfg_dependant;
20 use rustc_hir::{Expr, ExprKind};
21 use rustc_lint::{LateContext, LateLintPass, LintContext};
22 use rustc_middle::lint::in_external_macro;
23 use rustc_semver::RustcVersion;
24 use rustc_session::{declare_tool_lint, impl_lint_pass};
26 declare_clippy_lint! {
28 /// Checks for casts from any numerical to a float type where
29 /// the receiving type cannot store all values from the original type without
30 /// rounding errors. This possible rounding is to be expected, so this lint is
31 /// `Allow` by default.
33 /// Basically, this warns on casting any integer with 32 or more bits to `f32`
34 /// or any 64-bit integer to `f64`.
36 /// ### Why is this bad?
37 /// It's not bad at all. But in some applications it can be
38 /// helpful to know where precision loss can take place. This lint can help find
39 /// those places in the code.
46 #[clippy::version = "pre 1.29.0"]
47 pub CAST_PRECISION_LOSS,
49 "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
52 declare_clippy_lint! {
54 /// Checks for casts from a signed to an unsigned numerical
55 /// type. In this case, negative values wrap around to large positive values,
56 /// which can be quite surprising in practice. However, as the cast works as
57 /// defined, this lint is `Allow` by default.
59 /// ### Why is this bad?
60 /// Possibly surprising results. You can activate this lint
61 /// as a one-time check to see where numerical wrapping can arise.
66 /// y as u128; // will return 18446744073709551615
68 #[clippy::version = "pre 1.29.0"]
71 "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
74 declare_clippy_lint! {
76 /// Checks for casts between numerical types that may
77 /// truncate large values. This is expected behavior, so the cast is `Allow` by
80 /// ### Why is this bad?
81 /// In some problem domains, it is good practice to avoid
82 /// truncation. This lint can be activated to help assess where additional
83 /// checks could be beneficial.
87 /// fn as_u8(x: u64) -> u8 {
91 #[clippy::version = "pre 1.29.0"]
92 pub CAST_POSSIBLE_TRUNCATION,
94 "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
97 declare_clippy_lint! {
99 /// Checks for casts from an unsigned type to a signed type of
100 /// the same size. Performing such a cast is a 'no-op' for the compiler,
101 /// i.e., nothing is changed at the bit level, and the binary representation of
102 /// the value is reinterpreted. This can cause wrapping if the value is too big
103 /// for the target signed type. However, the cast works as defined, so this lint
104 /// is `Allow` by default.
106 /// ### Why is this bad?
107 /// While such a cast is not bad in itself, the results can
108 /// be surprising when this is not the intended behavior, as demonstrated by the
113 /// u32::MAX as i32; // will yield a value of `-1`
115 #[clippy::version = "pre 1.29.0"]
116 pub CAST_POSSIBLE_WRAP,
118 "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
121 declare_clippy_lint! {
123 /// Checks for casts between numerical types that may
124 /// be replaced by safe conversion functions.
126 /// ### Why is this bad?
127 /// Rust's `as` keyword will perform many kinds of
128 /// conversions, including silently lossy conversions. Conversion functions such
129 /// as `i32::from` will only perform lossless conversions. Using the conversion
130 /// functions prevents conversions from turning into silent lossy conversions if
131 /// the types of the input expressions ever change, and make it easier for
132 /// people reading the code to know that the conversion is lossless.
136 /// fn as_u64(x: u8) -> u64 {
141 /// Using `::from` would look like this:
144 /// fn as_u64(x: u8) -> u64 {
148 #[clippy::version = "pre 1.29.0"]
151 "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
154 declare_clippy_lint! {
156 /// Checks for casts to the same type, casts of int literals to integer types
157 /// and casts of float literals to float types.
159 /// ### Why is this bad?
160 /// It's just unnecessary.
164 /// let _ = 2i32 as i32;
165 /// let _ = 0.5 as f32;
174 #[clippy::version = "pre 1.29.0"]
175 pub UNNECESSARY_CAST,
177 "cast to the same type, e.g., `x as i32` where `x: i32`"
180 declare_clippy_lint! {
182 /// Checks for casts, using `as` or `pointer::cast`,
183 /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
185 /// ### Why is this bad?
186 /// Dereferencing the resulting pointer may be undefined
189 /// ### Known problems
190 /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
191 /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
192 /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
196 /// let _ = (&1u8 as *const u8) as *const u16;
197 /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
199 /// (&1u8 as *const u8).cast::<u16>();
200 /// (&mut 1u8 as *mut u8).cast::<u16>();
202 #[clippy::version = "pre 1.29.0"]
203 pub CAST_PTR_ALIGNMENT,
205 "cast from a pointer to a more-strictly-aligned pointer"
208 declare_clippy_lint! {
210 /// Checks for casts of function pointers to something other than usize
212 /// ### Why is this bad?
213 /// Casting a function pointer to anything other than usize/isize is not portable across
214 /// architectures, because you end up losing bits if the target type is too small or end up with a
215 /// bunch of extra bits that waste space and add more instructions to the final binary than
216 /// strictly necessary for the problem
218 /// Casting to isize also doesn't make sense since there are no signed addresses.
223 /// fn fun() -> i32 { 1 }
224 /// let a = fun as i64;
227 /// fn fun2() -> i32 { 1 }
228 /// let a = fun2 as usize;
230 #[clippy::version = "pre 1.29.0"]
231 pub FN_TO_NUMERIC_CAST,
233 "casting a function pointer to a numeric type other than usize"
236 declare_clippy_lint! {
238 /// Checks for casts of a function pointer to a numeric type not wide enough to
241 /// ### Why is this bad?
242 /// Such a cast discards some bits of the function's address. If this is intended, it would be more
243 /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
244 /// a comment) to perform the truncation.
249 /// fn fn1() -> i16 {
252 /// let _ = fn1 as i32;
254 /// // Better: Cast to usize first, then comment with the reason for the truncation
255 /// fn fn2() -> i16 {
258 /// let fn_ptr = fn2 as usize;
259 /// let fn_ptr_truncated = fn_ptr as i32;
261 #[clippy::version = "pre 1.29.0"]
262 pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
264 "casting a function pointer to a numeric type not wide enough to store the address"
267 declare_clippy_lint! {
269 /// Checks for casts of a function pointer to any integer type.
271 /// ### Why is this bad?
272 /// Casting a function pointer to an integer can have surprising results and can occur
273 /// accidentally if parantheses are omitted from a function call. If you aren't doing anything
274 /// low-level with function pointers then you can opt-out of casting functions to integers in
275 /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
276 /// pointer casts in your code.
280 /// // Bad: fn1 is cast as `usize`
281 /// fn fn1() -> u16 {
284 /// let _ = fn1 as usize;
286 /// // Good: maybe you intended to call the function?
287 /// fn fn2() -> u16 {
290 /// let _ = fn2() as usize;
292 /// // Good: maybe you intended to cast it to a function type?
293 /// fn fn3() -> u16 {
296 /// let _ = fn3 as fn() -> u16;
298 #[clippy::version = "1.58.0"]
299 pub FN_TO_NUMERIC_CAST_ANY,
301 "casting a function pointer to any integer type"
304 declare_clippy_lint! {
306 /// Checks for casts of `&T` to `&mut T` anywhere in the code.
308 /// ### Why is this bad?
309 /// It’s basically guaranteed to be undefined behaviour.
310 /// `UnsafeCell` is the only way to obtain aliasable data that is considered
317 /// *(r as *const _ as *mut _) += 1;
322 /// Instead consider using interior mutability types.
325 /// use std::cell::UnsafeCell;
327 /// fn x(r: &UnsafeCell<i32>) {
333 #[clippy::version = "1.33.0"]
336 "a cast of reference to a mutable pointer"
339 declare_clippy_lint! {
341 /// Checks for expressions where a character literal is cast
342 /// to `u8` and suggests using a byte literal instead.
344 /// ### Why is this bad?
345 /// In general, casting values to smaller types is
346 /// error-prone and should be avoided where possible. In the particular case of
347 /// converting a character literal to u8, it is easy to avoid by just using a
348 /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
349 /// than `'a' as u8`.
356 /// A better version, using the byte literal:
361 #[clippy::version = "pre 1.29.0"]
364 "casting a character literal to `u8` truncates"
367 declare_clippy_lint! {
369 /// Checks for `as` casts between raw pointers without changing its mutability,
370 /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
372 /// ### Why is this bad?
373 /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
374 /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
378 /// let ptr: *const u32 = &42_u32;
379 /// let mut_ptr: *mut u32 = &mut 42_u32;
380 /// let _ = ptr as *const i32;
381 /// let _ = mut_ptr as *mut i32;
385 /// let ptr: *const u32 = &42_u32;
386 /// let mut_ptr: *mut u32 = &mut 42_u32;
387 /// let _ = ptr.cast::<i32>();
388 /// let _ = mut_ptr.cast::<i32>();
390 #[clippy::version = "1.51.0"]
393 "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
396 declare_clippy_lint! {
398 /// Checks for casts from an enum type to an integral type which will definitely truncate the
401 /// ### Why is this bad?
402 /// The resulting integral value will not match the value of the variant it came from.
406 /// enum E { X = 256 };
407 /// let _ = E::X as u8;
409 #[clippy::version = "1.60.0"]
410 pub CAST_ENUM_TRUNCATION,
412 "casts from an enum type to an integral type which will truncate the value"
415 declare_clippy_lint! {
416 /// Checks for `as` casts between raw pointers to slices with differently sized elements.
418 /// ### Why is this bad?
419 /// The produced raw pointer to a slice does not update its length metadata. The produced
420 /// pointer will point to a different number of bytes than the original pointer because the
421 /// length metadata of a raw slice pointer is in elements rather than bytes.
422 /// Producing a slice reference from the raw pointer will either create a slice with
423 /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
428 /// let a = [1_i32, 2, 3, 4];
429 /// let p = &a as *const [i32] as *const [u8];
431 /// println!("{:?}", &*p);
434 /// // Undefined Behavior (note: also potential alignment issues)
436 /// let a = [1_u8, 2, 3, 4];
437 /// let p = &a as *const [u8] as *const [u32];
439 /// println!("{:?}", &*p);
442 /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
444 /// let a = [1_i32, 2, 3, 4];
445 /// let old_ptr = &a as *const [i32];
446 /// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
447 /// // The length comes from the known length of 4 i32s times the 4 bytes per i32
448 /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
450 /// println!("{:?}", &*new_ptr);
453 #[clippy::version = "1.60.0"]
454 pub CAST_SLICE_DIFFERENT_SIZES,
456 "casting using `as` between raw pointers to slices of types with different sizes"
459 declare_clippy_lint! {
461 /// Checks for casts from an enum tuple constructor to an integer.
463 /// ### Why is this bad?
464 /// The cast is easily confused with casting a c-like enum value to an integer.
468 /// enum E { X(i32) };
469 /// let _ = E::X as usize;
471 #[clippy::version = "1.61.0"]
472 pub CAST_ENUM_CONSTRUCTOR,
474 "casts from an enum tuple constructor to an integer"
477 declare_clippy_lint! {
479 /// Checks for uses of the `abs()` method that cast the result to unsigned.
481 /// ### Why is this bad?
482 /// The `unsigned_abs()` method avoids panic when called on the MIN value.
486 /// let x: i32 = -42;
487 /// let y: u32 = x.abs() as u32;
490 /// let x: i32 = -42;
491 /// let y: u32 = x.unsigned_abs();
493 #[clippy::version = "1.61.0"]
494 pub CAST_ABS_TO_UNSIGNED,
496 "casting the result of `abs()` to an unsigned integer can panic"
500 msrv: Option<RustcVersion>,
505 pub fn new(msrv: Option<RustcVersion>) -> Self {
510 impl_lint_pass!(Casts => [
513 CAST_POSSIBLE_TRUNCATION,
518 CAST_SLICE_DIFFERENT_SIZES,
520 FN_TO_NUMERIC_CAST_ANY,
522 FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
525 CAST_ENUM_TRUNCATION,
526 CAST_ENUM_CONSTRUCTOR,
530 impl<'tcx> LateLintPass<'tcx> for Casts {
531 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
532 if !in_external_macro(cx.sess(), expr.span) {
533 ptr_as_ptr::check(cx, expr, &self.msrv);
536 if expr.span.from_expansion() {
540 if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
541 if is_hir_ty_cfg_dependant(cx, cast_to) {
544 let (cast_from, cast_to) = (
545 cx.typeck_results().expr_ty(cast_expr),
546 cx.typeck_results().expr_ty(expr),
549 if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
553 fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
554 fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
555 fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
557 if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
558 cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
559 if cast_from.is_numeric() {
560 cast_possible_wrap::check(cx, expr, cast_from, cast_to);
561 cast_precision_loss::check(cx, expr, cast_from, cast_to);
562 cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
563 cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
565 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
566 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
570 cast_ref_to_mut::check(cx, expr);
571 cast_ptr_alignment::check(cx, expr);
572 char_lit_as_u8::check(cx, expr);
573 ptr_as_ptr::check(cx, expr, &self.msrv);
574 cast_slice_different_sizes::check(cx, expr, &self.msrv);
577 extract_msrv_attr!(LateContext);