]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 rustc_hir::{Expr, ExprKind}; | |
16 | use rustc_lint::{LateContext, LateLintPass, LintContext}; | |
17 | use rustc_middle::lint::in_external_macro; | |
18 | use rustc_semver::RustcVersion; | |
19 | use rustc_session::{declare_tool_lint, impl_lint_pass}; | |
20 | ||
21 | use crate::utils::is_hir_ty_cfg_dependant; | |
22 | ||
23 | declare_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 | ||
48 | declare_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 | ||
69 | declare_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 | ||
91 | declare_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 | ||
114 | declare_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 | ||
146 | declare_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 | ||
171 | declare_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 | ||
195 | declare_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 | ||
222 | declare_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 | ||
252 | declare_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 | ||
286 | declare_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 | ||
313 | declare_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 | ||
344 | pub struct Casts { | |
345 | msrv: Option<RustcVersion>, | |
346 | } | |
347 | ||
348 | impl Casts { | |
349 | #[must_use] | |
350 | pub fn new(msrv: Option<RustcVersion>) -> Self { | |
351 | Self { msrv } | |
352 | } | |
353 | } | |
354 | ||
355 | impl_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 | ||
370 | impl<'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 | } |