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