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};
"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
// 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) {
}
}
}
+
+ 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));
+ }
+ }
+ }
+ }
+ }
+ }