]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! lint on C-like enums that are `repr(isize/usize)` and have values that |
2 | //! don't fit into an `i32` | |
3 | ||
ed00b5ec | 4 | use clippy_utils::consts::{mir_to_const, Constant}; |
cdc7bbd5 | 5 | use clippy_utils::diagnostics::span_lint; |
f20569fa XL |
6 | use rustc_hir::{Item, ItemKind}; |
7 | use rustc_lint::{LateContext, LateLintPass}; | |
8 | use rustc_middle::ty::util::IntTypeExt; | |
9 | use rustc_middle::ty::{self, IntTy, UintTy}; | |
4b012472 | 10 | use rustc_session::declare_lint_pass; |
f20569fa XL |
11 | |
12 | declare_clippy_lint! { | |
94222f64 XL |
13 | /// ### What it does |
14 | /// Checks for C-like enumerations that are | |
f20569fa XL |
15 | /// `repr(isize/usize)` and have values that don't fit into an `i32`. |
16 | /// | |
94222f64 XL |
17 | /// ### Why is this bad? |
18 | /// This will truncate the variant value on 32 bit | |
f20569fa XL |
19 | /// architectures, but works fine on 64 bit. |
20 | /// | |
94222f64 | 21 | /// ### Example |
ed00b5ec | 22 | /// ```no_run |
f20569fa XL |
23 | /// # #[cfg(target_pointer_width = "64")] |
24 | /// #[repr(usize)] | |
25 | /// enum NonPortable { | |
26 | /// X = 0x1_0000_0000, | |
27 | /// Y = 0, | |
28 | /// } | |
29 | /// ``` | |
a2a8927a | 30 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
31 | pub ENUM_CLIKE_UNPORTABLE_VARIANT, |
32 | correctness, | |
33 | "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`" | |
34 | } | |
35 | ||
36 | declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]); | |
37 | ||
38 | impl<'tcx> LateLintPass<'tcx> for UnportableVariant { | |
923072b8 | 39 | #[expect(clippy::cast_possible_wrap)] |
f20569fa XL |
40 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { |
41 | if cx.tcx.data_layout.pointer_size.bits() != 64 { | |
42 | return; | |
43 | } | |
44 | if let ItemKind::Enum(def, _) = &item.kind { | |
45 | for var in def.variants { | |
46 | if let Some(anon_const) = &var.disr_expr { | |
47 | let def_id = cx.tcx.hir().body_owner_def_id(anon_const.body); | |
add651ee | 48 | let mut ty = cx.tcx.type_of(def_id.to_def_id()).instantiate_identity(); |
f20569fa XL |
49 | let constant = cx |
50 | .tcx | |
51 | .const_eval_poly(def_id.to_def_id()) | |
52 | .ok() | |
781aab86 | 53 | .map(|val| rustc_middle::mir::Const::from_value(val, ty)); |
ed00b5ec | 54 | if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { |
f20569fa XL |
55 | if let ty::Adt(adt, _) = ty.kind() { |
56 | if adt.is_enum() { | |
5e7ed085 | 57 | ty = adt.repr().discr_type().to_ty(cx.tcx); |
f20569fa XL |
58 | } |
59 | } | |
60 | match ty.kind() { | |
61 | ty::Int(IntTy::Isize) => { | |
62 | let val = ((val as i128) << 64) >> 64; | |
63 | if i32::try_from(val).is_ok() { | |
64 | continue; | |
65 | } | |
66 | }, | |
67 | ty::Uint(UintTy::Usize) if val > u128::from(u32::MAX) => {}, | |
68 | _ => continue, | |
69 | } | |
70 | span_lint( | |
71 | cx, | |
72 | ENUM_CLIKE_UNPORTABLE_VARIANT, | |
73 | var.span, | |
74 | "C-like enum variant discriminant is not portable to 32-bit targets", | |
75 | ); | |
76 | }; | |
77 | } | |
78 | } | |
79 | } | |
80 | } | |
81 | } |