]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/types.rs
Unignore u128 test for stage 0,1
[rust.git] / src / librustc_lint / types.rs
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.
4 //
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.
10
11 #![allow(non_snake_case)]
12
13 use rustc::hir::def_id::DefId;
14 use rustc::ty::subst::Substs;
15 use rustc::ty::{self, AdtKind, Ty, TyCtxt};
16 use rustc::ty::layout::{Layout, Primitive};
17 use rustc::traits::Reveal;
18 use middle::const_val::ConstVal;
19 use rustc_const_eval::ConstContext;
20 use rustc_const_eval::EvalHint::ExprTypeChecked;
21 use util::nodemap::FxHashSet;
22 use lint::{LateContext, LintContext, LintArray};
23 use lint::{LintPass, LateLintPass};
24
25 use std::cmp;
26 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
27
28 use syntax::ast;
29 use syntax::abi::Abi;
30 use syntax::attr;
31 use syntax_pos::Span;
32 use syntax::codemap;
33
34 use rustc::hir;
35
36 declare_lint! {
37     UNUSED_COMPARISONS,
38     Warn,
39     "comparisons made useless by limits of the types involved"
40 }
41
42 declare_lint! {
43     OVERFLOWING_LITERALS,
44     Warn,
45     "literal out of range for its type"
46 }
47
48 declare_lint! {
49     EXCEEDING_BITSHIFTS,
50     Deny,
51     "shift exceeds the type's number of bits"
52 }
53
54 declare_lint! {
55     VARIANT_SIZE_DIFFERENCES,
56     Allow,
57     "detects enums with widely varying variant sizes"
58 }
59
60 #[derive(Copy, Clone)]
61 pub struct TypeLimits {
62     /// Id of the last visited negated expression
63     negated_expr_id: ast::NodeId,
64 }
65
66 impl TypeLimits {
67     pub fn new() -> TypeLimits {
68         TypeLimits { negated_expr_id: ast::DUMMY_NODE_ID }
69     }
70 }
71
72 impl LintPass for TypeLimits {
73     fn get_lints(&self) -> LintArray {
74         lint_array!(UNUSED_COMPARISONS,
75                     OVERFLOWING_LITERALS,
76                     EXCEEDING_BITSHIFTS)
77     }
78 }
79
80 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
81     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
82         match e.node {
83             hir::ExprUnary(hir::UnNeg, ref expr) => {
84                 // propagate negation, if the negation itself isn't negated
85                 if self.negated_expr_id != e.id {
86                     self.negated_expr_id = expr.id;
87                 }
88             }
89             hir::ExprBinary(binop, ref l, ref r) => {
90                 if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
91                     cx.span_lint(UNUSED_COMPARISONS,
92                                  e.span,
93                                  "comparison is useless due to type limits");
94                 }
95
96                 if binop.node.is_shift() {
97                     let opt_ty_bits = match cx.tables.node_id_to_type(l.id).sty {
98                         ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
99                         ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
100                         _ => None,
101                     };
102
103                     if let Some(bits) = opt_ty_bits {
104                         let exceeding = if let hir::ExprLit(ref lit) = r.node {
105                             if let ast::LitKind::Int(shift, _) = lit.node {
106                                 shift as u64 >= bits
107                             } else {
108                                 false
109                             }
110                         } else {
111                             let const_cx = ConstContext::with_tables(cx.tcx, cx.tables);
112                             match const_cx.eval(&r, ExprTypeChecked) {
113                                 Ok(ConstVal::Integral(i)) => {
114                                     i.is_negative() ||
115                                     i.to_u64()
116                                         .map(|i| i >= bits)
117                                         .unwrap_or(true)
118                                 }
119                                 _ => false,
120                             }
121                         };
122                         if exceeding {
123                             cx.span_lint(EXCEEDING_BITSHIFTS,
124                                          e.span,
125                                          "bitshift exceeds the type's number of bits");
126                         }
127                     };
128                 }
129             }
130             hir::ExprLit(ref lit) => {
131                 match cx.tables.node_id_to_type(e.id).sty {
132                     ty::TyInt(t) => {
133                         match lit.node {
134                             ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
135                             ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => {
136                                 let int_type = if let ast::IntTy::Is = t {
137                                     cx.sess().target.int_type
138                                 } else {
139                                     t
140                                 };
141                                 let (_, max) = int_ty_range(int_type);
142                                 let max = max as u128;
143                                 let negative = self.negated_expr_id == e.id;
144
145                                 // Detect literal value out of range [min, max] inclusive
146                                 // avoiding use of -min to prevent overflow/panic
147                                 if (negative && v > max + 1) ||
148                                    (!negative && v > max) {
149                                     cx.span_lint(OVERFLOWING_LITERALS,
150                                                  e.span,
151                                                  &format!("literal out of range for {:?}", t));
152                                     return;
153                                 }
154                             }
155                             _ => bug!(),
156                         };
157                     }
158                     ty::TyUint(t) => {
159                         let uint_type = if let ast::UintTy::Us = t {
160                             cx.sess().target.uint_type
161                         } else {
162                             t
163                         };
164                         let (min, max) = uint_ty_range(uint_type);
165                         let lit_val: u128 = match lit.node {
166                             // _v is u8, within range by definition
167                             ast::LitKind::Byte(_v) => return,
168                             ast::LitKind::Int(v, _) => v,
169                             _ => bug!(),
170                         };
171                         if lit_val < min || lit_val > max {
172                             cx.span_lint(OVERFLOWING_LITERALS,
173                                          e.span,
174                                          &format!("literal out of range for {:?}", t));
175                         }
176                     }
177                     ty::TyFloat(t) => {
178                         let (min, max) = float_ty_range(t);
179                         let lit_val: f64 = match lit.node {
180                             ast::LitKind::Float(v, _) |
181                             ast::LitKind::FloatUnsuffixed(v) => {
182                                 match v.as_str().parse() {
183                                     Ok(f) => f,
184                                     Err(_) => return,
185                                 }
186                             }
187                             _ => bug!(),
188                         };
189                         if lit_val < min || lit_val > max {
190                             cx.span_lint(OVERFLOWING_LITERALS,
191                                          e.span,
192                                          &format!("literal out of range for {:?}", t));
193                         }
194                     }
195                     _ => (),
196                 };
197             }
198             _ => (),
199         };
200
201         fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
202             match binop.node {
203                 hir::BiLt => v > min && v <= max,
204                 hir::BiLe => v >= min && v < max,
205                 hir::BiGt => v >= min && v < max,
206                 hir::BiGe => v > min && v <= max,
207                 hir::BiEq | hir::BiNe => v >= min && v <= max,
208                 _ => bug!(),
209             }
210         }
211
212         fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
213             codemap::respan(binop.span,
214                             match binop.node {
215                                 hir::BiLt => hir::BiGt,
216                                 hir::BiLe => hir::BiGe,
217                                 hir::BiGt => hir::BiLt,
218                                 hir::BiGe => hir::BiLe,
219                                 _ => return binop,
220                             })
221         }
222
223         // for isize & usize, be conservative with the warnings, so that the
224         // warnings are consistent between 32- and 64-bit platforms
225         fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) {
226             match int_ty {
227                 ast::IntTy::Is => (i64::min_value() as i128, i64::max_value() as i128),
228                 ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128),
229                 ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128),
230                 ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128),
231                 ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128),
232                 ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()),
233             }
234         }
235
236         fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) {
237             match uint_ty {
238                 ast::UintTy::Us => (u64::min_value() as u128, u64::max_value() as u128),
239                 ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128),
240                 ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128),
241                 ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128),
242                 ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128),
243                 ast::UintTy::U128 => (u128::min_value(), u128::max_value()),
244             }
245         }
246
247         fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
248             match float_ty {
249                 ast::FloatTy::F32 => (f32::MIN as f64, f32::MAX as f64),
250                 ast::FloatTy::F64 => (f64::MIN, f64::MAX),
251             }
252         }
253
254         fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
255             match int_ty {
256                 ast::IntTy::Is => int_ty_bits(target_int_ty, target_int_ty),
257                 ast::IntTy::I8 => 8,
258                 ast::IntTy::I16 => 16 as u64,
259                 ast::IntTy::I32 => 32,
260                 ast::IntTy::I64 => 64,
261                 ast::IntTy::I128 => 128,
262             }
263         }
264
265         fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
266             match uint_ty {
267                 ast::UintTy::Us => uint_ty_bits(target_uint_ty, target_uint_ty),
268                 ast::UintTy::U8 => 8,
269                 ast::UintTy::U16 => 16,
270                 ast::UintTy::U32 => 32,
271                 ast::UintTy::U64 => 64,
272                 ast::UintTy::U128 => 128,
273             }
274         }
275
276         fn check_limits(cx: &LateContext,
277                         binop: hir::BinOp,
278                         l: &hir::Expr,
279                         r: &hir::Expr)
280                         -> bool {
281             let (lit, expr, swap) = match (&l.node, &r.node) {
282                 (&hir::ExprLit(_), _) => (l, r, true),
283                 (_, &hir::ExprLit(_)) => (r, l, false),
284                 _ => return true,
285             };
286             // Normalize the binop so that the literal is always on the RHS in
287             // the comparison
288             let norm_binop = if swap { rev_binop(binop) } else { binop };
289             match cx.tables.node_id_to_type(expr.id).sty {
290                 ty::TyInt(int_ty) => {
291                     let (min, max) = int_ty_range(int_ty);
292                     let lit_val: i128 = match lit.node {
293                         hir::ExprLit(ref li) => {
294                             match li.node {
295                                 ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
296                                 ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i128,
297                                 _ => return true
298                             }
299                         },
300                         _ => bug!()
301                     };
302                     is_valid(norm_binop, lit_val, min, max)
303                 }
304                 ty::TyUint(uint_ty) => {
305                     let (min, max) :(u128, u128) = uint_ty_range(uint_ty);
306                     let lit_val: u128 = match lit.node {
307                         hir::ExprLit(ref li) => {
308                             match li.node {
309                                 ast::LitKind::Int(v, _) => v,
310                                 _ => return true
311                             }
312                         },
313                         _ => bug!()
314                     };
315                     is_valid(norm_binop, lit_val, min, max)
316                 }
317                 _ => true,
318             }
319         }
320
321         fn is_comparison(binop: hir::BinOp) -> bool {
322             match binop.node {
323                 hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => true,
324                 _ => false,
325             }
326         }
327     }
328 }
329
330 declare_lint! {
331     IMPROPER_CTYPES,
332     Warn,
333     "proper use of libc types in foreign modules"
334 }
335
336 struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
337     cx: &'a LateContext<'a, 'tcx>,
338 }
339
340 enum FfiResult {
341     FfiSafe,
342     FfiUnsafe(&'static str),
343     FfiBadStruct(DefId, &'static str),
344     FfiBadUnion(DefId, &'static str),
345     FfiBadEnum(DefId, &'static str),
346 }
347
348 /// Check if this enum can be safely exported based on the
349 /// "nullable pointer optimization". Currently restricted
350 /// to function pointers and references, but could be
351 /// expanded to cover NonZero raw pointers and newtypes.
352 /// FIXME: This duplicates code in trans.
353 fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
354                                   def: &'tcx ty::AdtDef,
355                                   substs: &Substs<'tcx>)
356                                   -> bool {
357     if def.variants.len() == 2 {
358         let data_idx;
359
360         if def.variants[0].fields.is_empty() {
361             data_idx = 1;
362         } else if def.variants[1].fields.is_empty() {
363             data_idx = 0;
364         } else {
365             return false;
366         }
367
368         if def.variants[data_idx].fields.len() == 1 {
369             match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
370                 ty::TyFnPtr(_) => {
371                     return true;
372                 }
373                 ty::TyRef(..) => {
374                     return true;
375                 }
376                 _ => {}
377             }
378         }
379     }
380     false
381 }
382
383 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
384     /// Check if the given type is "ffi-safe" (has a stable, well-defined
385     /// representation which can be exported to C code).
386     fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult {
387         use self::FfiResult::*;
388         let cx = self.cx.tcx;
389
390         // Protect against infinite recursion, for example
391         // `struct S(*mut S);`.
392         // FIXME: A recursion limit is necessary as well, for irregular
393         // recusive types.
394         if !cache.insert(ty) {
395             return FfiSafe;
396         }
397
398         match ty.sty {
399             ty::TyAdt(def, substs) => {
400                 match def.adt_kind() {
401                     AdtKind::Struct => {
402                         if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
403                             return FfiUnsafe("found struct without foreign-function-safe \
404                                               representation annotation in foreign module, \
405                                               consider adding a #[repr(C)] attribute to the type");
406                         }
407
408                         // We can't completely trust repr(C) markings; make sure the
409                         // fields are actually safe.
410                         if def.struct_variant().fields.is_empty() {
411                             return FfiUnsafe("found zero-size struct in foreign module, consider \
412                                               adding a member to this struct");
413                         }
414
415                         for field in &def.struct_variant().fields {
416                             let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
417                             let r = self.check_type_for_ffi(cache, field_ty);
418                             match r {
419                                 FfiSafe => {}
420                                 FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
421                                     return r;
422                                 }
423                                 FfiUnsafe(s) => {
424                                     return FfiBadStruct(def.did, s);
425                                 }
426                             }
427                         }
428                         FfiSafe
429                     }
430                     AdtKind::Union => {
431                         if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
432                             return FfiUnsafe("found union without foreign-function-safe \
433                                               representation annotation in foreign module, \
434                                               consider adding a #[repr(C)] attribute to the type");
435                         }
436
437                         for field in &def.struct_variant().fields {
438                             let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
439                             let r = self.check_type_for_ffi(cache, field_ty);
440                             match r {
441                                 FfiSafe => {}
442                                 FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
443                                     return r;
444                                 }
445                                 FfiUnsafe(s) => {
446                                     return FfiBadUnion(def.did, s);
447                                 }
448                             }
449                         }
450                         FfiSafe
451                     }
452                     AdtKind::Enum => {
453                         if def.variants.is_empty() {
454                             // Empty enums are okay... although sort of useless.
455                             return FfiSafe;
456                         }
457
458                         // Check for a repr() attribute to specify the size of the
459                         // discriminant.
460                         let repr_hints = cx.lookup_repr_hints(def.did);
461                         match &repr_hints[..] {
462                             &[] => {
463                                 // Special-case types like `Option<extern fn()>`.
464                                 if !is_repr_nullable_ptr(cx, def, substs) {
465                                     return FfiUnsafe("found enum without foreign-function-safe \
466                                                       representation annotation in foreign \
467                                                       module, consider adding a #[repr(...)] \
468                                                       attribute to the type");
469                                 }
470                             }
471                             &[ref hint] => {
472                                 if !hint.is_ffi_safe() {
473                                     // FIXME: This shouldn't be reachable: we should check
474                                     // this earlier.
475                                     return FfiUnsafe("enum has unexpected #[repr(...)] attribute");
476                                 }
477
478                                 // Enum with an explicitly sized discriminant; either
479                                 // a C-style enum or a discriminated union.
480
481                                 // The layout of enum variants is implicitly repr(C).
482                                 // FIXME: Is that correct?
483                             }
484                             _ => {
485                                 // FIXME: This shouldn't be reachable: we should check
486                                 // this earlier.
487                                 return FfiUnsafe("enum has too many #[repr(...)] attributes");
488                             }
489                         }
490
491                         // Check the contained variants.
492                         for variant in &def.variants {
493                             for field in &variant.fields {
494                                 let arg = cx.normalize_associated_type(&field.ty(cx, substs));
495                                 let r = self.check_type_for_ffi(cache, arg);
496                                 match r {
497                                     FfiSafe => {}
498                                     FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
499                                         return r;
500                                     }
501                                     FfiUnsafe(s) => {
502                                         return FfiBadEnum(def.did, s);
503                                     }
504                                 }
505                             }
506                         }
507                         FfiSafe
508                     }
509                 }
510             }
511
512             ty::TyChar => {
513                 FfiUnsafe("found Rust type `char` in foreign module, while \
514                            `u32` or `libc::wchar_t` should be used")
515             }
516
517             // Primitive types with a stable representation.
518             ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe,
519
520             ty::TySlice(_) => {
521                 FfiUnsafe("found Rust slice type in foreign module, \
522                            consider using a raw pointer instead")
523             }
524
525             ty::TyDynamic(..) => {
526                 FfiUnsafe("found Rust trait type in foreign module, \
527                            consider using a raw pointer instead")
528             }
529
530             ty::TyStr => {
531                 FfiUnsafe("found Rust type `str` in foreign module; \
532                            consider using a `*const libc::c_char`")
533             }
534
535             ty::TyTuple(..) => {
536                 FfiUnsafe("found Rust tuple type in foreign module; \
537                            consider using a struct instead")
538             }
539
540             ty::TyRawPtr(ref m) |
541             ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty),
542
543             ty::TyArray(ty, _) => self.check_type_for_ffi(cache, ty),
544
545             ty::TyFnPtr(bare_fn) => {
546                 match bare_fn.abi {
547                     Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
548                         return FfiUnsafe("found function pointer with Rust calling convention in \
549                                           foreign module; consider using an `extern` function \
550                                           pointer")
551                     }
552                     _ => {}
553                 }
554
555                 let sig = cx.erase_late_bound_regions(&bare_fn.sig);
556                 if !sig.output().is_nil() {
557                     let r = self.check_type_for_ffi(cache, sig.output());
558                     match r {
559                         FfiSafe => {}
560                         _ => {
561                             return r;
562                         }
563                     }
564                 }
565                 for arg in sig.inputs() {
566                     let r = self.check_type_for_ffi(cache, arg);
567                     match r {
568                         FfiSafe => {}
569                         _ => {
570                             return r;
571                         }
572                     }
573                 }
574                 FfiSafe
575             }
576
577             ty::TyParam(..) |
578             ty::TyInfer(..) |
579             ty::TyError |
580             ty::TyClosure(..) |
581             ty::TyProjection(..) |
582             ty::TyAnon(..) |
583             ty::TyFnDef(..) => bug!("Unexpected type in foreign function"),
584         }
585     }
586
587     fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) {
588         // it is only OK to use this function because extern fns cannot have
589         // any generic types right now:
590         let ty = self.cx.tcx.normalize_associated_type(&ty);
591
592         match self.check_type_for_ffi(&mut FxHashSet(), ty) {
593             FfiResult::FfiSafe => {}
594             FfiResult::FfiUnsafe(s) => {
595                 self.cx.span_lint(IMPROPER_CTYPES, sp, s);
596             }
597             FfiResult::FfiBadStruct(_, s) => {
598                 // FIXME: This diagnostic is difficult to read, and doesn't
599                 // point at the relevant field.
600                 self.cx.span_lint(IMPROPER_CTYPES,
601                                   sp,
602                                   &format!("found non-foreign-function-safe member in struct \
603                                             marked #[repr(C)]: {}",
604                                            s));
605             }
606             FfiResult::FfiBadUnion(_, s) => {
607                 // FIXME: This diagnostic is difficult to read, and doesn't
608                 // point at the relevant field.
609                 self.cx.span_lint(IMPROPER_CTYPES,
610                                   sp,
611                                   &format!("found non-foreign-function-safe member in union \
612                                             marked #[repr(C)]: {}",
613                                            s));
614             }
615             FfiResult::FfiBadEnum(_, s) => {
616                 // FIXME: This diagnostic is difficult to read, and doesn't
617                 // point at the relevant variant.
618                 self.cx.span_lint(IMPROPER_CTYPES,
619                                   sp,
620                                   &format!("found non-foreign-function-safe member in enum: {}",
621                                            s));
622             }
623         }
624     }
625
626     fn check_foreign_fn(&mut self, id: ast::NodeId, decl: &hir::FnDecl) {
627         let def_id = self.cx.tcx.hir.local_def_id(id);
628         let sig = self.cx.tcx.item_type(def_id).fn_sig();
629         let sig = self.cx.tcx.erase_late_bound_regions(&sig);
630
631         for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) {
632             self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty);
633         }
634
635         if let hir::Return(ref ret_hir) = decl.output {
636             let ret_ty = sig.output();
637             if !ret_ty.is_nil() {
638                 self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty);
639             }
640         }
641     }
642
643     fn check_foreign_static(&mut self, id: ast::NodeId, span: Span) {
644         let def_id = self.cx.tcx.hir.local_def_id(id);
645         let ty = self.cx.tcx.item_type(def_id);
646         self.check_type_for_ffi_and_report_errors(span, ty);
647     }
648 }
649
650 #[derive(Copy, Clone)]
651 pub struct ImproperCTypes;
652
653 impl LintPass for ImproperCTypes {
654     fn get_lints(&self) -> LintArray {
655         lint_array!(IMPROPER_CTYPES)
656     }
657 }
658
659 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes {
660     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
661         let mut vis = ImproperCTypesVisitor { cx: cx };
662         if let hir::ItemForeignMod(ref nmod) = it.node {
663             if nmod.abi != Abi::RustIntrinsic && nmod.abi != Abi::PlatformIntrinsic {
664                 for ni in &nmod.items {
665                     match ni.node {
666                         hir::ForeignItemFn(ref decl, _, _) => {
667                             vis.check_foreign_fn(ni.id, decl);
668                         }
669                         hir::ForeignItemStatic(ref ty, _) => {
670                             vis.check_foreign_static(ni.id, ty.span);
671                         }
672                     }
673                 }
674             }
675         }
676     }
677 }
678
679 pub struct VariantSizeDifferences;
680
681 impl LintPass for VariantSizeDifferences {
682     fn get_lints(&self) -> LintArray {
683         lint_array!(VARIANT_SIZE_DIFFERENCES)
684     }
685 }
686
687 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
688     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
689         if let hir::ItemEnum(ref enum_definition, ref gens) = it.node {
690             if gens.ty_params.is_empty() {
691                 // sizes only make sense for non-generic types
692                 let t = cx.tcx.item_type(cx.tcx.hir.local_def_id(it.id));
693                 let layout = cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
694                     let ty = cx.tcx.erase_regions(&t);
695                     ty.layout(&infcx).unwrap_or_else(|e| {
696                         bug!("failed to get layout for `{}`: {}", t, e)
697                     })
698                 });
699
700                 if let Layout::General { ref variants, ref size, discr, .. } = *layout {
701                     let discr_size = Primitive::Int(discr).size(&cx.tcx.data_layout).bytes();
702
703                     debug!("enum `{}` is {} bytes large with layout:\n{:#?}",
704                       t, size.bytes(), layout);
705
706                     let (largest, slargest, largest_index) = enum_definition.variants
707                         .iter()
708                         .zip(variants)
709                         .map(|(variant, variant_layout)| {
710                             // Subtract the size of the enum discriminant
711                             let bytes = variant_layout.min_size
712                                 .bytes()
713                                 .saturating_sub(discr_size);
714
715                             debug!("- variant `{}` is {} bytes large", variant.node.name, bytes);
716                             bytes
717                         })
718                         .enumerate()
719                         .fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l {
720                             (size, l, idx)
721                         } else if size > s {
722                             (l, size, li)
723                         } else {
724                             (l, s, li)
725                         });
726
727                     // we only warn if the largest variant is at least thrice as large as
728                     // the second-largest.
729                     if largest > slargest * 3 && slargest > 0 {
730                         cx.span_lint(VARIANT_SIZE_DIFFERENCES,
731                                      enum_definition.variants[largest_index].span,
732                                      &format!("enum variant is more than three times larger \
733                                                ({} bytes) than the next largest",
734                                               largest));
735                     }
736                 }
737             }
738         }
739     }
740 }