1 //! lint when there is a large size difference between variants on an enum
5 use utils
::{snippet_opt, span_lint_and_then, type_size}
;
6 use rustc
::ty
::TypeFoldable
;
8 /// **What it does:** Checks for large size differences between variants on
11 /// **Why is this bad?** Enum size is bounded by the largest variant. Having a
13 /// can penalize the memory layout of that enum.
15 /// **Known problems:** None.
25 pub LARGE_ENUM_VARIANT
,
27 "large size difference between variants on an enum"
30 #[derive(Copy, Clone)]
31 pub struct LargeEnumVariant
{
32 maximum_size_difference_allowed
: u64,
35 impl LargeEnumVariant
{
36 pub fn new(maximum_size_difference_allowed
: u64) -> Self {
38 maximum_size_difference_allowed
: maximum_size_difference_allowed
,
43 impl LintPass
for LargeEnumVariant
{
44 fn get_lints(&self) -> LintArray
{
45 lint_array
!(LARGE_ENUM_VARIANT
)
49 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for LargeEnumVariant
{
50 fn check_item(&mut self, cx
: &LateContext
, item
: &Item
) {
51 let did
= cx
.tcx
.hir
.local_def_id(item
.id
);
52 if let ItemEnum(ref def
, _
) = item
.node
{
53 let ty
= cx
.tcx
.type_of(did
);
54 let adt
= ty
.ty_adt_def()
55 .expect("already checked whether this is an enum");
57 let mut smallest_variant
: Option
<(_
, _
)> = None
;
58 let mut largest_variant
: Option
<(_
, _
)> = None
;
60 for (i
, variant
) in adt
.variants
.iter().enumerate() {
61 let size
: u64 = variant
65 let ty
= cx
.tcx
.type_of(f
.did
);
67 0 // we can't reason about generics, so we treat them as zero sized
69 type_size(cx
, ty
).expect("size should be computable for concrete type")
74 let grouped
= (size
, (i
, variant
));
76 update_if(&mut smallest_variant
, grouped
, |a
, b
| b
.0 <= a
.0);
77 update_if(&mut largest_variant
, grouped
, |a
, b
| b
.0 >= a
.0);
80 if let (Some(smallest
), Some(largest
)) = (smallest_variant
, largest_variant
) {
81 let difference
= largest
.0 - smallest
.0;
83 if difference
> self.maximum_size_difference_allowed
{
84 let (i
, variant
) = largest
.1;
90 "large size difference between variants",
92 if variant
.fields
.len() == 1 {
93 let span
= match def
.variants
[i
].node
.data
{
94 VariantData
::Struct(ref fields
, _
) | VariantData
::Tuple(ref fields
, _
) => {
97 VariantData
::Unit(_
) => unreachable
!(),
99 if let Some(snip
) = snippet_opt(cx
, span
) {
102 "consider boxing the large fields to reduce the total size of the \
104 format
!("Box<{}>", snip
),
110 def
.variants
[i
].span
,
111 "consider boxing the large fields to reduce the total size of the enum",
121 fn update_if
<T
, F
>(old
: &mut Option
<T
>, new
: T
, f
: F
)
123 F
: Fn(&T
, &T
) -> bool
,
125 if let Some(ref mut val
) = *old
{