]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #34755 - jonas-schievink:minor-differences, r=eddyb
authorbors <bors@rust-lang.org>
Tue, 12 Jul 2016 08:06:34 +0000 (01:06 -0700)
committerGitHub <noreply@github.com>
Tue, 12 Jul 2016 08:06:34 +0000 (01:06 -0700)
Move variant_size_differences out of trans

Also enhances the error message a bit, fixes #30505 on the way, and adds
a test (which was missing).

Closes #34018

1  2 
src/librustc_lint/types.rs

index 5e3ae01591d69c62c544a0e308943f1fafe0b640,2dbe54fe7688b1904d7439781012e7405dd6e716..9b08ddcafab8e7d9e773a2d81e85dd15f2549a21
  use rustc::hir::def_id::DefId;
  use rustc::ty::subst::Substs;
  use rustc::ty::{self, Ty, TyCtxt};
+ use rustc::ty::layout::{Layout, Primitive};
+ use rustc::traits::ProjectionMode;
  use middle::const_val::ConstVal;
  use rustc_const_eval::eval_const_expr_partial;
  use rustc_const_eval::EvalHint::ExprTypeChecked;
 -use util::common::slice_pat;
  use util::nodemap::{FnvHashSet};
  use lint::{LateContext, LintContext, LintArray};
  use lint::{LintPass, LateLintPass};
@@@ -76,6 -79,12 +78,12 @@@ declare_lint! 
      "shift exceeds the type's number of bits"
  }
  
+ declare_lint! {
+     VARIANT_SIZE_DIFFERENCES,
+     Allow,
+     "detects enums with widely varying variant sizes"
+ }
  #[derive(Copy, Clone)]
  pub struct TypeLimits {
      /// Id of the last visited negated expression
@@@ -460,7 -469,7 +468,7 @@@ impl<'a, 'tcx> ImproperCTypesVisitor<'a
                  // Check for a repr() attribute to specify the size of the
                  // discriminant.
                  let repr_hints = cx.lookup_repr_hints(def.did);
 -                match slice_pat(&&**repr_hints) {
 +                match &repr_hints[..] {
                      &[] => {
                          // Special-case types like `Option<extern fn()>`.
                          if !is_repr_nullable_ptr(cx, def, substs) {
@@@ -675,3 -684,64 +683,64 @@@ impl LateLintPass for ImproperCTypes 
          }
      }
  }
+ pub struct VariantSizeDifferences;
+ impl LintPass for VariantSizeDifferences {
+     fn get_lints(&self) -> LintArray {
+         lint_array!(VARIANT_SIZE_DIFFERENCES)
+     }
+ }
+ impl LateLintPass for VariantSizeDifferences {
+     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
+         if let hir::ItemEnum(ref enum_definition, ref gens) = it.node {
+             if gens.ty_params.is_empty() {  // sizes only make sense for non-generic types
+                 let t = cx.tcx.node_id_to_type(it.id);
+                 let layout = cx.tcx.normalizing_infer_ctxt(ProjectionMode::Any).enter(|infcx| {
+                     t.layout(&infcx).unwrap_or_else(|e| {
+                         bug!("failed to get layout for `{}`: {}", t, e)
+                     })
+                 });
+                 if let Layout::General { ref variants, ref size, discr, .. } = *layout {
+                     let discr_size = Primitive::Int(discr).size(&cx.tcx.data_layout).bytes();
+                     debug!("enum `{}` is {} bytes large", t, size.bytes());
+                     let (largest, slargest, largest_index) = enum_definition.variants
+                         .iter()
+                         .zip(variants)
+                         .map(|(variant, variant_layout)| {
+                             // Subtract the size of the enum discriminant
+                             let bytes = variant_layout.min_size().bytes()
+                                                                  .saturating_sub(discr_size);
+                             debug!("- variant `{}` is {} bytes large", variant.node.name, bytes);
+                             bytes
+                         })
+                         .enumerate()
+                         .fold((0, 0, 0),
+                             |(l, s, li), (idx, size)|
+                                 if size > l {
+                                     (size, l, idx)
+                                 } else if size > s {
+                                     (l, size, li)
+                                 } else {
+                                     (l, s, li)
+                                 }
+                         );
+                     // we only warn if the largest variant is at least thrice as large as
+                     // the second-largest.
+                     if largest > slargest * 3 && slargest > 0 {
+                         cx.span_lint(VARIANT_SIZE_DIFFERENCES,
+                                      enum_definition.variants[largest_index].span,
+                                      &format!("enum variant is more than three times larger \
+                                                ({} bytes) than the next largest", largest));
+                     }
+                 }
+             }
+         }
+     }
+ }