]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/casts/mod.rs
Rollup merge of #95376 - WaffleLapkin:drain_keep_rest, r=dtolnay
[rust.git] / src / tools / clippy / clippy_lints / src / casts / mod.rs
1 mod cast_abs_to_unsigned;
2 mod cast_enum_constructor;
3 mod cast_lossless;
4 mod cast_possible_truncation;
5 mod cast_possible_wrap;
6 mod cast_precision_loss;
7 mod cast_ptr_alignment;
8 mod cast_ref_to_mut;
9 mod cast_sign_loss;
10 mod cast_slice_different_sizes;
11 mod char_lit_as_u8;
12 mod fn_to_numeric_cast;
13 mod fn_to_numeric_cast_any;
14 mod fn_to_numeric_cast_with_truncation;
15 mod ptr_as_ptr;
16 mod unnecessary_cast;
17 mod utils;
18
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};
25
26 declare_clippy_lint! {
27     /// ### What it does
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.
32     ///
33     /// Basically, this warns on casting any integer with 32 or more bits to `f32`
34     /// or any 64-bit integer to `f64`.
35     ///
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.
40     ///
41     /// ### Example
42     /// ```rust
43     /// let x = u64::MAX;
44     /// x as f64;
45     /// ```
46     #[clippy::version = "pre 1.29.0"]
47     pub CAST_PRECISION_LOSS,
48     pedantic,
49     "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
50 }
51
52 declare_clippy_lint! {
53     /// ### What it does
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.
58     ///
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.
62     ///
63     /// ### Example
64     /// ```rust
65     /// let y: i8 = -1;
66     /// y as u128; // will return 18446744073709551615
67     /// ```
68     #[clippy::version = "pre 1.29.0"]
69     pub CAST_SIGN_LOSS,
70     pedantic,
71     "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
72 }
73
74 declare_clippy_lint! {
75     /// ### What it does
76     /// Checks for casts between numerical types that may
77     /// truncate large values. This is expected behavior, so the cast is `Allow` by
78     /// default.
79     ///
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.
84     ///
85     /// ### Example
86     /// ```rust
87     /// fn as_u8(x: u64) -> u8 {
88     ///     x as u8
89     /// }
90     /// ```
91     #[clippy::version = "pre 1.29.0"]
92     pub CAST_POSSIBLE_TRUNCATION,
93     pedantic,
94     "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
95 }
96
97 declare_clippy_lint! {
98     /// ### What it does
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.
105     ///
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
109     /// example below.
110     ///
111     /// ### Example
112     /// ```rust
113     /// u32::MAX as i32; // will yield a value of `-1`
114     /// ```
115     #[clippy::version = "pre 1.29.0"]
116     pub CAST_POSSIBLE_WRAP,
117     pedantic,
118     "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
119 }
120
121 declare_clippy_lint! {
122     /// ### What it does
123     /// Checks for casts between numerical types that may
124     /// be replaced by safe conversion functions.
125     ///
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.
133     ///
134     /// ### Example
135     /// ```rust
136     /// fn as_u64(x: u8) -> u64 {
137     ///     x as u64
138     /// }
139     /// ```
140     ///
141     /// Using `::from` would look like this:
142     ///
143     /// ```rust
144     /// fn as_u64(x: u8) -> u64 {
145     ///     u64::from(x)
146     /// }
147     /// ```
148     #[clippy::version = "pre 1.29.0"]
149     pub CAST_LOSSLESS,
150     pedantic,
151     "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
152 }
153
154 declare_clippy_lint! {
155     /// ### What it does
156     /// Checks for casts to the same type, casts of int literals to integer types
157     /// and casts of float literals to float types.
158     ///
159     /// ### Why is this bad?
160     /// It's just unnecessary.
161     ///
162     /// ### Example
163     /// ```rust
164     /// let _ = 2i32 as i32;
165     /// let _ = 0.5 as f32;
166     /// ```
167     ///
168     /// Better:
169     ///
170     /// ```rust
171     /// let _ = 2_i32;
172     /// let _ = 0.5_f32;
173     /// ```
174     #[clippy::version = "pre 1.29.0"]
175     pub UNNECESSARY_CAST,
176     complexity,
177     "cast to the same type, e.g., `x as i32` where `x: i32`"
178 }
179
180 declare_clippy_lint! {
181     /// ### What it does
182     /// Checks for casts, using `as` or `pointer::cast`,
183     /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
184     ///
185     /// ### Why is this bad?
186     /// Dereferencing the resulting pointer may be undefined
187     /// behavior.
188     ///
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.
193     ///
194     /// ### Example
195     /// ```rust
196     /// let _ = (&1u8 as *const u8) as *const u16;
197     /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
198     ///
199     /// (&1u8 as *const u8).cast::<u16>();
200     /// (&mut 1u8 as *mut u8).cast::<u16>();
201     /// ```
202     #[clippy::version = "pre 1.29.0"]
203     pub CAST_PTR_ALIGNMENT,
204     pedantic,
205     "cast from a pointer to a more-strictly-aligned pointer"
206 }
207
208 declare_clippy_lint! {
209     /// ### What it does
210     /// Checks for casts of function pointers to something other than usize
211     ///
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
217     ///
218     /// Casting to isize also doesn't make sense since there are no signed addresses.
219     ///
220     /// ### Example
221     /// ```rust
222     /// fn fun() -> i32 { 1 }
223     /// let _ = fun as i64;
224     /// ```
225     ///
226     /// Use instead:
227     /// ```rust
228     /// # fn fun() -> i32 { 1 }
229     /// let _ = fun as usize;
230     /// ```
231     #[clippy::version = "pre 1.29.0"]
232     pub FN_TO_NUMERIC_CAST,
233     style,
234     "casting a function pointer to a numeric type other than usize"
235 }
236
237 declare_clippy_lint! {
238     /// ### What it does
239     /// Checks for casts of a function pointer to a numeric type not wide enough to
240     /// store address.
241     ///
242     /// ### Why is this bad?
243     /// Such a cast discards some bits of the function's address. If this is intended, it would be more
244     /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
245     /// a comment) to perform the truncation.
246     ///
247     /// ### Example
248     /// ```rust
249     /// fn fn1() -> i16 {
250     ///     1
251     /// };
252     /// let _ = fn1 as i32;
253     /// ```
254     ///
255     /// Use instead:
256     /// ```rust
257     /// // Cast to usize first, then comment with the reason for the truncation
258     /// fn fn1() -> i16 {
259     ///     1
260     /// };
261     /// let fn_ptr = fn1 as usize;
262     /// let fn_ptr_truncated = fn_ptr as i32;
263     /// ```
264     #[clippy::version = "pre 1.29.0"]
265     pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
266     style,
267     "casting a function pointer to a numeric type not wide enough to store the address"
268 }
269
270 declare_clippy_lint! {
271     /// ### What it does
272     /// Checks for casts of a function pointer to any integer type.
273     ///
274     /// ### Why is this bad?
275     /// Casting a function pointer to an integer can have surprising results and can occur
276     /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
277     /// low-level with function pointers then you can opt-out of casting functions to integers in
278     /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
279     /// pointer casts in your code.
280     ///
281     /// ### Example
282     /// ```rust
283     /// // fn1 is cast as `usize`
284     /// fn fn1() -> u16 {
285     ///     1
286     /// };
287     /// let _ = fn1 as usize;
288     /// ```
289     ///
290     /// Use instead:
291     /// ```rust
292     /// // maybe you intended to call the function?
293     /// fn fn2() -> u16 {
294     ///     1
295     /// };
296     /// let _ = fn2() as usize;
297     ///
298     /// // or
299     ///
300     /// // maybe you intended to cast it to a function type?
301     /// fn fn3() -> u16 {
302     ///     1
303     /// }
304     /// let _ = fn3 as fn() -> u16;
305     /// ```
306     #[clippy::version = "1.58.0"]
307     pub FN_TO_NUMERIC_CAST_ANY,
308     restriction,
309     "casting a function pointer to any integer type"
310 }
311
312 declare_clippy_lint! {
313     /// ### What it does
314     /// Checks for casts of `&T` to `&mut T` anywhere in the code.
315     ///
316     /// ### Why is this bad?
317     /// It’s basically guaranteed to be undefined behavior.
318     /// `UnsafeCell` is the only way to obtain aliasable data that is considered
319     /// mutable.
320     ///
321     /// ### Example
322     /// ```rust,ignore
323     /// fn x(r: &i32) {
324     ///     unsafe {
325     ///         *(r as *const _ as *mut _) += 1;
326     ///     }
327     /// }
328     /// ```
329     ///
330     /// Instead consider using interior mutability types.
331     ///
332     /// ```rust
333     /// use std::cell::UnsafeCell;
334     ///
335     /// fn x(r: &UnsafeCell<i32>) {
336     ///     unsafe {
337     ///         *r.get() += 1;
338     ///     }
339     /// }
340     /// ```
341     #[clippy::version = "1.33.0"]
342     pub CAST_REF_TO_MUT,
343     correctness,
344     "a cast of reference to a mutable pointer"
345 }
346
347 declare_clippy_lint! {
348     /// ### What it does
349     /// Checks for expressions where a character literal is cast
350     /// to `u8` and suggests using a byte literal instead.
351     ///
352     /// ### Why is this bad?
353     /// In general, casting values to smaller types is
354     /// error-prone and should be avoided where possible. In the particular case of
355     /// converting a character literal to u8, it is easy to avoid by just using a
356     /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
357     /// than `'a' as u8`.
358     ///
359     /// ### Example
360     /// ```rust,ignore
361     /// 'x' as u8
362     /// ```
363     ///
364     /// A better version, using the byte literal:
365     ///
366     /// ```rust,ignore
367     /// b'x'
368     /// ```
369     #[clippy::version = "pre 1.29.0"]
370     pub CHAR_LIT_AS_U8,
371     complexity,
372     "casting a character literal to `u8` truncates"
373 }
374
375 declare_clippy_lint! {
376     /// ### What it does
377     /// Checks for `as` casts between raw pointers without changing its mutability,
378     /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
379     ///
380     /// ### Why is this bad?
381     /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
382     /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
383     ///
384     /// ### Example
385     /// ```rust
386     /// let ptr: *const u32 = &42_u32;
387     /// let mut_ptr: *mut u32 = &mut 42_u32;
388     /// let _ = ptr as *const i32;
389     /// let _ = mut_ptr as *mut i32;
390     /// ```
391     /// Use instead:
392     /// ```rust
393     /// let ptr: *const u32 = &42_u32;
394     /// let mut_ptr: *mut u32 = &mut 42_u32;
395     /// let _ = ptr.cast::<i32>();
396     /// let _ = mut_ptr.cast::<i32>();
397     /// ```
398     #[clippy::version = "1.51.0"]
399     pub PTR_AS_PTR,
400     pedantic,
401     "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
402 }
403
404 declare_clippy_lint! {
405     /// ### What it does
406     /// Checks for casts from an enum type to an integral type which will definitely truncate the
407     /// value.
408     ///
409     /// ### Why is this bad?
410     /// The resulting integral value will not match the value of the variant it came from.
411     ///
412     /// ### Example
413     /// ```rust
414     /// enum E { X = 256 };
415     /// let _ = E::X as u8;
416     /// ```
417     #[clippy::version = "1.61.0"]
418     pub CAST_ENUM_TRUNCATION,
419     suspicious,
420     "casts from an enum type to an integral type which will truncate the value"
421 }
422
423 declare_clippy_lint! {
424     /// ### What it does
425     /// Checks for `as` casts between raw pointers to slices with differently sized elements.
426     ///
427     /// ### Why is this bad?
428     /// The produced raw pointer to a slice does not update its length metadata. The produced
429     /// pointer will point to a different number of bytes than the original pointer because the
430     /// length metadata of a raw slice pointer is in elements rather than bytes.
431     /// Producing a slice reference from the raw pointer will either create a slice with
432     /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
433     ///
434     /// ### Example
435     /// // Missing data
436     /// ```rust
437     /// let a = [1_i32, 2, 3, 4];
438     /// let p = &a as *const [i32] as *const [u8];
439     /// unsafe {
440     ///     println!("{:?}", &*p);
441     /// }
442     /// ```
443     /// // Undefined Behavior (note: also potential alignment issues)
444     /// ```rust
445     /// let a = [1_u8, 2, 3, 4];
446     /// let p = &a as *const [u8] as *const [u32];
447     /// unsafe {
448     ///     println!("{:?}", &*p);
449     /// }
450     /// ```
451     /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
452     /// ```rust
453     /// let a = [1_i32, 2, 3, 4];
454     /// let old_ptr = &a as *const [i32];
455     /// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
456     /// // The length comes from the known length of 4 i32s times the 4 bytes per i32
457     /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
458     /// unsafe {
459     ///     println!("{:?}", &*new_ptr);
460     /// }
461     /// ```
462     #[clippy::version = "1.61.0"]
463     pub CAST_SLICE_DIFFERENT_SIZES,
464     correctness,
465     "casting using `as` between raw pointers to slices of types with different sizes"
466 }
467
468 declare_clippy_lint! {
469     /// ### What it does
470     /// Checks for casts from an enum tuple constructor to an integer.
471     ///
472     /// ### Why is this bad?
473     /// The cast is easily confused with casting a c-like enum value to an integer.
474     ///
475     /// ### Example
476     /// ```rust
477     /// enum E { X(i32) };
478     /// let _ = E::X as usize;
479     /// ```
480     #[clippy::version = "1.61.0"]
481     pub CAST_ENUM_CONSTRUCTOR,
482     suspicious,
483     "casts from an enum tuple constructor to an integer"
484 }
485
486 declare_clippy_lint! {
487     /// ### What it does
488     /// Checks for uses of the `abs()` method that cast the result to unsigned.
489     ///
490     /// ### Why is this bad?
491     /// The `unsigned_abs()` method avoids panic when called on the MIN value.
492     ///
493     /// ### Example
494     /// ```rust
495     /// let x: i32 = -42;
496     /// let y: u32 = x.abs() as u32;
497     /// ```
498     /// Use instead:
499     /// ```rust
500     /// let x: i32 = -42;
501     /// let y: u32 = x.unsigned_abs();
502     /// ```
503     #[clippy::version = "1.62.0"]
504     pub CAST_ABS_TO_UNSIGNED,
505     suspicious,
506     "casting the result of `abs()` to an unsigned integer can panic"
507 }
508
509 pub struct Casts {
510     msrv: Option<RustcVersion>,
511 }
512
513 impl Casts {
514     #[must_use]
515     pub fn new(msrv: Option<RustcVersion>) -> Self {
516         Self { msrv }
517     }
518 }
519
520 impl_lint_pass!(Casts => [
521     CAST_PRECISION_LOSS,
522     CAST_SIGN_LOSS,
523     CAST_POSSIBLE_TRUNCATION,
524     CAST_POSSIBLE_WRAP,
525     CAST_LOSSLESS,
526     CAST_REF_TO_MUT,
527     CAST_PTR_ALIGNMENT,
528     CAST_SLICE_DIFFERENT_SIZES,
529     UNNECESSARY_CAST,
530     FN_TO_NUMERIC_CAST_ANY,
531     FN_TO_NUMERIC_CAST,
532     FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
533     CHAR_LIT_AS_U8,
534     PTR_AS_PTR,
535     CAST_ENUM_TRUNCATION,
536     CAST_ENUM_CONSTRUCTOR,
537     CAST_ABS_TO_UNSIGNED
538 ]);
539
540 impl<'tcx> LateLintPass<'tcx> for Casts {
541     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
542         if !in_external_macro(cx.sess(), expr.span) {
543             ptr_as_ptr::check(cx, expr, self.msrv);
544         }
545
546         if expr.span.from_expansion() {
547             return;
548         }
549
550         if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
551             if is_hir_ty_cfg_dependant(cx, cast_to) {
552                 return;
553             }
554             let (cast_from, cast_to) = (
555                 cx.typeck_results().expr_ty(cast_expr),
556                 cx.typeck_results().expr_ty(expr),
557             );
558
559             if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
560                 return;
561             }
562
563             fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
564             fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
565             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
566
567             if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
568                 cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
569                 if cast_from.is_numeric() {
570                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
571                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
572                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
573                     cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
574                 }
575                 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
576                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
577             }
578         }
579
580         cast_ref_to_mut::check(cx, expr);
581         cast_ptr_alignment::check(cx, expr);
582         char_lit_as_u8::check(cx, expr);
583         ptr_as_ptr::check(cx, expr, self.msrv);
584         cast_slice_different_sizes::check(cx, expr, self.msrv);
585     }
586
587     extract_msrv_attr!(LateContext);
588 }