4 mod cast_abs_to_unsigned;
5 mod cast_enum_constructor;
7 mod cast_possible_truncation;
8 mod cast_possible_wrap;
9 mod cast_precision_loss;
10 mod cast_ptr_alignment;
13 mod cast_slice_different_sizes;
14 mod cast_slice_from_raw_parts;
16 mod fn_to_numeric_cast;
17 mod fn_to_numeric_cast_any;
18 mod fn_to_numeric_cast_with_truncation;
23 use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
24 use rustc_hir::{Expr, ExprKind};
25 use rustc_lint::{LateContext, LateLintPass, LintContext};
26 use rustc_middle::lint::in_external_macro;
27 use rustc_semver::RustcVersion;
28 use rustc_session::{declare_tool_lint, impl_lint_pass};
30 declare_clippy_lint! {
32 /// Checks for casts from any numerical to a float type where
33 /// the receiving type cannot store all values from the original type without
34 /// rounding errors. This possible rounding is to be expected, so this lint is
35 /// `Allow` by default.
37 /// Basically, this warns on casting any integer with 32 or more bits to `f32`
38 /// or any 64-bit integer to `f64`.
40 /// ### Why is this bad?
41 /// It's not bad at all. But in some applications it can be
42 /// helpful to know where precision loss can take place. This lint can help find
43 /// those places in the code.
50 #[clippy::version = "pre 1.29.0"]
51 pub CAST_PRECISION_LOSS,
53 "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
56 declare_clippy_lint! {
58 /// Checks for casts from a signed to an unsigned numerical
59 /// type. In this case, negative values wrap around to large positive values,
60 /// which can be quite surprising in practice. However, as the cast works as
61 /// defined, this lint is `Allow` by default.
63 /// ### Why is this bad?
64 /// Possibly surprising results. You can activate this lint
65 /// as a one-time check to see where numerical wrapping can arise.
70 /// y as u128; // will return 18446744073709551615
72 #[clippy::version = "pre 1.29.0"]
75 "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
78 declare_clippy_lint! {
80 /// Checks for casts between numerical types that may
81 /// truncate large values. This is expected behavior, so the cast is `Allow` by
84 /// ### Why is this bad?
85 /// In some problem domains, it is good practice to avoid
86 /// truncation. This lint can be activated to help assess where additional
87 /// checks could be beneficial.
91 /// fn as_u8(x: u64) -> u8 {
95 #[clippy::version = "pre 1.29.0"]
96 pub CAST_POSSIBLE_TRUNCATION,
98 "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
101 declare_clippy_lint! {
103 /// Checks for casts from an unsigned type to a signed type of
104 /// the same size. Performing such a cast is a 'no-op' for the compiler,
105 /// i.e., nothing is changed at the bit level, and the binary representation of
106 /// the value is reinterpreted. This can cause wrapping if the value is too big
107 /// for the target signed type. However, the cast works as defined, so this lint
108 /// is `Allow` by default.
110 /// ### Why is this bad?
111 /// While such a cast is not bad in itself, the results can
112 /// be surprising when this is not the intended behavior, as demonstrated by the
117 /// u32::MAX as i32; // will yield a value of `-1`
119 #[clippy::version = "pre 1.29.0"]
120 pub CAST_POSSIBLE_WRAP,
122 "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
125 declare_clippy_lint! {
127 /// Checks for casts between numerical types that may
128 /// be replaced by safe conversion functions.
130 /// ### Why is this bad?
131 /// Rust's `as` keyword will perform many kinds of
132 /// conversions, including silently lossy conversions. Conversion functions such
133 /// as `i32::from` will only perform lossless conversions. Using the conversion
134 /// functions prevents conversions from turning into silent lossy conversions if
135 /// the types of the input expressions ever change, and make it easier for
136 /// people reading the code to know that the conversion is lossless.
140 /// fn as_u64(x: u8) -> u64 {
145 /// Using `::from` would look like this:
148 /// fn as_u64(x: u8) -> u64 {
152 #[clippy::version = "pre 1.29.0"]
155 "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
158 declare_clippy_lint! {
160 /// Checks for casts to the same type, casts of int literals to integer types
161 /// and casts of float literals to float types.
163 /// ### Why is this bad?
164 /// It's just unnecessary.
168 /// let _ = 2i32 as i32;
169 /// let _ = 0.5 as f32;
178 #[clippy::version = "pre 1.29.0"]
179 pub UNNECESSARY_CAST,
181 "cast to the same type, e.g., `x as i32` where `x: i32`"
184 declare_clippy_lint! {
186 /// Checks for casts, using `as` or `pointer::cast`,
187 /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
189 /// ### Why is this bad?
190 /// Dereferencing the resulting pointer may be undefined
193 /// ### Known problems
194 /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
195 /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
196 /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
200 /// let _ = (&1u8 as *const u8) as *const u16;
201 /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
203 /// (&1u8 as *const u8).cast::<u16>();
204 /// (&mut 1u8 as *mut u8).cast::<u16>();
206 #[clippy::version = "pre 1.29.0"]
207 pub CAST_PTR_ALIGNMENT,
209 "cast from a pointer to a more-strictly-aligned pointer"
212 declare_clippy_lint! {
214 /// Checks for casts of function pointers to something other than usize
216 /// ### Why is this bad?
217 /// Casting a function pointer to anything other than usize/isize is not portable across
218 /// architectures, because you end up losing bits if the target type is too small or end up with a
219 /// bunch of extra bits that waste space and add more instructions to the final binary than
220 /// strictly necessary for the problem
222 /// Casting to isize also doesn't make sense since there are no signed addresses.
226 /// fn fun() -> i32 { 1 }
227 /// let _ = fun as i64;
232 /// # fn fun() -> i32 { 1 }
233 /// let _ = fun as usize;
235 #[clippy::version = "pre 1.29.0"]
236 pub FN_TO_NUMERIC_CAST,
238 "casting a function pointer to a numeric type other than usize"
241 declare_clippy_lint! {
243 /// Checks for casts of a function pointer to a numeric type not wide enough to
246 /// ### Why is this bad?
247 /// Such a cast discards some bits of the function's address. If this is intended, it would be more
248 /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
249 /// a comment) to perform the truncation.
253 /// fn fn1() -> i16 {
256 /// let _ = fn1 as i32;
261 /// // Cast to usize first, then comment with the reason for the truncation
262 /// fn fn1() -> i16 {
265 /// let fn_ptr = fn1 as usize;
266 /// let fn_ptr_truncated = fn_ptr as i32;
268 #[clippy::version = "pre 1.29.0"]
269 pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
271 "casting a function pointer to a numeric type not wide enough to store the address"
274 declare_clippy_lint! {
276 /// Checks for casts of a function pointer to any integer type.
278 /// ### Why is this bad?
279 /// Casting a function pointer to an integer can have surprising results and can occur
280 /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
281 /// low-level with function pointers then you can opt-out of casting functions to integers in
282 /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
283 /// pointer casts in your code.
287 /// // fn1 is cast as `usize`
288 /// fn fn1() -> u16 {
291 /// let _ = fn1 as usize;
296 /// // maybe you intended to call the function?
297 /// fn fn2() -> u16 {
300 /// let _ = fn2() as usize;
304 /// // maybe you intended to cast it to a function type?
305 /// fn fn3() -> u16 {
308 /// let _ = fn3 as fn() -> u16;
310 #[clippy::version = "1.58.0"]
311 pub FN_TO_NUMERIC_CAST_ANY,
313 "casting a function pointer to any integer type"
316 declare_clippy_lint! {
318 /// Checks for casts of `&T` to `&mut T` anywhere in the code.
320 /// ### Why is this bad?
321 /// It’s basically guaranteed to be undefined behavior.
322 /// `UnsafeCell` is the only way to obtain aliasable data that is considered
329 /// *(r as *const _ as *mut _) += 1;
334 /// Instead consider using interior mutability types.
337 /// use std::cell::UnsafeCell;
339 /// fn x(r: &UnsafeCell<i32>) {
345 #[clippy::version = "1.33.0"]
348 "a cast of reference to a mutable pointer"
351 declare_clippy_lint! {
353 /// Checks for expressions where a character literal is cast
354 /// to `u8` and suggests using a byte literal instead.
356 /// ### Why is this bad?
357 /// In general, casting values to smaller types is
358 /// error-prone and should be avoided where possible. In the particular case of
359 /// converting a character literal to u8, it is easy to avoid by just using a
360 /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
361 /// than `'a' as u8`.
368 /// A better version, using the byte literal:
373 #[clippy::version = "pre 1.29.0"]
376 "casting a character literal to `u8` truncates"
379 declare_clippy_lint! {
381 /// Checks for `as` casts between raw pointers without changing its mutability,
382 /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
384 /// ### Why is this bad?
385 /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
386 /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
390 /// let ptr: *const u32 = &42_u32;
391 /// let mut_ptr: *mut u32 = &mut 42_u32;
392 /// let _ = ptr as *const i32;
393 /// let _ = mut_ptr as *mut i32;
397 /// let ptr: *const u32 = &42_u32;
398 /// let mut_ptr: *mut u32 = &mut 42_u32;
399 /// let _ = ptr.cast::<i32>();
400 /// let _ = mut_ptr.cast::<i32>();
402 #[clippy::version = "1.51.0"]
405 "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
408 declare_clippy_lint! {
410 /// Checks for casts from an enum type to an integral type which will definitely truncate the
413 /// ### Why is this bad?
414 /// The resulting integral value will not match the value of the variant it came from.
418 /// enum E { X = 256 };
419 /// let _ = E::X as u8;
421 #[clippy::version = "1.61.0"]
422 pub CAST_ENUM_TRUNCATION,
424 "casts from an enum type to an integral type which will truncate the value"
427 declare_clippy_lint! {
429 /// Checks for `as` casts between raw pointers to slices with differently sized elements.
431 /// ### Why is this bad?
432 /// The produced raw pointer to a slice does not update its length metadata. The produced
433 /// pointer will point to a different number of bytes than the original pointer because the
434 /// length metadata of a raw slice pointer is in elements rather than bytes.
435 /// Producing a slice reference from the raw pointer will either create a slice with
436 /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
441 /// let a = [1_i32, 2, 3, 4];
442 /// let p = &a as *const [i32] as *const [u8];
444 /// println!("{:?}", &*p);
447 /// // Undefined Behavior (note: also potential alignment issues)
449 /// let a = [1_u8, 2, 3, 4];
450 /// let p = &a as *const [u8] as *const [u32];
452 /// println!("{:?}", &*p);
455 /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
457 /// let a = [1_i32, 2, 3, 4];
458 /// let old_ptr = &a as *const [i32];
459 /// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
460 /// // The length comes from the known length of 4 i32s times the 4 bytes per i32
461 /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
463 /// println!("{:?}", &*new_ptr);
466 #[clippy::version = "1.61.0"]
467 pub CAST_SLICE_DIFFERENT_SIZES,
469 "casting using `as` between raw pointers to slices of types with different sizes"
472 declare_clippy_lint! {
474 /// Checks for casts from an enum tuple constructor to an integer.
476 /// ### Why is this bad?
477 /// The cast is easily confused with casting a c-like enum value to an integer.
481 /// enum E { X(i32) };
482 /// let _ = E::X as usize;
484 #[clippy::version = "1.61.0"]
485 pub CAST_ENUM_CONSTRUCTOR,
487 "casts from an enum tuple constructor to an integer"
490 declare_clippy_lint! {
492 /// Checks for uses of the `abs()` method that cast the result to unsigned.
494 /// ### Why is this bad?
495 /// The `unsigned_abs()` method avoids panic when called on the MIN value.
499 /// let x: i32 = -42;
500 /// let y: u32 = x.abs() as u32;
504 /// let x: i32 = -42;
505 /// let y: u32 = x.unsigned_abs();
507 #[clippy::version = "1.62.0"]
508 pub CAST_ABS_TO_UNSIGNED,
510 "casting the result of `abs()` to an unsigned integer can panic"
513 declare_clippy_lint! {
515 /// Check for the usage of `as _` conversion using inferred type.
517 /// ### Why is this bad?
518 /// The conversion might include lossy conversion and dangerous cast that might go
519 /// undetected due to the type being inferred.
521 /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
525 /// fn foo(n: usize) {}
526 /// let n: u16 = 256;
531 /// fn foo(n: usize) {}
532 /// let n: u16 = 256;
535 #[clippy::version = "1.63.0"]
538 "detects `as _` conversion"
541 declare_clippy_lint! {
543 /// Checks for the usage of `&expr as *const T` or
544 /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
545 /// `ptr::addr_of_mut` instead.
547 /// ### Why is this bad?
548 /// This would improve readability and avoid creating a reference
549 /// that points to an uninitialized value or unaligned place.
550 /// Read the `ptr::addr_of` docs for more information.
555 /// let p = &val as *const i32;
557 /// let mut val_mut = 1;
558 /// let p_mut = &mut val_mut as *mut i32;
563 /// let p = std::ptr::addr_of!(val);
565 /// let mut val_mut = 1;
566 /// let p_mut = std::ptr::addr_of_mut!(val_mut);
568 #[clippy::version = "1.60.0"]
571 "borrowing just to cast to a raw pointer"
573 declare_clippy_lint! {
575 /// Checks for a raw slice being cast to a slice pointer
577 /// ### Why is this bad?
578 /// This can result in multiple `&mut` references to the same location when only a pointer is
580 /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
581 /// the same [safety requirements] to be upheld.
585 /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
586 /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
590 /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
591 /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
593 /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
594 #[clippy::version = "1.64.0"]
595 pub CAST_SLICE_FROM_RAW_PARTS,
597 "casting a slice created from a pointer and length to a slice pointer"
600 declare_clippy_lint! {
602 /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer
604 /// ### Why is this bad?
605 /// Since `as_ptr` took a `&self`, the pointer won't have write permissions, making it
606 /// unlikely that having it as a mutable pointer is correct.
610 /// let string = String::with_capacity(1);
611 /// let ptr = string.as_ptr() as *mut _;
612 /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
616 /// let mut string = String::with_capacity(1);
617 /// let string = string.as_mut_ptr();
618 /// unsafe { ptr.write(4) };
620 #[clippy::version = "1.66.0"]
623 "casting the result of the `&self`-taking as_ptr to a mutabe point"
627 msrv: Option<RustcVersion>,
632 pub fn new(msrv: Option<RustcVersion>) -> Self {
637 impl_lint_pass!(Casts => [
640 CAST_POSSIBLE_TRUNCATION,
645 CAST_SLICE_DIFFERENT_SIZES,
647 FN_TO_NUMERIC_CAST_ANY,
649 FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
652 CAST_ENUM_TRUNCATION,
653 CAST_ENUM_CONSTRUCTOR,
654 CAST_ABS_TO_UNSIGNED,
657 CAST_SLICE_FROM_RAW_PARTS,
661 impl<'tcx> LateLintPass<'tcx> for Casts {
662 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
663 if !in_external_macro(cx.sess(), expr.span) {
664 ptr_as_ptr::check(cx, expr, self.msrv);
667 if expr.span.from_expansion() {
671 if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
672 if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
675 let (cast_from, cast_to) = (
676 cx.typeck_results().expr_ty(cast_expr),
677 cx.typeck_results().expr_ty(expr),
680 if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
683 cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
684 as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to);
685 fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
686 fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
687 fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
689 if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
690 cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
691 if cast_from.is_numeric() {
692 cast_possible_wrap::check(cx, expr, cast_from, cast_to);
693 cast_precision_loss::check(cx, expr, cast_from, cast_to);
694 cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
695 cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
697 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
698 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
701 as_underscore::check(cx, expr, cast_to_hir);
703 if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
704 borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
708 cast_ref_to_mut::check(cx, expr);
709 cast_ptr_alignment::check(cx, expr);
710 char_lit_as_u8::check(cx, expr);
711 ptr_as_ptr::check(cx, expr, self.msrv);
712 cast_slice_different_sizes::check(cx, expr, self.msrv);
715 extract_msrv_attr!(LateContext);