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