]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/enum_clike.rs
Rustup
[rust.git] / clippy_lints / src / enum_clike.rs
1 //! lint on C-like enums that are `repr(isize/usize)` and have values that
2 //! don't fit into an `i32`
3
4 use rustc::lint::*;
5 use rustc::hir::*;
6 use rustc::ty;
7 use rustc::ty::subst::Substs;
8 use syntax::ast::{IntTy, UintTy};
9 use crate::utils::span_lint;
10 use crate::consts::{Constant, miri_to_const};
11 use rustc::ty::util::IntTypeExt;
12 use rustc::mir::interpret::GlobalId;
13
14 /// **What it does:** Checks for C-like enumerations that are
15 /// `repr(isize/usize)` and have values that don't fit into an `i32`.
16 ///
17 /// **Why is this bad?** This will truncate the variant value on 32 bit
18 /// architectures, but works fine on 64 bit.
19 ///
20 /// **Known problems:** None.
21 ///
22 /// **Example:**
23 /// ```rust
24 /// #[repr(usize)]
25 /// enum NonPortable {
26 ///     X = 0x1_0000_0000,
27 ///     Y = 0
28 /// }
29 /// ```
30 declare_clippy_lint! {
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 pub struct UnportableVariant;
37
38 impl LintPass for UnportableVariant {
39     fn get_lints(&self) -> LintArray {
40         lint_array!(ENUM_CLIKE_UNPORTABLE_VARIANT)
41     }
42 }
43
44 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
45     #[allow(cast_possible_truncation, cast_sign_loss)]
46     fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
47         if cx.tcx.data_layout.pointer_size.bits() != 64 {
48             return;
49         }
50         if let ItemEnum(ref def, _) = item.node {
51             for var in &def.variants {
52                 let variant = &var.node;
53                 if let Some(ref anon_const) = variant.disr_expr {
54                     let param_env = ty::ParamEnv::empty();
55                     let did = cx.tcx.hir.body_owner_def_id(anon_const.body);
56                     let substs = Substs::identity_for_item(cx.tcx.global_tcx(), did);
57                     let instance = ty::Instance::new(did, substs);
58                     let cid = GlobalId {
59                         instance,
60                         promoted: None
61                     };
62                     let constant = cx.tcx.const_eval(param_env.and(cid)).ok();
63                     if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx.tcx, c)) {
64                         let mut ty = cx.tcx.type_of(did);
65                         if let ty::TyAdt(adt, _) = ty.sty {
66                             if adt.is_enum() {
67                                 ty = adt.repr.discr_type().to_ty(cx.tcx);
68                             }
69                         }
70                         match ty.sty {
71                             ty::TyInt(IntTy::Isize) => {
72                                 let val = ((val as i128) << 64) >> 64;
73                                 if val <= i128::from(i32::max_value()) && val >= i128::from(i32::min_value()) {
74                                     continue;
75                                 }
76                             }
77                             ty::TyUint(UintTy::Usize) if val > u128::from(u32::max_value()) => {},
78                             _ => continue,
79                         }
80                         span_lint(
81                             cx,
82                             ENUM_CLIKE_UNPORTABLE_VARIANT,
83                             var.span,
84                             "Clike enum variant discriminant is not portable to 32-bit targets",
85                         );
86                     };
87                 }
88             }
89         }
90     }
91 }