Skip to content

Commit f89e72a

Browse files
committed
Use approx_ty_size for large_enum_variant
1 parent 09e4659 commit f89e72a

File tree

5 files changed

+260
-137
lines changed

5 files changed

+260
-137
lines changed

clippy_lints/src/large_enum_variant.rs

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//! lint when there is a large size difference between variants on an enum
22
33
use clippy_utils::source::snippet_with_applicability;
4-
use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy};
4+
use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy};
55
use rustc_errors::Applicability;
66
use rustc_hir::{Item, ItemKind};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_middle::lint::in_external_macro;
9-
use rustc_middle::ty::layout::LayoutOf;
10-
use rustc_middle::ty::{Adt, Ty};
9+
use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty};
1110
use rustc_session::{declare_tool_lint, impl_lint_pass};
1211
use rustc_span::source_map::Span;
1312

@@ -17,7 +16,7 @@ declare_clippy_lint! {
1716
/// `enum`s.
1817
///
1918
/// ### Why is this bad?
20-
/// Enum size is bounded by the largest variant. Having a
19+
/// Enum size is bounded by the largest variant. Having one
2120
/// large variant can penalize the memory layout of that enum.
2221
///
2322
/// ### Known problems
@@ -33,9 +32,6 @@ declare_clippy_lint! {
3332
/// use case it may be possible to store the large data in an auxiliary
3433
/// structure (e.g. Arena or ECS).
3534
///
36-
/// The lint will ignore generic types if the layout depends on the
37-
/// generics, even if the size difference will be large anyway.
38-
///
3935
/// ### Example
4036
/// ```rust
4137
/// enum Test {
@@ -83,6 +79,38 @@ struct VariantInfo {
8379
fields_size: Vec<FieldInfo>,
8480
}
8581

82+
fn variants_size<'tcx>(
83+
cx: &LateContext<'tcx>,
84+
adt: AdtDef<'tcx>,
85+
subst: &'tcx List<GenericArg<'tcx>>,
86+
) -> Vec<VariantInfo> {
87+
let mut variants_size = adt
88+
.variants()
89+
.iter()
90+
.enumerate()
91+
.map(|(i, variant)| {
92+
let mut fields_size = variant
93+
.fields
94+
.iter()
95+
.enumerate()
96+
.map(|(i, f)| FieldInfo {
97+
ind: i,
98+
size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
99+
})
100+
.collect::<Vec<_>>();
101+
fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
102+
103+
VariantInfo {
104+
ind: i,
105+
size: fields_size.iter().map(|info| info.size).sum(),
106+
fields_size,
107+
}
108+
})
109+
.collect::<Vec<_>>();
110+
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
111+
variants_size
112+
}
113+
86114
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
87115

88116
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
@@ -92,57 +120,45 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
92120
}
93121
if let ItemKind::Enum(ref def, _) = item.kind {
94122
let ty = cx.tcx.type_of(item.def_id);
95-
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
123+
let (adt, subst) = match ty.kind() {
124+
Adt(adt, subst) => (adt, subst),
125+
_ => panic!("already checked whether this is an enum"),
126+
};
96127
if adt.variants().len() <= 1 {
97128
return;
98129
}
99-
let mut variants_size: Vec<VariantInfo> = Vec::new();
100-
for (i, variant) in adt.variants().iter().enumerate() {
101-
let mut fields_size = Vec::new();
102-
for (i, f) in variant.fields.iter().enumerate() {
103-
let ty = cx.tcx.type_of(f.did);
104-
// don't lint variants which have a field of generic type.
105-
match cx.layout_of(ty) {
106-
Ok(l) => {
107-
let fsize = l.size.bytes();
108-
fields_size.push(FieldInfo { ind: i, size: fsize });
109-
},
110-
Err(_) => {
111-
return;
112-
},
113-
}
114-
}
115-
let size: u64 = fields_size.iter().map(|info| info.size).sum();
116-
117-
variants_size.push(VariantInfo {
118-
ind: i,
119-
size,
120-
fields_size,
121-
});
122-
}
123-
124-
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
130+
let variants_size = variants_size(cx, *adt, subst);
125131

126132
let mut difference = variants_size[0].size - variants_size[1].size;
127133
if difference > self.maximum_size_difference_allowed {
128134
let help_text = "consider boxing the large fields to reduce the total size of the enum";
129135
span_lint_and_then(
130136
cx,
131137
LARGE_ENUM_VARIANT,
132-
def.variants[variants_size[0].ind].span,
138+
item.span,
133139
"large size difference between variants",
134140
|diag| {
141+
diag.span_label(
142+
item.span,
143+
format!("the entire enum is at least {} bytes", approx_ty_size(cx, ty)),
144+
);
135145
diag.span_label(
136146
def.variants[variants_size[0].ind].span,
137-
&format!("this variant is {} bytes", variants_size[0].size),
147+
format!("the largest variant contains at least {} bytes", variants_size[0].size),
138148
);
139-
diag.span_note(
149+
diag.span_label(
140150
def.variants[variants_size[1].ind].span,
141-
&format!("and the second-largest variant is {} bytes:", variants_size[1].size),
151+
&if variants_size[1].fields_size.is_empty() {
152+
"the second-largest variant carries no data at all".to_owned()
153+
} else {
154+
format!(
155+
"the second-largest variant contains at least {} bytes",
156+
variants_size[1].size
157+
)
158+
},
142159
);
143160

144161
let fields = def.variants[variants_size[0].ind].data.fields();
145-
variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
146162
let mut applicability = Applicability::MaybeIncorrect;
147163
if is_copy(cx, ty) || maybe_copy(cx, ty) {
148164
diag.span_note(

tests/ui/large_enum_variant.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,30 @@ impl<T: Copy> Clone for SomeGenericPossiblyCopyEnum<T> {
130130

131131
impl<T: Copy> Copy for SomeGenericPossiblyCopyEnum<T> {}
132132

133+
enum LargeEnumWithGenerics<T> {
134+
Small,
135+
Large((T, [u8; 512])),
136+
}
137+
138+
struct Foo<T> {
139+
foo: T,
140+
}
141+
142+
enum WithGenerics {
143+
Large([Foo<u64>; 64]),
144+
Small(u8),
145+
}
146+
147+
enum PossiblyLargeEnumWithConst<const U: usize> {
148+
SmallBuffer([u8; 4]),
149+
MightyBuffer([u16; U]),
150+
}
151+
152+
enum LargeEnumOfConst {
153+
Ok,
154+
Error(PossiblyLargeEnumWithConst<256>),
155+
}
156+
133157
fn main() {
134158
large_enum_variant!();
135159
}

0 commit comments

Comments
 (0)