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