]>
Commit | Line | Data |
---|---|---|
4b012472 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
f20569fa | 2 | use rustc_hir::Expr; |
4b012472 | 3 | use rustc_lint::LateContext; |
f20569fa XL |
4 | use rustc_middle::ty::Ty; |
5 | ||
f20569fa XL |
6 | use super::{utils, CAST_POSSIBLE_WRAP}; |
7 | ||
fe692bf9 FG |
8 | // this should be kept in sync with the allowed bit widths of `usize` and `isize` |
9 | const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64]; | |
10 | ||
11 | // whether the lint should be emitted, and the required pointer size, if it matters | |
12 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
13 | enum EmitState { | |
14 | NoLint, | |
15 | LintAlways, | |
16 | LintOnPtrSize(u64), | |
17 | } | |
18 | ||
f20569fa XL |
19 | pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { |
20 | if !(cast_from.is_integral() && cast_to.is_integral()) { | |
21 | return; | |
22 | } | |
23 | ||
fe692bf9 FG |
24 | // emit a lint if a cast is: |
25 | // 1. unsigned to signed | |
26 | // and | |
27 | // 2. either: | |
28 | // | |
29 | // 2a. between two types of constant size that are always the same size | |
30 | // 2b. between one target-dependent size and one constant size integer, | |
31 | // and the constant integer is in the allowed set of target dependent sizes | |
32 | // (the ptr size could be chosen to be the same as the constant size) | |
33 | ||
34 | if cast_from.is_signed() || !cast_to.is_signed() { | |
35 | return; | |
36 | } | |
37 | ||
f20569fa XL |
38 | let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); |
39 | let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); | |
40 | ||
fe692bf9 FG |
41 | let should_lint = match (cast_from.is_ptr_sized_integral(), cast_to.is_ptr_sized_integral()) { |
42 | (true, true) => { | |
43 | // casts between two ptr sized integers are trivially always the same size | |
44 | // so do not depend on any specific pointer size to be the same | |
45 | EmitState::LintAlways | |
46 | }, | |
47 | (true, false) => { | |
48 | // the first type is `usize` and the second is a constant sized signed integer | |
49 | if ALLOWED_POINTER_SIZES.contains(&to_nbits) { | |
50 | EmitState::LintOnPtrSize(to_nbits) | |
51 | } else { | |
52 | EmitState::NoLint | |
53 | } | |
54 | }, | |
55 | (false, true) => { | |
56 | // the first type is a constant sized unsigned integer, and the second is `isize` | |
57 | if ALLOWED_POINTER_SIZES.contains(&from_nbits) { | |
58 | EmitState::LintOnPtrSize(from_nbits) | |
59 | } else { | |
60 | EmitState::NoLint | |
61 | } | |
62 | }, | |
63 | (false, false) => { | |
64 | // the types are both a constant known size | |
65 | // and do not depend on any specific pointer size to be the same | |
66 | if from_nbits == to_nbits { | |
67 | EmitState::LintAlways | |
f20569fa | 68 | } else { |
fe692bf9 FG |
69 | EmitState::NoLint |
70 | } | |
71 | }, | |
72 | }; | |
73 | ||
74 | let message = match should_lint { | |
75 | EmitState::NoLint => return, | |
76 | EmitState::LintAlways => format!("casting `{cast_from}` to `{cast_to}` may wrap around the value"), | |
77 | EmitState::LintOnPtrSize(ptr_size) => format!( | |
78 | "casting `{cast_from}` to `{cast_to}` may wrap around the value on targets with {ptr_size}-bit wide pointers", | |
f20569fa XL |
79 | ), |
80 | }; | |
81 | ||
e8be2606 | 82 | span_lint_and_then(cx, CAST_POSSIBLE_WRAP, expr.span, message, |diag| { |
fe692bf9 FG |
83 | if let EmitState::LintOnPtrSize(16) = should_lint { |
84 | diag | |
4b012472 FG |
85 | .note("`usize` and `isize` may be as small as 16 bits on some platforms") |
86 | .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); | |
87 | }; | |
fe692bf9 | 88 | }); |
f20569fa | 89 | } |