]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/empty_enum.rs
Add empty_enum lint (just a copy of large_enum_variant for now)
[rust.git] / clippy_lints / src / empty_enum.rs
1 //! lint when there is an enum with no variants
2
3 use rustc::lint::*;
4 use rustc::hir::*;
5 use utils::{span_lint_and_then, snippet_opt};
6 use rustc::ty::layout::TargetDataLayout;
7 use rustc::ty::TypeFoldable;
8 use rustc::traits::Reveal;
9
10 /// **What it does:** Checks for `enum`s with no variants.
11 ///
12 /// **Why is this bad?** Enum's with no variants should be replaced with `!`.
13 ///
14 /// **Known problems:** None.
15 ///
16 /// **Example:**
17 /// ```rust
18 /// enum Test {}
19 /// ```
20 declare_lint! {
21     pub EMPTY_ENUM,
22     Warn,
23     "enum with no variants"
24 }
25
26 #[derive(Copy,Clone)]
27 pub struct EmptyEnum;
28
29 impl LintPass for EmptyEnum {
30     fn get_lints(&self) -> LintArray {
31         lint_array!(EMPTY_ENUM)
32     }
33 }
34
35 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
36     fn check_item(&mut self, cx: &LateContext, item: &Item) {
37         let did = cx.tcx.hir.local_def_id(item.id);
38         if let ItemEnum(ref def, _) = item.node {
39             let ty = cx.tcx.item_type(did);
40             let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
41             for (i, variant) in adt.variants.iter().enumerate() {
42                 let data_layout = TargetDataLayout::parse(cx.sess());
43                 cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
44                     let size: u64 = variant.fields
45                         .iter()
46                         .map(|f| {
47                             let ty = cx.tcx.item_type(f.did);
48                             if ty.needs_subst() {
49                                 0 // we can't reason about generics, so we treat them as zero sized
50                             } else {
51                                 ty.layout(&infcx)
52                                     .expect("layout should be computable for concrete type")
53                                     .size(&data_layout)
54                                     .bytes()
55                             }
56                         })
57                         .sum();
58                     if size > 0 {
59                         span_lint_and_then(cx, EMPTY_ENUM, def.variants[i].span, "large enum variant found", |db| {
60                             if variant.fields.len() == 1 {
61                                 let span = match def.variants[i].node.data {
62                                     VariantData::Struct(ref fields, _) |
63                                     VariantData::Tuple(ref fields, _) => fields[0].ty.span,
64                                     VariantData::Unit(_) => unreachable!(),
65                                 };
66                                 if let Some(snip) = snippet_opt(cx, span) {
67                                     db.span_suggestion(span,
68                                                        "consider boxing the large fields to reduce the total size of \
69                                                         the enum",
70                                                        format!("Box<{}>", snip));
71                                     return;
72                                 }
73                             }
74                             db.span_help(def.variants[i].span,
75                                          "consider boxing the large fields to reduce the total size of the enum");
76                         });
77                     }
78                 });
79             }
80         }
81     }
82 }