X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_lints%2Fsrc%2Flarge_enum_variant.rs;h=f166748d86b81a021822759440e66456206309b5;hb=dc4ea800b7126e0751ba75eae095cc2a805dc8da;hp=429f7440510b320b23b3ae901cba6746a8796f2d;hpb=aff57e0f43855925eeb747963f6875335eba596b;p=rust.git diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 429f7440510..f166748d86b 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,28 +1,41 @@ //! lint when there is a large size difference between variants on an enum -use crate::utils::{snippet_opt, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_target::abi::LayoutOf; declare_clippy_lint! { /// **What it does:** Checks for large size differences between variants on /// `enum`s. /// /// **Why is this bad?** Enum size is bounded by the largest variant. Having a - /// large variant - /// can penalize the memory layout of that enum. + /// large variant can penalize the memory layout of that enum. /// - /// **Known problems:** None. + /// **Known problems:** This lint obviously cannot take the distribution of + /// variants in your running program into account. It is possible that the + /// smaller variants make up less than 1% of all instances, in which case + /// the overhead is negligible and the boxing is counter-productive. Always + /// measure the change this lint suggests. /// /// **Example:** + /// /// ```rust + /// // Bad /// enum Test { /// A(i32), /// B([i32; 8000]), /// } + /// + /// // Possibly better + /// enum Test2 { + /// A(i32), + /// B(Box<[i32; 8000]>), + /// } /// ``` pub LARGE_ENUM_VARIANT, perf, @@ -45,15 +58,17 @@ pub fn new(maximum_size_difference_allowed: u64) -> Self { impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant { - fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { - let did = cx.tcx.hir().local_def_id(item.hir_id); +impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } if let ItemKind::Enum(ref def, _) = item.kind { - let ty = cx.tcx.type_of(did); + let ty = cx.tcx.type_of(item.def_id); let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - let mut smallest_variant: Option<(_, _)> = None; let mut largest_variant: Option<(_, _)> = None; + let mut second_variant: Option<(_, _)> = None; for (i, variant) in adt.variants.iter().enumerate() { let size: u64 = variant @@ -69,44 +84,51 @@ fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { let grouped = (size, (i, variant)); - update_if(&mut smallest_variant, grouped, |a, b| b.0 <= a.0); - update_if(&mut largest_variant, grouped, |a, b| b.0 >= a.0); + if grouped.0 >= largest_variant.map_or(0, |x| x.0) { + second_variant = largest_variant; + largest_variant = Some(grouped); + } } - if let (Some(smallest), Some(largest)) = (smallest_variant, largest_variant) { - let difference = largest.0 - smallest.0; + if let (Some(largest), Some(second)) = (largest_variant, second_variant) { + let difference = largest.0 - second.0; if difference > self.maximum_size_difference_allowed { let (i, variant) = largest.1; + let help_text = "consider boxing the large fields to reduce the total size of the enum"; span_lint_and_then( cx, LARGE_ENUM_VARIANT, def.variants[i].span, "large size difference between variants", - |db| { + |diag| { + diag.span_label( + def.variants[(largest.1).0].span, + &format!("this variant is {} bytes", largest.0), + ); + diag.span_note( + def.variants[(second.1).0].span, + &format!("and the second-largest variant is {} bytes:", second.0), + ); if variant.fields.len() == 1 { let span = match def.variants[i].data { - VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => { + VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) => { fields[0].ty.span }, VariantData::Unit(..) => unreachable!(), }; if let Some(snip) = snippet_opt(cx, span) { - db.span_suggestion( + diag.span_suggestion( span, - "consider boxing the large fields to reduce the total size of the \ - enum", + help_text, format!("Box<{}>", snip), Applicability::MaybeIncorrect, ); return; } } - db.span_help( - def.variants[i].span, - "consider boxing the large fields to reduce the total size of the enum", - ); + diag.span_help(def.variants[i].span, help_text); }, ); } @@ -114,16 +136,3 @@ fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { } } } - -fn update_if(old: &mut Option, new: T, f: F) -where - F: Fn(&T, &T) -> bool, -{ - if let Some(ref mut val) = *old { - if f(val, &new) { - *val = new; - } - } else { - *old = Some(new); - } -}