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