]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/enum_clike.rs
Merge pull request #2202 from topecongiro/format
[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::middle::const_val::ConstVal;
6 use rustc_const_math::*;
7 use rustc::hir::*;
8 use rustc::ty;
9 use rustc::traits::Reveal;
10 use rustc::ty::subst::Substs;
11 use utils::span_lint;
12
13 /// **What it does:** Checks for C-like enumerations that are
14 /// `repr(isize/usize)` and have values that don't fit into an `i32`.
15 ///
16 /// **Why is this bad?** This will truncate the variant value on 32 bit
17 /// architectures, but works fine on 64 bit.
18 ///
19 /// **Known problems:** None.
20 ///
21 /// **Example:**
22 /// ```rust
23 /// #[repr(usize)]
24 /// enum NonPortable {
25 ///     X = 0x1_0000_0000,
26 ///     Y = 0
27 /// }
28 /// ```
29 declare_lint! {
30     pub ENUM_CLIKE_UNPORTABLE_VARIANT,
31     Warn,
32     "C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
33 }
34
35 pub struct UnportableVariant;
36
37 impl LintPass for UnportableVariant {
38     fn get_lints(&self) -> LintArray {
39         lint_array!(ENUM_CLIKE_UNPORTABLE_VARIANT)
40     }
41 }
42
43 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
44     #[allow(cast_possible_truncation, cast_sign_loss)]
45     fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
46         if let ItemEnum(ref def, _) = item.node {
47             for var in &def.variants {
48                 let variant = &var.node;
49                 if let Some(body_id) = variant.disr_expr {
50                     let expr = &cx.tcx.hir.body(body_id).value;
51                     let did = cx.tcx.hir.body_owner_def_id(body_id);
52                     let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
53                     let substs = Substs::identity_for_item(cx.tcx.global_tcx(), did);
54                     let bad = match cx.tcx
55                         .at(expr.span)
56                         .const_eval(param_env.and((did, substs)))
57                     {
58                         Ok(&ty::Const {
59                             val: ConstVal::Integral(Usize(Us64(i))),
60                             ..
61                         }) => u64::from(i as u32) != i,
62                         Ok(&ty::Const {
63                             val: ConstVal::Integral(Isize(Is64(i))),
64                             ..
65                         }) => i64::from(i as i32) != i,
66                         _ => false,
67                     };
68                     if bad {
69                         span_lint(
70                             cx,
71                             ENUM_CLIKE_UNPORTABLE_VARIANT,
72                             var.span,
73                             "Clike enum variant discriminant is not portable to 32-bit targets",
74                         );
75                     }
76                 }
77             }
78         }
79     }
80 }