]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/casts/mod.rs
Merge commit '7248d06384c6a90de58c04c1f46be88821278d8b' into sync-from-clippy
[rust.git] / src / tools / clippy / clippy_lints / src / casts / mod.rs
1 mod as_underscore;
2 mod borrow_as_ptr;
3 mod cast_abs_to_unsigned;
4 mod cast_enum_constructor;
5 mod cast_lossless;
6 mod cast_possible_truncation;
7 mod cast_possible_wrap;
8 mod cast_precision_loss;
9 mod cast_ptr_alignment;
10 mod cast_ref_to_mut;
11 mod cast_sign_loss;
12 mod cast_slice_different_sizes;
13 mod cast_slice_from_raw_parts;
14 mod char_lit_as_u8;
15 mod fn_to_numeric_cast;
16 mod fn_to_numeric_cast_any;
17 mod fn_to_numeric_cast_with_truncation;
18 mod ptr_as_ptr;
19 mod unnecessary_cast;
20 mod utils;
21
22 use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
23 use rustc_hir::{Expr, ExprKind};
24 use rustc_lint::{LateContext, LateLintPass, LintContext};
25 use rustc_middle::lint::in_external_macro;
26 use rustc_semver::RustcVersion;
27 use rustc_session::{declare_tool_lint, impl_lint_pass};
28
29 declare_clippy_lint! {
30     /// ### What it does
31     /// Checks for casts from any numerical to a float type where
32     /// the receiving type cannot store all values from the original type without
33     /// rounding errors. This possible rounding is to be expected, so this lint is
34     /// `Allow` by default.
35     ///
36     /// Basically, this warns on casting any integer with 32 or more bits to `f32`
37     /// or any 64-bit integer to `f64`.
38     ///
39     /// ### Why is this bad?
40     /// It's not bad at all. But in some applications it can be
41     /// helpful to know where precision loss can take place. This lint can help find
42     /// those places in the code.
43     ///
44     /// ### Example
45     /// ```rust
46     /// let x = u64::MAX;
47     /// x as f64;
48     /// ```
49     #[clippy::version = "pre 1.29.0"]
50     pub CAST_PRECISION_LOSS,
51     pedantic,
52     "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
53 }
54
55 declare_clippy_lint! {
56     /// ### What it does
57     /// Checks for casts from a signed to an unsigned numerical
58     /// type. In this case, negative values wrap around to large positive values,
59     /// which can be quite surprising in practice. However, as the cast works as
60     /// defined, this lint is `Allow` by default.
61     ///
62     /// ### Why is this bad?
63     /// Possibly surprising results. You can activate this lint
64     /// as a one-time check to see where numerical wrapping can arise.
65     ///
66     /// ### Example
67     /// ```rust
68     /// let y: i8 = -1;
69     /// y as u128; // will return 18446744073709551615
70     /// ```
71     #[clippy::version = "pre 1.29.0"]
72     pub CAST_SIGN_LOSS,
73     pedantic,
74     "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
75 }
76
77 declare_clippy_lint! {
78     /// ### What it does
79     /// Checks for casts between numerical types that may
80     /// truncate large values. This is expected behavior, so the cast is `Allow` by
81     /// default.
82     ///
83     /// ### Why is this bad?
84     /// In some problem domains, it is good practice to avoid
85     /// truncation. This lint can be activated to help assess where additional
86     /// checks could be beneficial.
87     ///
88     /// ### Example
89     /// ```rust
90     /// fn as_u8(x: u64) -> u8 {
91     ///     x as u8
92     /// }
93     /// ```
94     #[clippy::version = "pre 1.29.0"]
95     pub CAST_POSSIBLE_TRUNCATION,
96     pedantic,
97     "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
98 }
99
100 declare_clippy_lint! {
101     /// ### What it does
102     /// Checks for casts from an unsigned type to a signed type of
103     /// the same size. Performing such a cast is a 'no-op' for the compiler,
104     /// i.e., nothing is changed at the bit level, and the binary representation of
105     /// the value is reinterpreted. This can cause wrapping if the value is too big
106     /// for the target signed type. However, the cast works as defined, so this lint
107     /// is `Allow` by default.
108     ///
109     /// ### Why is this bad?
110     /// While such a cast is not bad in itself, the results can
111     /// be surprising when this is not the intended behavior, as demonstrated by the
112     /// example below.
113     ///
114     /// ### Example
115     /// ```rust
116     /// u32::MAX as i32; // will yield a value of `-1`
117     /// ```
118     #[clippy::version = "pre 1.29.0"]
119     pub CAST_POSSIBLE_WRAP,
120     pedantic,
121     "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
122 }
123
124 declare_clippy_lint! {
125     /// ### What it does
126     /// Checks for casts between numerical types that may
127     /// be replaced by safe conversion functions.
128     ///
129     /// ### Why is this bad?
130     /// Rust's `as` keyword will perform many kinds of
131     /// conversions, including silently lossy conversions. Conversion functions such
132     /// as `i32::from` will only perform lossless conversions. Using the conversion
133     /// functions prevents conversions from turning into silent lossy conversions if
134     /// the types of the input expressions ever change, and make it easier for
135     /// people reading the code to know that the conversion is lossless.
136     ///
137     /// ### Example
138     /// ```rust
139     /// fn as_u64(x: u8) -> u64 {
140     ///     x as u64
141     /// }
142     /// ```
143     ///
144     /// Using `::from` would look like this:
145     ///
146     /// ```rust
147     /// fn as_u64(x: u8) -> u64 {
148     ///     u64::from(x)
149     /// }
150     /// ```
151     #[clippy::version = "pre 1.29.0"]
152     pub CAST_LOSSLESS,
153     pedantic,
154     "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
155 }
156
157 declare_clippy_lint! {
158     /// ### What it does
159     /// Checks for casts to the same type, casts of int literals to integer types
160     /// and casts of float literals to float types.
161     ///
162     /// ### Why is this bad?
163     /// It's just unnecessary.
164     ///
165     /// ### Example
166     /// ```rust
167     /// let _ = 2i32 as i32;
168     /// let _ = 0.5 as f32;
169     /// ```
170     ///
171     /// Better:
172     ///
173     /// ```rust
174     /// let _ = 2_i32;
175     /// let _ = 0.5_f32;
176     /// ```
177     #[clippy::version = "pre 1.29.0"]
178     pub UNNECESSARY_CAST,
179     complexity,
180     "cast to the same type, e.g., `x as i32` where `x: i32`"
181 }
182
183 declare_clippy_lint! {
184     /// ### What it does
185     /// Checks for casts, using `as` or `pointer::cast`,
186     /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
187     ///
188     /// ### Why is this bad?
189     /// Dereferencing the resulting pointer may be undefined
190     /// behavior.
191     ///
192     /// ### Known problems
193     /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
194     /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
195     /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
196     ///
197     /// ### Example
198     /// ```rust
199     /// let _ = (&1u8 as *const u8) as *const u16;
200     /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
201     ///
202     /// (&1u8 as *const u8).cast::<u16>();
203     /// (&mut 1u8 as *mut u8).cast::<u16>();
204     /// ```
205     #[clippy::version = "pre 1.29.0"]
206     pub CAST_PTR_ALIGNMENT,
207     pedantic,
208     "cast from a pointer to a more-strictly-aligned pointer"
209 }
210
211 declare_clippy_lint! {
212     /// ### What it does
213     /// Checks for casts of function pointers to something other than usize
214     ///
215     /// ### Why is this bad?
216     /// Casting a function pointer to anything other than usize/isize is not portable across
217     /// architectures, because you end up losing bits if the target type is too small or end up with a
218     /// bunch of extra bits that waste space and add more instructions to the final binary than
219     /// strictly necessary for the problem
220     ///
221     /// Casting to isize also doesn't make sense since there are no signed addresses.
222     ///
223     /// ### Example
224     /// ```rust
225     /// fn fun() -> i32 { 1 }
226     /// let _ = fun as i64;
227     /// ```
228     ///
229     /// Use instead:
230     /// ```rust
231     /// # fn fun() -> i32 { 1 }
232     /// let _ = fun as usize;
233     /// ```
234     #[clippy::version = "pre 1.29.0"]
235     pub FN_TO_NUMERIC_CAST,
236     style,
237     "casting a function pointer to a numeric type other than usize"
238 }
239
240 declare_clippy_lint! {
241     /// ### What it does
242     /// Checks for casts of a function pointer to a numeric type not wide enough to
243     /// store address.
244     ///
245     /// ### Why is this bad?
246     /// Such a cast discards some bits of the function's address. If this is intended, it would be more
247     /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
248     /// a comment) to perform the truncation.
249     ///
250     /// ### Example
251     /// ```rust
252     /// fn fn1() -> i16 {
253     ///     1
254     /// };
255     /// let _ = fn1 as i32;
256     /// ```
257     ///
258     /// Use instead:
259     /// ```rust
260     /// // Cast to usize first, then comment with the reason for the truncation
261     /// fn fn1() -> i16 {
262     ///     1
263     /// };
264     /// let fn_ptr = fn1 as usize;
265     /// let fn_ptr_truncated = fn_ptr as i32;
266     /// ```
267     #[clippy::version = "pre 1.29.0"]
268     pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
269     style,
270     "casting a function pointer to a numeric type not wide enough to store the address"
271 }
272
273 declare_clippy_lint! {
274     /// ### What it does
275     /// Checks for casts of a function pointer to any integer type.
276     ///
277     /// ### Why is this bad?
278     /// Casting a function pointer to an integer can have surprising results and can occur
279     /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
280     /// low-level with function pointers then you can opt-out of casting functions to integers in
281     /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
282     /// pointer casts in your code.
283     ///
284     /// ### Example
285     /// ```rust
286     /// // fn1 is cast as `usize`
287     /// fn fn1() -> u16 {
288     ///     1
289     /// };
290     /// let _ = fn1 as usize;
291     /// ```
292     ///
293     /// Use instead:
294     /// ```rust
295     /// // maybe you intended to call the function?
296     /// fn fn2() -> u16 {
297     ///     1
298     /// };
299     /// let _ = fn2() as usize;
300     ///
301     /// // or
302     ///
303     /// // maybe you intended to cast it to a function type?
304     /// fn fn3() -> u16 {
305     ///     1
306     /// }
307     /// let _ = fn3 as fn() -> u16;
308     /// ```
309     #[clippy::version = "1.58.0"]
310     pub FN_TO_NUMERIC_CAST_ANY,
311     restriction,
312     "casting a function pointer to any integer type"
313 }
314
315 declare_clippy_lint! {
316     /// ### What it does
317     /// Checks for casts of `&T` to `&mut T` anywhere in the code.
318     ///
319     /// ### Why is this bad?
320     /// It’s basically guaranteed to be undefined behavior.
321     /// `UnsafeCell` is the only way to obtain aliasable data that is considered
322     /// mutable.
323     ///
324     /// ### Example
325     /// ```rust,ignore
326     /// fn x(r: &i32) {
327     ///     unsafe {
328     ///         *(r as *const _ as *mut _) += 1;
329     ///     }
330     /// }
331     /// ```
332     ///
333     /// Instead consider using interior mutability types.
334     ///
335     /// ```rust
336     /// use std::cell::UnsafeCell;
337     ///
338     /// fn x(r: &UnsafeCell<i32>) {
339     ///     unsafe {
340     ///         *r.get() += 1;
341     ///     }
342     /// }
343     /// ```
344     #[clippy::version = "1.33.0"]
345     pub CAST_REF_TO_MUT,
346     correctness,
347     "a cast of reference to a mutable pointer"
348 }
349
350 declare_clippy_lint! {
351     /// ### What it does
352     /// Checks for expressions where a character literal is cast
353     /// to `u8` and suggests using a byte literal instead.
354     ///
355     /// ### Why is this bad?
356     /// In general, casting values to smaller types is
357     /// error-prone and should be avoided where possible. In the particular case of
358     /// converting a character literal to u8, it is easy to avoid by just using a
359     /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
360     /// than `'a' as u8`.
361     ///
362     /// ### Example
363     /// ```rust,ignore
364     /// 'x' as u8
365     /// ```
366     ///
367     /// A better version, using the byte literal:
368     ///
369     /// ```rust,ignore
370     /// b'x'
371     /// ```
372     #[clippy::version = "pre 1.29.0"]
373     pub CHAR_LIT_AS_U8,
374     complexity,
375     "casting a character literal to `u8` truncates"
376 }
377
378 declare_clippy_lint! {
379     /// ### What it does
380     /// Checks for `as` casts between raw pointers without changing its mutability,
381     /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
382     ///
383     /// ### Why is this bad?
384     /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
385     /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
386     ///
387     /// ### Example
388     /// ```rust
389     /// let ptr: *const u32 = &42_u32;
390     /// let mut_ptr: *mut u32 = &mut 42_u32;
391     /// let _ = ptr as *const i32;
392     /// let _ = mut_ptr as *mut i32;
393     /// ```
394     /// Use instead:
395     /// ```rust
396     /// let ptr: *const u32 = &42_u32;
397     /// let mut_ptr: *mut u32 = &mut 42_u32;
398     /// let _ = ptr.cast::<i32>();
399     /// let _ = mut_ptr.cast::<i32>();
400     /// ```
401     #[clippy::version = "1.51.0"]
402     pub PTR_AS_PTR,
403     pedantic,
404     "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
405 }
406
407 declare_clippy_lint! {
408     /// ### What it does
409     /// Checks for casts from an enum type to an integral type which will definitely truncate the
410     /// value.
411     ///
412     /// ### Why is this bad?
413     /// The resulting integral value will not match the value of the variant it came from.
414     ///
415     /// ### Example
416     /// ```rust
417     /// enum E { X = 256 };
418     /// let _ = E::X as u8;
419     /// ```
420     #[clippy::version = "1.61.0"]
421     pub CAST_ENUM_TRUNCATION,
422     suspicious,
423     "casts from an enum type to an integral type which will truncate the value"
424 }
425
426 declare_clippy_lint! {
427     /// ### What it does
428     /// Checks for `as` casts between raw pointers to slices with differently sized elements.
429     ///
430     /// ### Why is this bad?
431     /// The produced raw pointer to a slice does not update its length metadata. The produced
432     /// pointer will point to a different number of bytes than the original pointer because the
433     /// length metadata of a raw slice pointer is in elements rather than bytes.
434     /// Producing a slice reference from the raw pointer will either create a slice with
435     /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
436     ///
437     /// ### Example
438     /// // Missing data
439     /// ```rust
440     /// let a = [1_i32, 2, 3, 4];
441     /// let p = &a as *const [i32] as *const [u8];
442     /// unsafe {
443     ///     println!("{:?}", &*p);
444     /// }
445     /// ```
446     /// // Undefined Behavior (note: also potential alignment issues)
447     /// ```rust
448     /// let a = [1_u8, 2, 3, 4];
449     /// let p = &a as *const [u8] as *const [u32];
450     /// unsafe {
451     ///     println!("{:?}", &*p);
452     /// }
453     /// ```
454     /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
455     /// ```rust
456     /// let a = [1_i32, 2, 3, 4];
457     /// let old_ptr = &a as *const [i32];
458     /// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
459     /// // The length comes from the known length of 4 i32s times the 4 bytes per i32
460     /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
461     /// unsafe {
462     ///     println!("{:?}", &*new_ptr);
463     /// }
464     /// ```
465     #[clippy::version = "1.61.0"]
466     pub CAST_SLICE_DIFFERENT_SIZES,
467     correctness,
468     "casting using `as` between raw pointers to slices of types with different sizes"
469 }
470
471 declare_clippy_lint! {
472     /// ### What it does
473     /// Checks for casts from an enum tuple constructor to an integer.
474     ///
475     /// ### Why is this bad?
476     /// The cast is easily confused with casting a c-like enum value to an integer.
477     ///
478     /// ### Example
479     /// ```rust
480     /// enum E { X(i32) };
481     /// let _ = E::X as usize;
482     /// ```
483     #[clippy::version = "1.61.0"]
484     pub CAST_ENUM_CONSTRUCTOR,
485     suspicious,
486     "casts from an enum tuple constructor to an integer"
487 }
488
489 declare_clippy_lint! {
490     /// ### What it does
491     /// Checks for uses of the `abs()` method that cast the result to unsigned.
492     ///
493     /// ### Why is this bad?
494     /// The `unsigned_abs()` method avoids panic when called on the MIN value.
495     ///
496     /// ### Example
497     /// ```rust
498     /// let x: i32 = -42;
499     /// let y: u32 = x.abs() as u32;
500     /// ```
501     /// Use instead:
502     /// ```rust
503     /// let x: i32 = -42;
504     /// let y: u32 = x.unsigned_abs();
505     /// ```
506     #[clippy::version = "1.62.0"]
507     pub CAST_ABS_TO_UNSIGNED,
508     suspicious,
509     "casting the result of `abs()` to an unsigned integer can panic"
510 }
511
512 declare_clippy_lint! {
513     /// ### What it does
514     /// Check for the usage of `as _` conversion using inferred type.
515     ///
516     /// ### Why is this bad?
517     /// The conversion might include lossy conversion and dangerous cast that might go
518     /// undetected due to the type being inferred.
519     ///
520     /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
521     ///
522     /// ### Example
523     /// ```rust
524     /// fn foo(n: usize) {}
525     /// let n: u16 = 256;
526     /// foo(n as _);
527     /// ```
528     /// Use instead:
529     /// ```rust
530     /// fn foo(n: usize) {}
531     /// let n: u16 = 256;
532     /// foo(n as usize);
533     /// ```
534     #[clippy::version = "1.63.0"]
535     pub AS_UNDERSCORE,
536     restriction,
537     "detects `as _` conversion"
538 }
539
540 declare_clippy_lint! {
541     /// ### What it does
542     /// Checks for the usage of `&expr as *const T` or
543     /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
544     /// `ptr::addr_of_mut` instead.
545     ///
546     /// ### Why is this bad?
547     /// This would improve readability and avoid creating a reference
548     /// that points to an uninitialized value or unaligned place.
549     /// Read the `ptr::addr_of` docs for more information.
550     ///
551     /// ### Example
552     /// ```rust
553     /// let val = 1;
554     /// let p = &val as *const i32;
555     ///
556     /// let mut val_mut = 1;
557     /// let p_mut = &mut val_mut as *mut i32;
558     /// ```
559     /// Use instead:
560     /// ```rust
561     /// let val = 1;
562     /// let p = std::ptr::addr_of!(val);
563     ///
564     /// let mut val_mut = 1;
565     /// let p_mut = std::ptr::addr_of_mut!(val_mut);
566     /// ```
567     #[clippy::version = "1.60.0"]
568     pub BORROW_AS_PTR,
569     pedantic,
570     "borrowing just to cast to a raw pointer"
571 }
572 declare_clippy_lint! {
573     /// ### What it does
574     /// Checks for a raw slice being cast to a slice pointer
575     ///
576     /// ### Why is this bad?
577     /// This can result in multiple `&mut` references to the same location when only a pointer is
578     /// required.
579     /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
580     /// the same [safety requirements] to be upheld.
581     ///
582     /// ### Example
583     /// ```rust,ignore
584     /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
585     /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
586     /// ```
587     /// Use instead:
588     /// ```rust,ignore
589     /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
590     /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
591     /// ```
592     /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
593     #[clippy::version = "1.64.0"]
594     pub CAST_SLICE_FROM_RAW_PARTS,
595     suspicious,
596     "casting a slice created from a pointer and length to a slice pointer"
597 }
598
599 pub struct Casts {
600     msrv: Option<RustcVersion>,
601 }
602
603 impl Casts {
604     #[must_use]
605     pub fn new(msrv: Option<RustcVersion>) -> Self {
606         Self { msrv }
607     }
608 }
609
610 impl_lint_pass!(Casts => [
611     CAST_PRECISION_LOSS,
612     CAST_SIGN_LOSS,
613     CAST_POSSIBLE_TRUNCATION,
614     CAST_POSSIBLE_WRAP,
615     CAST_LOSSLESS,
616     CAST_REF_TO_MUT,
617     CAST_PTR_ALIGNMENT,
618     CAST_SLICE_DIFFERENT_SIZES,
619     UNNECESSARY_CAST,
620     FN_TO_NUMERIC_CAST_ANY,
621     FN_TO_NUMERIC_CAST,
622     FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
623     CHAR_LIT_AS_U8,
624     PTR_AS_PTR,
625     CAST_ENUM_TRUNCATION,
626     CAST_ENUM_CONSTRUCTOR,
627     CAST_ABS_TO_UNSIGNED,
628     AS_UNDERSCORE,
629     BORROW_AS_PTR,
630     CAST_SLICE_FROM_RAW_PARTS
631 ]);
632
633 impl<'tcx> LateLintPass<'tcx> for Casts {
634     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
635         if !in_external_macro(cx.sess(), expr.span) {
636             ptr_as_ptr::check(cx, expr, self.msrv);
637         }
638
639         if expr.span.from_expansion() {
640             return;
641         }
642
643         if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
644             if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
645                 return;
646             }
647             let (cast_from, cast_to) = (
648                 cx.typeck_results().expr_ty(cast_expr),
649                 cx.typeck_results().expr_ty(expr),
650             );
651
652             if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
653                 return;
654             }
655             cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
656             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
657             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
658             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
659
660             if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
661                 cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
662                 if cast_from.is_numeric() {
663                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
664                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
665                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
666                     cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
667                 }
668                 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
669                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
670             }
671
672             as_underscore::check(cx, expr, cast_to_hir);
673
674             if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
675                 borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
676             }
677         }
678
679         cast_ref_to_mut::check(cx, expr);
680         cast_ptr_alignment::check(cx, expr);
681         char_lit_as_u8::check(cx, expr);
682         ptr_as_ptr::check(cx, expr, self.msrv);
683         cast_slice_different_sizes::check(cx, expr, self.msrv);
684     }
685
686     extract_msrv_attr!(LateContext);
687 }