1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![allow(non_snake_case)]
13 use rustc
::hir
::map
as hir_map
;
14 use rustc
::ty
::subst
::Substs
;
15 use rustc
::ty
::{self, AdtKind, ParamEnv, Ty, TyCtxt}
;
16 use rustc
::ty
::layout
::{self, IntegerExt, LayoutOf}
;
17 use util
::nodemap
::FxHashSet
;
18 use lint
::{LateContext, LintContext, LintArray}
;
19 use lint
::{LintPass, LateLintPass}
;
22 use std
::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}
;
24 use syntax
::{ast, attr}
;
25 use rustc_target
::spec
::abi
::Abi
;
34 "comparisons made useless by limits of the types involved"
40 "literal out of range for its type"
44 VARIANT_SIZE_DIFFERENCES
,
46 "detects enums with widely varying variant sizes"
49 #[derive(Copy, Clone)]
50 pub struct TypeLimits
{
51 /// Id of the last visited negated expression
52 negated_expr_id
: ast
::NodeId
,
56 pub fn new() -> TypeLimits
{
57 TypeLimits { negated_expr_id: ast::DUMMY_NODE_ID }
61 impl LintPass
for TypeLimits
{
62 fn get_lints(&self) -> LintArray
{
63 lint_array
!(UNUSED_COMPARISONS
,
68 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for TypeLimits
{
69 fn check_expr(&mut self, cx
: &LateContext
<'a
, 'tcx
>, e
: &'tcx hir
::Expr
) {
71 hir
::ExprUnary(hir
::UnNeg
, ref expr
) => {
72 // propagate negation, if the negation itself isn't negated
73 if self.negated_expr_id
!= e
.id
{
74 self.negated_expr_id
= expr
.id
;
77 hir
::ExprBinary(binop
, ref l
, ref r
) => {
78 if is_comparison(binop
) && !check_limits(cx
, binop
, &l
, &r
) {
79 cx
.span_lint(UNUSED_COMPARISONS
,
81 "comparison is useless due to type limits");
84 hir
::ExprLit(ref lit
) => {
85 match cx
.tables
.node_id_to_type(e
.hir_id
).sty
{
88 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
)) |
89 ast
::LitKind
::Int(v
, ast
::LitIntType
::Unsuffixed
) => {
90 let int_type
= if let ast
::IntTy
::Isize
= t
{
91 cx
.sess().target
.isize_ty
95 let (_
, max
) = int_ty_range(int_type
);
96 let max
= max
as u128
;
97 let negative
= self.negated_expr_id
== e
.id
;
99 // Detect literal value out of range [min, max] inclusive
100 // avoiding use of -min to prevent overflow/panic
101 if (negative
&& v
> max
+ 1) || (!negative
&& v
> max
) {
102 if let Some(repr_str
) = get_bin_hex_repr(cx
, lit
) {
103 report_bin_hex_error(
114 OVERFLOWING_LITERALS
,
116 &format
!("literal out of range for {:?}", t
),
125 let uint_type
= if let ast
::UintTy
::Usize
= t
{
126 cx
.sess().target
.usize_ty
130 let (min
, max
) = uint_ty_range(uint_type
);
131 let lit_val
: u128
= match lit
.node
{
132 // _v is u8, within range by definition
133 ast
::LitKind
::Byte(_v
) => return,
134 ast
::LitKind
::Int(v
, _
) => v
,
137 if lit_val
< min
|| lit_val
> max
{
138 let parent_id
= cx
.tcx
.hir
.get_parent_node(e
.id
);
139 if let hir_map
::NodeExpr(parent_expr
) = cx
.tcx
.hir
.get(parent_id
) {
140 if let hir
::ExprCast(..) = parent_expr
.node
{
141 if let ty
::TyChar
= cx
.tables
.expr_ty(parent_expr
).sty
{
142 let mut err
= cx
.struct_span_lint(
143 OVERFLOWING_LITERALS
,
145 "only u8 can be cast into char");
146 err
.span_suggestion(parent_expr
.span
,
147 &"use a char literal instead",
148 format
!("'\\u{{{:X}}}'", lit_val
));
154 if let Some(repr_str
) = get_bin_hex_repr(cx
, lit
) {
155 report_bin_hex_error(
166 OVERFLOWING_LITERALS
,
168 &format
!("literal out of range for {:?}", t
),
173 let is_infinite
= match lit
.node
{
174 ast
::LitKind
::Float(v
, _
) |
175 ast
::LitKind
::FloatUnsuffixed(v
) => {
177 ast
::FloatTy
::F32
=> v
.as_str().parse().map(f32::is_infinite
),
178 ast
::FloatTy
::F64
=> v
.as_str().parse().map(f64::is_infinite
),
183 if is_infinite
== Ok(true) {
184 cx
.span_lint(OVERFLOWING_LITERALS
,
186 &format
!("literal out of range for {:?}", t
));
195 fn is_valid
<T
: cmp
::PartialOrd
>(binop
: hir
::BinOp
, v
: T
, min
: T
, max
: T
) -> bool
{
197 hir
::BiLt
=> v
> min
&& v
<= max
,
198 hir
::BiLe
=> v
>= min
&& v
< max
,
199 hir
::BiGt
=> v
>= min
&& v
< max
,
200 hir
::BiGe
=> v
> min
&& v
<= max
,
201 hir
::BiEq
| hir
::BiNe
=> v
>= min
&& v
<= max
,
206 fn rev_binop(binop
: hir
::BinOp
) -> hir
::BinOp
{
207 codemap
::respan(binop
.span
,
209 hir
::BiLt
=> hir
::BiGt
,
210 hir
::BiLe
=> hir
::BiGe
,
211 hir
::BiGt
=> hir
::BiLt
,
212 hir
::BiGe
=> hir
::BiLe
,
217 // for isize & usize, be conservative with the warnings, so that the
218 // warnings are consistent between 32- and 64-bit platforms
219 fn int_ty_range(int_ty
: ast
::IntTy
) -> (i128
, i128
) {
221 ast
::IntTy
::Isize
=> (i64::min_value() as i128
, i64::max_value() as i128
),
222 ast
::IntTy
::I8
=> (i8::min_value() as i64 as i128
, i8::max_value() as i128
),
223 ast
::IntTy
::I16
=> (i16::min_value() as i64 as i128
, i16::max_value() as i128
),
224 ast
::IntTy
::I32
=> (i32::min_value() as i64 as i128
, i32::max_value() as i128
),
225 ast
::IntTy
::I64
=> (i64::min_value() as i128
, i64::max_value() as i128
),
226 ast
::IntTy
::I128
=>(i128
::min_value() as i128
, i128
::max_value()),
230 fn uint_ty_range(uint_ty
: ast
::UintTy
) -> (u128
, u128
) {
232 ast
::UintTy
::Usize
=> (u64::min_value() as u128
, u64::max_value() as u128
),
233 ast
::UintTy
::U8
=> (u8::min_value() as u128
, u8::max_value() as u128
),
234 ast
::UintTy
::U16
=> (u16::min_value() as u128
, u16::max_value() as u128
),
235 ast
::UintTy
::U32
=> (u32::min_value() as u128
, u32::max_value() as u128
),
236 ast
::UintTy
::U64
=> (u64::min_value() as u128
, u64::max_value() as u128
),
237 ast
::UintTy
::U128
=> (u128
::min_value(), u128
::max_value()),
241 fn check_limits(cx
: &LateContext
,
246 let (lit
, expr
, swap
) = match (&l
.node
, &r
.node
) {
247 (&hir
::ExprLit(_
), _
) => (l
, r
, true),
248 (_
, &hir
::ExprLit(_
)) => (r
, l
, false),
251 // Normalize the binop so that the literal is always on the RHS in
253 let norm_binop
= if swap { rev_binop(binop) }
else { binop }
;
254 match cx
.tables
.node_id_to_type(expr
.hir_id
).sty
{
255 ty
::TyInt(int_ty
) => {
256 let (min
, max
) = int_ty_range(int_ty
);
257 let lit_val
: i128
= match lit
.node
{
258 hir
::ExprLit(ref li
) => {
260 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
)) |
261 ast
::LitKind
::Int(v
, ast
::LitIntType
::Unsuffixed
) => v
as i128
,
267 is_valid(norm_binop
, lit_val
, min
, max
)
269 ty
::TyUint(uint_ty
) => {
270 let (min
, max
) :(u128
, u128
) = uint_ty_range(uint_ty
);
271 let lit_val
: u128
= match lit
.node
{
272 hir
::ExprLit(ref li
) => {
274 ast
::LitKind
::Int(v
, _
) => v
,
280 is_valid(norm_binop
, lit_val
, min
, max
)
286 fn is_comparison(binop
: hir
::BinOp
) -> bool
{
288 hir
::BiEq
| hir
::BiLt
| hir
::BiLe
| hir
::BiNe
| hir
::BiGe
| hir
::BiGt
=> true,
293 fn get_bin_hex_repr(cx
: &LateContext
, lit
: &ast
::Lit
) -> Option
<String
> {
294 let src
= cx
.sess().codemap().span_to_snippet(lit
.span
).ok()?
;
295 let firstch
= src
.chars().next()?
;
298 match src
.chars().nth(1) {
299 Some('x'
) | Some('b'
) => return Some(src
),
307 // This function finds the next fitting type and generates a suggestion string.
308 // It searches for fitting types in the following way (`X < Y`):
309 // - `iX`: if literal fits in `uX` => `uX`, else => `iY`
313 // No suggestion for: `isize`, `usize`.
314 fn get_type_suggestion
<'a
>(
315 t
: &ty
::TypeVariants
,
318 ) -> Option
<String
> {
319 use syntax
::ast
::IntTy
::*;
320 use syntax
::ast
::UintTy
::*;
321 macro_rules
! find_fit
{
322 ($ty
:expr
, $val
:expr
, $negative
:expr
,
323 $
($
type:ident
=> [$
($utypes
:expr
),*] => [$
($itypes
:expr
),*]),+) => {
325 let _neg
= if negative { 1 }
else { 0 }
;
328 $
(if !negative
&& val
<= uint_ty_range($utypes
).1 {
329 return Some(format
!("{:?}", $utypes
))
331 $
(if val
<= int_ty_range($itypes
).1 as u128
+ _neg
{
332 return Some(format
!("{:?}", $itypes
))
342 &ty
::TyInt(i
) => find_fit
!(i
, val
, negative
,
343 I8
=> [U8
] => [I16
, I32
, I64
, I128
],
344 I16
=> [U16
] => [I32
, I64
, I128
],
345 I32
=> [U32
] => [I64
, I128
],
346 I64
=> [U64
] => [I128
],
347 I128
=> [U128
] => []),
348 &ty
::TyUint(u
) => find_fit
!(u
, val
, negative
,
349 U8
=> [U8
, U16
, U32
, U64
, U128
] => [],
350 U16
=> [U16
, U32
, U64
, U128
] => [],
351 U32
=> [U32
, U64
, U128
] => [],
352 U64
=> [U64
, U128
] => [],
353 U128
=> [U128
] => []),
358 fn report_bin_hex_error(
361 ty
: ty
::TypeVariants
,
366 let (t
, actually
) = match ty
{
368 let ity
= attr
::IntType
::SignedInt(t
);
369 let bits
= layout
::Integer
::from_attr(cx
.tcx
, ity
).size().bits();
370 let actually
= (val
<< (128 - bits
)) as i128
>> (128 - bits
);
371 (format
!("{:?}", t
), actually
.to_string())
374 let ity
= attr
::IntType
::UnsignedInt(t
);
375 let bits
= layout
::Integer
::from_attr(cx
.tcx
, ity
).size().bits();
376 let actually
= (val
<< (128 - bits
)) >> (128 - bits
);
377 (format
!("{:?}", t
), actually
.to_string())
381 let mut err
= cx
.struct_span_lint(
382 OVERFLOWING_LITERALS
,
384 &format
!("literal out of range for {}", t
),
387 "the literal `{}` (decimal `{}`) does not fit into \
388 an `{}` and will become `{}{}`",
389 repr_str
, val
, t
, actually
, t
391 if let Some(sugg_ty
) =
392 get_type_suggestion(&cx
.tables
.node_id_to_type(expr
.hir_id
).sty
, val
, negative
)
394 if let Some(pos
) = repr_str
.chars().position(|c
| c
== 'i'
|| c
== 'u'
) {
395 let (sans_suffix
, _
) = repr_str
.split_at(pos
);
398 &format
!("consider using `{}` instead", sugg_ty
),
399 format
!("{}{}", sans_suffix
, sugg_ty
),
402 err
.help(&format
!("consider using `{}` instead", sugg_ty
));
414 "proper use of libc types in foreign modules"
417 struct ImproperCTypesVisitor
<'a
, 'tcx
: 'a
> {
418 cx
: &'a LateContext
<'a
, 'tcx
>,
421 enum FfiResult
<'tcx
> {
423 FfiPhantom(Ty
<'tcx
>),
426 reason
: &'
static str,
427 help
: Option
<&'
static str>,
431 /// Check if this enum can be safely exported based on the
432 /// "nullable pointer optimization". Currently restricted
433 /// to function pointers and references, but could be
434 /// expanded to cover NonZero raw pointers and newtypes.
435 /// FIXME: This duplicates code in codegen.
436 fn is_repr_nullable_ptr
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
437 def
: &'tcx ty
::AdtDef
,
438 substs
: &Substs
<'tcx
>)
440 if def
.variants
.len() == 2 {
443 if def
.variants
[0].fields
.is_empty() {
445 } else if def
.variants
[1].fields
.is_empty() {
451 if def
.variants
[data_idx
].fields
.len() == 1 {
452 match def
.variants
[data_idx
].fields
[0].ty(tcx
, substs
).sty
{
466 impl<'a
, 'tcx
> ImproperCTypesVisitor
<'a
, 'tcx
> {
467 /// Check if the given type is "ffi-safe" (has a stable, well-defined
468 /// representation which can be exported to C code).
469 fn check_type_for_ffi(&self,
470 cache
: &mut FxHashSet
<Ty
<'tcx
>>,
471 ty
: Ty
<'tcx
>) -> FfiResult
<'tcx
> {
472 use self::FfiResult
::*;
474 let cx
= self.cx
.tcx
;
476 // Protect against infinite recursion, for example
477 // `struct S(*mut S);`.
478 // FIXME: A recursion limit is necessary as well, for irregular
480 if !cache
.insert(ty
) {
485 ty
::TyAdt(def
, substs
) => {
486 if def
.is_phantom_data() {
487 return FfiPhantom(ty
);
489 match def
.adt_kind() {
491 if !def
.repr
.c() && !def
.repr
.transparent() {
494 reason
: "this struct has unspecified layout",
495 help
: Some("consider adding a #[repr(C)] or #[repr(transparent)] \
496 attribute to this struct"),
500 if def
.non_enum_variant().fields
.is_empty() {
503 reason
: "this struct has no fields",
504 help
: Some("consider adding a member to this struct"),
508 // We can't completely trust repr(C) and repr(transparent) markings;
509 // make sure the fields are actually safe.
510 let mut all_phantom
= true;
511 for field
in &def
.non_enum_variant().fields
{
512 let field_ty
= cx
.normalize_erasing_regions(
513 ParamEnv
::reveal_all(),
514 field
.ty(cx
, substs
),
516 // repr(transparent) types are allowed to have arbitrary ZSTs, not just
517 // PhantomData -- skip checking all ZST fields
518 if def
.repr
.transparent() {
520 .layout_of(cx
.param_env(field
.did
).and(field_ty
))
521 .map(|layout
| layout
.is_zst())
527 let r
= self.check_type_for_ffi(cache
, field_ty
);
533 FfiUnsafe { .. }
=> {
539 if all_phantom { FfiPhantom(ty) }
else { FfiSafe }
545 reason
: "this union has unspecified layout",
546 help
: Some("consider adding a #[repr(C)] attribute to this union"),
550 if def
.non_enum_variant().fields
.is_empty() {
553 reason
: "this union has no fields",
554 help
: Some("consider adding a field to this union"),
558 let mut all_phantom
= true;
559 for field
in &def
.non_enum_variant().fields
{
560 let field_ty
= cx
.normalize_erasing_regions(
561 ParamEnv
::reveal_all(),
562 field
.ty(cx
, substs
),
564 let r
= self.check_type_for_ffi(cache
, field_ty
);
570 FfiUnsafe { .. }
=> {
576 if all_phantom { FfiPhantom(ty) }
else { FfiSafe }
579 if def
.variants
.is_empty() {
580 // Empty enums are okay... although sort of useless.
584 // Check for a repr() attribute to specify the size of the
586 if !def
.repr
.c() && def
.repr
.int
.is_none() {
587 // Special-case types like `Option<extern fn()>`.
588 if !is_repr_nullable_ptr(cx
, def
, substs
) {
591 reason
: "enum has no representation hint",
592 help
: Some("consider adding a #[repr(...)] attribute \
598 // Check the contained variants.
599 for variant
in &def
.variants
{
600 for field
in &variant
.fields
{
601 let arg
= cx
.normalize_erasing_regions(
602 ParamEnv
::reveal_all(),
603 field
.ty(cx
, substs
),
605 let r
= self.check_type_for_ffi(cache
, arg
);
608 FfiUnsafe { .. }
=> {
614 reason
: "this enum contains a PhantomData field",
626 ty
::TyChar
=> FfiUnsafe
{
628 reason
: "the `char` type has no C equivalent",
629 help
: Some("consider using `u32` or `libc::wchar_t` instead"),
632 ty
::TyInt(ast
::IntTy
::I128
) | ty
::TyUint(ast
::UintTy
::U128
) => FfiUnsafe
{
634 reason
: "128-bit integers don't currently have a known stable ABI",
638 // Primitive types with a stable representation.
639 ty
::TyBool
| ty
::TyInt(..) | ty
::TyUint(..) | ty
::TyFloat(..) | ty
::TyNever
=> FfiSafe
,
641 ty
::TySlice(_
) => FfiUnsafe
{
643 reason
: "slices have no C equivalent",
644 help
: Some("consider using a raw pointer instead"),
647 ty
::TyDynamic(..) => FfiUnsafe
{
649 reason
: "trait objects have no C equivalent",
653 ty
::TyStr
=> FfiUnsafe
{
655 reason
: "string slices have no C equivalent",
656 help
: Some("consider using `*const u8` and a length instead"),
659 ty
::TyTuple(..) => FfiUnsafe
{
661 reason
: "tuples have unspecified layout",
662 help
: Some("consider using a struct instead"),
665 ty
::TyRawPtr(ty
::TypeAndMut { ty, .. }
) |
666 ty
::TyRef(_
, ty
, _
) => self.check_type_for_ffi(cache
, ty
),
668 ty
::TyArray(ty
, _
) => self.check_type_for_ffi(cache
, ty
),
670 ty
::TyFnPtr(sig
) => {
672 Abi
::Rust
| Abi
::RustIntrinsic
| Abi
::PlatformIntrinsic
| Abi
::RustCall
=> {
675 reason
: "this function pointer has Rust-specific calling convention",
676 help
: Some("consider using an `fn \"extern\"(...) -> ...` \
677 function pointer instead"),
683 let sig
= cx
.erase_late_bound_regions(&sig
);
684 if !sig
.output().is_nil() {
685 let r
= self.check_type_for_ffi(cache
, sig
.output());
693 for arg
in sig
.inputs() {
694 let r
= self.check_type_for_ffi(cache
, arg
);
705 ty
::TyForeign(..) => FfiSafe
,
711 ty
::TyGenerator(..) |
712 ty
::TyGeneratorWitness(..) |
713 ty
::TyProjection(..) |
715 ty
::TyFnDef(..) => bug
!("Unexpected type in foreign function"),
719 fn check_type_for_ffi_and_report_errors(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) {
720 // it is only OK to use this function because extern fns cannot have
721 // any generic types right now:
722 let ty
= self.cx
.tcx
.normalize_erasing_regions(ParamEnv
::reveal_all(), ty
);
724 match self.check_type_for_ffi(&mut FxHashSet(), ty
) {
725 FfiResult
::FfiSafe
=> {}
726 FfiResult
::FfiPhantom(ty
) => {
727 self.cx
.span_lint(IMPROPER_CTYPES
,
729 &format
!("`extern` block uses type `{}` which is not FFI-safe: \
730 composed only of PhantomData", ty
));
732 FfiResult
::FfiUnsafe { ty: unsafe_ty, reason, help }
=> {
733 let msg
= format
!("`extern` block uses type `{}` which is not FFI-safe: {}",
735 let mut diag
= self.cx
.struct_span_lint(IMPROPER_CTYPES
, sp
, &msg
);
736 if let Some(s
) = help
{
739 if let ty
::TyAdt(def
, _
) = unsafe_ty
.sty
{
740 if let Some(sp
) = self.cx
.tcx
.hir
.span_if_local(def
.did
) {
741 diag
.span_note(sp
, "type defined here");
749 fn check_foreign_fn(&mut self, id
: ast
::NodeId
, decl
: &hir
::FnDecl
) {
750 let def_id
= self.cx
.tcx
.hir
.local_def_id(id
);
751 let sig
= self.cx
.tcx
.fn_sig(def_id
);
752 let sig
= self.cx
.tcx
.erase_late_bound_regions(&sig
);
754 for (input_ty
, input_hir
) in sig
.inputs().iter().zip(&decl
.inputs
) {
755 self.check_type_for_ffi_and_report_errors(input_hir
.span
, input_ty
);
758 if let hir
::Return(ref ret_hir
) = decl
.output
{
759 let ret_ty
= sig
.output();
760 if !ret_ty
.is_nil() {
761 self.check_type_for_ffi_and_report_errors(ret_hir
.span
, ret_ty
);
766 fn check_foreign_static(&mut self, id
: ast
::NodeId
, span
: Span
) {
767 let def_id
= self.cx
.tcx
.hir
.local_def_id(id
);
768 let ty
= self.cx
.tcx
.type_of(def_id
);
769 self.check_type_for_ffi_and_report_errors(span
, ty
);
773 #[derive(Copy, Clone)]
774 pub struct ImproperCTypes
;
776 impl LintPass
for ImproperCTypes
{
777 fn get_lints(&self) -> LintArray
{
778 lint_array
!(IMPROPER_CTYPES
)
782 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for ImproperCTypes
{
783 fn check_item(&mut self, cx
: &LateContext
, it
: &hir
::Item
) {
784 let mut vis
= ImproperCTypesVisitor { cx: cx }
;
785 if let hir
::ItemForeignMod(ref nmod
) = it
.node
{
786 if nmod
.abi
!= Abi
::RustIntrinsic
&& nmod
.abi
!= Abi
::PlatformIntrinsic
{
787 for ni
in &nmod
.items
{
789 hir
::ForeignItemFn(ref decl
, _
, _
) => {
790 vis
.check_foreign_fn(ni
.id
, decl
);
792 hir
::ForeignItemStatic(ref ty
, _
) => {
793 vis
.check_foreign_static(ni
.id
, ty
.span
);
795 hir
::ForeignItemType
=> ()
803 pub struct VariantSizeDifferences
;
805 impl LintPass
for VariantSizeDifferences
{
806 fn get_lints(&self) -> LintArray
{
807 lint_array
!(VARIANT_SIZE_DIFFERENCES
)
811 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for VariantSizeDifferences
{
812 fn check_item(&mut self, cx
: &LateContext
, it
: &hir
::Item
) {
813 if let hir
::ItemEnum(ref enum_definition
, ref gens
) = it
.node
{
814 if gens
.params
.iter().all(|param
| param
.is_lifetime_param()) {
815 // sizes only make sense for non-generic types
816 let item_def_id
= cx
.tcx
.hir
.local_def_id(it
.id
);
817 let t
= cx
.tcx
.type_of(item_def_id
);
818 let ty
= cx
.tcx
.erase_regions(&t
);
819 let layout
= cx
.layout_of(ty
).unwrap_or_else(|e
| {
820 bug
!("failed to get layout for `{}`: {}", t
, e
)
823 if let layout
::Variants
::Tagged { ref variants, ref tag, .. }
= layout
.variants
{
824 let discr_size
= tag
.value
.size(cx
.tcx
).bytes();
826 debug
!("enum `{}` is {} bytes large with layout:\n{:#?}",
827 t
, layout
.size
.bytes(), layout
);
829 let (largest
, slargest
, largest_index
) = enum_definition
.variants
832 .map(|(variant
, variant_layout
)| {
833 // Subtract the size of the enum discriminant
834 let bytes
= variant_layout
.size
.bytes()
835 .saturating_sub(discr_size
);
837 debug
!("- variant `{}` is {} bytes large", variant
.node
.name
, bytes
);
841 .fold((0, 0, 0), |(l
, s
, li
), (idx
, size
)| if size
> l
{
849 // we only warn if the largest variant is at least thrice as large as
850 // the second-largest.
851 if largest
> slargest
* 3 && slargest
> 0 {
852 cx
.span_lint(VARIANT_SIZE_DIFFERENCES
,
853 enum_definition
.variants
[largest_index
].span
,
854 &format
!("enum variant is more than three times larger \
855 ({} bytes) than the next largest",