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