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