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