]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/enum_clike.rs
Merge branch 'master' into move_links
[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.at(expr.span).const_eval(
55                         param_env.and((did, substs)),
56                     ) {
57                         Ok(ConstVal::Integral(Usize(Us64(i)))) => i as u32 as u64 != i,
58                         Ok(ConstVal::Integral(Isize(Is64(i)))) => i as i32 as i64 != i,
59                         _ => false,
60                     };
61                     if bad {
62                         span_lint(
63                             cx,
64                             ENUM_CLIKE_UNPORTABLE_VARIANT,
65                             var.span,
66                             "Clike enum variant discriminant is not portable to 32-bit targets",
67                         );
68                     }
69                 }
70             }
71         }
72     }
73 }